vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / orchestrator / topology / instance_handler.py
1 # Licensed to the Apache Software Foundation (ASF) under one or more
2 # contributor license agreements.  See the NOTICE file distributed with
3 # this work for additional information regarding copyright ownership.
4 # The ASF licenses this file to You under the Apache License, Version 2.0
5 # (the "License"); you may not use this file except in compliance with
6 # the License.  You may obtain a copy of the License at
7 #
8 #     http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS,
12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 from ... parser.modeling import context
17 from ... modeling import models, functions
18 from ... utils import formatting
19 from .. import execution_plugin
20 from .. import decorators
21 from . import common
22
23
24 class Artifact(common.InstanceHandlerBase):
25
26     def coerce(self, **kwargs):
27         self._topology.coerce(self._model.properties, **kwargs)
28
29     def validate(self, **kwargs):
30         self._topology.validate(self._model.properties, **kwargs)
31
32     def dump(self, out_stream):
33         with out_stream.indent():
34             out_stream.write(out_stream.node_style(self._model.name))
35             out_stream.write(out_stream.meta_style(self._model.description))
36             with out_stream.indent():
37                 out_stream.write('Artifact type: {0}'.format(out_stream.type_style(
38                     self._model.type.name)))
39                 out_stream.write('Source path: {0}'.format(
40                     out_stream.literal_style(self._model.source_path)))
41                 if self._model.target_path is not None:
42                     out_stream.write('Target path: {0}'.format(
43                         out_stream.literal_style(self._model.target_path)))
44                 if self._model.repository_url is not None:
45                     out_stream.write('Repository URL: {0}'.format(
46                         out_stream.literal_style(self._model.repository_url)))
47                 if self._model.repository_credential:
48                     out_stream.write('Repository credential: {0}'.format(
49                         out_stream.literal_style(self._model.repository_credential)))
50                 self._topology.dump(self._model.properties, out_stream, title='Properties')
51
52
53 class Capability(common.InstanceHandlerBase):
54     def coerce(self, **kwargs):
55         self._topology.coerce(self._model.properties, **kwargs)
56
57     def validate(self, **kwargs):
58         self._topology.validate(self._model.properties, **kwargs)
59
60     def dump(self, out_stream):
61         out_stream.write(out_stream.node_style(self._model.name))
62         with out_stream.indent():
63             out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
64             out_stream.write('Occurrences: {0:d} ({1:d}{2})'.format(
65                 self._model.occurrences,
66                 self._model.min_occurrences or 0,
67                 ' to {0:d}'.format(self._model.max_occurrences)
68                 if self._model.max_occurrences is not None
69                 else ' or more'))
70             self._topology.dump(self._model.properties, out_stream, title='Properties')
71
72
73 class Group(common.ActorHandlerBase):
74
75     def coerce(self, **kwargs):
76         self._coerce(self._model.properties, self._model.interfaces, **kwargs)
77
78     def validate(self, **kwargs):
79         self._validate(self._model.properties,
80                        self._model.interfaces,
81                        **kwargs)
82
83     def dump(self, out_stream):
84         out_stream.write('Group: {0}'.format(out_stream.node_style(self._model.name)))
85         with out_stream.indent():
86             out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
87             self._topology.dump(self._model.properties, out_stream, title='Properties')
88             self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
89             if self._model.nodes:
90                 out_stream.write('Member nodes:')
91                 with out_stream.indent():
92                     for node in self._model.nodes:
93                         out_stream.write(out_stream.node_style(node.name))
94
95     def configure_operations(self):
96         for interface in self._model.interfaces.values():
97             self._topology.configure_operations(interface)
98
99
100 class Interface(common.ActorHandlerBase):
101     def coerce(self, **kwargs):
102         self._coerce(self._model.inputs, self._model.operations, **kwargs)
103
104     def validate(self, **kwargs):
105         self._validate(self._model.inputs,
106                        self._model.operations,
107                        **kwargs)
108
109     def dump(self, out_stream):
110         out_stream.write(out_stream.node_style(self._model.name))
111         if self._model.description:
112             out_stream.write(out_stream.meta_style(self._model.description))
113         with out_stream.indent():
114             out_stream.write('Interface type: {0}'.format(
115                 out_stream.type_style(self._model.type.name)))
116             self._topology.dump(self._model.inputs, out_stream, title='Inputs')
117             self._topology.dump(self._model.operations, out_stream, title='Operations')
118
119     def configure_operations(self):
120         for operation in self._model.operations.values():
121             self._topology.configure_operations(operation)
122
123
124 class Node(common.ActorHandlerBase):
125     def coerce(self, **kwargs):
126         self._coerce(self._model.properties,
127                      self._model.attributes,
128                      self._model.interfaces,
129                      self._model.artifacts,
130                      self._model.capabilities,
131                      self._model.outbound_relationships,
132                      **kwargs)
133
134     def validate(self, **kwargs):
135         if len(self._model.name) > context.ID_MAX_LENGTH:
136             self._topology.report(
137                 '"{0}" has an ID longer than the limit of {1:d} characters: {2:d}'.format(
138                     self._model.name, context.ID_MAX_LENGTH, len(self._model.name)),
139                 level=self._topology.Issue.BETWEEN_INSTANCES)
140
141         self._validate(self._model.properties,
142                        self._model.attributes,
143                        self._model.interfaces,
144                        self._model.artifacts,
145                        self._model.capabilities,
146                        self._model.outbound_relationships)
147
148     def dump(self, out_stream):
149         out_stream.write('Node: {0}'.format(out_stream.node_style(self._model.name)))
150         with out_stream.indent():
151             out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
152             out_stream.write('Template: {0}'.format(
153                 out_stream.node_style(self._model.node_template.name)))
154             self._topology.dump(self._model.properties, out_stream, title='Properties')
155             self._topology.dump(self._model.attributes, out_stream, title='Attributes')
156             self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
157             self._topology.dump(self._model.artifacts, out_stream, title='Artifacts')
158             self._topology.dump(self._model.capabilities, out_stream, title='Capabilities')
159             self._topology.dump(self._model.outbound_relationships, out_stream,
160                                 title='Relationships')
161
162     def configure_operations(self):
163         for interface in self._model.interfaces.values():
164             self._topology.configure_operations(interface)
165         for relationship in self._model.outbound_relationships:
166             self._topology.configure_operations(relationship)
167
168     def validate_capabilities(self):
169         satisfied = False
170         for capability in self._model.capabilities.itervalues():
171             if not capability.has_enough_relationships:
172                 self._topology.report(
173                     'capability "{0}" of node "{1}" requires at least {2:d} '
174                     'relationships but has {3:d}'.format(capability.name,
175                                                          self._model.name,
176                                                          capability.min_occurrences,
177                                                          capability.occurrences),
178                     level=self._topology.Issue.BETWEEN_INSTANCES)
179                 satisfied = False
180         return satisfied
181
182     def satisfy_requirements(self):
183         satisfied = True
184         for requirement_template in self._model.node_template.requirement_templates:
185
186             # Since we try and satisfy requirements, which are node template bound, and use that
187             # information in the creation of the relationship, Some requirements may have been
188             # satisfied by a previous run on that node template.
189             # The entire mechanism of satisfying requirements needs to be refactored.
190             if any(rel.requirement_template == requirement_template
191                    for rel in self._model.outbound_relationships):
192                 continue
193
194             # Find target template
195             target_node_template, target_node_capability = self._find_target(requirement_template)
196             if target_node_template is not None:
197                 satisfied = self._satisfy_capability(
198                     target_node_capability, target_node_template, requirement_template)
199             else:
200                 self._topology.report('requirement "{0}" of node "{1}" has no target node template'.
201                                       format(requirement_template.name, self._model.name),
202                                       level=self._topology.Issue.BETWEEN_INSTANCES)
203                 satisfied = False
204         return satisfied
205
206     def _satisfy_capability(self, target_node_capability, target_node_template,
207                             requirement_template):
208         # Find target nodes
209         target_nodes = target_node_template.nodes
210         if target_nodes:
211             target_node = None
212             target_capability = None
213
214             if target_node_capability is not None:
215                 # Relate to the first target node that has capacity
216                 for node in target_nodes:
217                     a_target_capability = node.capabilities.get(target_node_capability.name)
218                     if a_target_capability.relate():
219                         target_node = node
220                         target_capability = a_target_capability
221                         break
222             else:
223                 # Use first target node
224                 target_node = target_nodes[0]
225
226             if target_node is not None:
227                 if requirement_template.relationship_template is not None:
228                     relationship_model = self._topology.instantiate(
229                         requirement_template.relationship_template)
230                 else:
231                     relationship_model = models.Relationship()
232                 relationship_model.name = requirement_template.name
233                 relationship_model.requirement_template = requirement_template
234                 relationship_model.target_node = target_node
235                 relationship_model.target_capability = target_capability
236                 self._model.outbound_relationships.append(relationship_model)
237                 return True
238             else:
239                 self._topology.report(
240                     'requirement "{0}" of node "{1}" targets node '
241                     'template "{2}" but its instantiated nodes do not '
242                     'have enough capacity'.format(
243                         requirement_template.name, self._model.name, target_node_template.name),
244                     level=self._topology.Issue.BETWEEN_INSTANCES)
245                 return False
246         else:
247             self._topology.report(
248                 'requirement "{0}" of node "{1}" targets node template '
249                 '"{2}" but it has no instantiated nodes'.format(
250                     requirement_template.name, self._model.name, target_node_template.name),
251                 level=self._topology.Issue.BETWEEN_INSTANCES)
252             return False
253
254     def _find_target(self, requirement_template):
255         # We might already have a specific node template from the requirement template, so
256         # we'll just verify it
257         if requirement_template.target_node_template is not None:
258             if not self._model.node_template.is_target_node_template_valid(
259                     requirement_template.target_node_template):
260                 self._topology.report(
261                     'requirement "{0}" of node template "{1}" is for node '
262                     'template "{2}" but it does not match constraints'.format(
263                         requirement_template.name,
264                         requirement_template.target_node_template.name,
265                         self._model.node_template.name),
266                     level=self._topology.Issue.BETWEEN_TYPES)
267             if (requirement_template.target_capability_type is not None or
268                     requirement_template.target_capability_name is not None):
269                 target_node_capability = self._get_capability(requirement_template)
270                 if target_node_capability is None:
271                     return None, None
272             else:
273                 target_node_capability = None
274
275             return requirement_template.target_node_template, target_node_capability
276
277         # Find first node that matches the type
278         elif requirement_template.target_node_type is not None:
279             for target_node_template in \
280                     self._model.node_template.service_template.node_templates.itervalues():
281                 if requirement_template.target_node_type.get_descendant(
282                         target_node_template.type.name) is None:
283                     continue
284
285                 if not self._model.node_template.is_target_node_template_valid(
286                         target_node_template):
287                     continue
288
289                 target_node_capability = self._get_capability(requirement_template,
290                                                               target_node_template)
291
292                 if target_node_capability is None:
293                     continue
294
295                 return target_node_template, target_node_capability
296
297         # Find the first node which has a capability of the required type
298         elif requirement_template.target_capability_type is not None:
299             for target_node_template in \
300                     self._model.node_template.service_template.node_templates.itervalues():
301                 target_node_capability = \
302                     self._get_capability(requirement_template, target_node_template)
303                 if target_node_capability:
304                     return target_node_template, target_node_capability
305
306         return None, None
307
308     def _get_capability(self, requirement_template, target_node_template=None):
309         target_node_template = target_node_template or requirement_template.target_node_template
310
311         for capability_template in target_node_template.capability_templates.values():
312             if self._satisfies_requirement(
313                     capability_template, requirement_template, target_node_template):
314                 return capability_template
315
316         return None
317
318     def _satisfies_requirement(
319             self, capability_template, requirement_template, target_node_template):
320         # Do we match the required capability type?
321         if (requirement_template.target_capability_type and
322                 requirement_template.target_capability_type.get_descendant(
323                     capability_template.type.name) is None):
324             return False
325
326         # Are we in valid_source_node_types?
327         if capability_template.valid_source_node_types:
328             for valid_source_node_type in capability_template.valid_source_node_types:
329                 if valid_source_node_type.get_descendant(
330                         self._model.node_template.type.name) is None:
331                     return False
332
333         # Apply requirement constraints
334         if requirement_template.target_node_template_constraints:
335             for node_template_constraint in requirement_template.target_node_template_constraints:
336                 if not node_template_constraint.matches(
337                         self._model.node_template, target_node_template):
338                     return False
339
340         return True
341
342
343 class Operation(common.ActorHandlerBase):
344     def coerce(self, **kwargs):
345         self._coerce(self._model.inputs,
346                      self._model.configurations,
347                      self._model.arguments,
348                      **kwargs)
349
350     def validate(self, **kwargs):
351         self._validate(self._model.inputs,
352                        self._model.configurations,
353                        self._model.arguments,
354                        **kwargs)
355
356     def dump(self, out_stream):
357         out_stream.write(out_stream.node_style(self._model.name))
358         if self._model.description:
359             out_stream.write(out_stream.meta_style(self._model.description))
360         with out_stream.indent():
361             if self._model.implementation is not None:
362                 out_stream.write('Implementation: {0}'.format(
363                     out_stream.literal_style(self._model.implementation)))
364             if self._model.dependencies:
365                 out_stream.write(
366                     'Dependencies: {0}'.format(', '.join((str(out_stream.literal_style(v))
367                                                           for v in self._model.dependencies))))
368             self._topology.dump(self._model.inputs, out_stream, title='Inputs')
369             if self._model.executor is not None:
370                 out_stream.write('Executor: {0}'.format(out_stream.literal_style(
371                     self._model.executor)))
372             if self._model.max_attempts is not None:
373                 out_stream.write('Max attempts: {0}'.format(out_stream.literal_style(
374                     self._model.max_attempts)))
375             if self._model.retry_interval is not None:
376                 out_stream.write('Retry interval: {0}'.format(
377                     out_stream.literal_style(self._model.retry_interval)))
378             if self._model.plugin is not None:
379                 out_stream.write('Plugin: {0}'.format(
380                     out_stream.literal_style(self._model.plugin.name)))
381             self._topology.dump(self._model.configurations, out_stream, title='Configuration')
382             if self._model.function is not None:
383                 out_stream.write('Function: {0}'.format(out_stream.literal_style(
384                     self._model.function)))
385             self._topology.dump(self._model.arguments, out_stream, title='Arguments')
386
387     def configure_operations(self):
388         if self._model.implementation is None and self._model.function is None:
389             return
390
391         if (self._model.interface is not None and
392                 self._model.plugin is None and
393                 self._model.function is None):
394             # ("interface" is None for workflow operations, which do not currently use "plugin")
395             # The default (None) plugin is the execution plugin
396             execution_plugin.instantiation.configure_operation(self._model, self._topology)
397         else:
398             # In the future plugins may be able to add their own "configure_operation" hook that
399             # can validate the configuration and otherwise create specially derived arguments. For
400             # now, we just send all configuration parameters as arguments without validation.
401             for key, conf in self._model.configurations.items():
402                 self._model.arguments[key] = self._topology.instantiate(conf.as_argument())
403
404         if self._model.interface is not None:
405             # Send all interface inputs as extra arguments
406             # ("interface" is None for workflow operations)
407             # Note that they will override existing arguments of the same names
408             for key, input in self._model.interface.inputs.items():
409                 self._model.arguments[key] = self._topology.instantiate(input.as_argument())
410
411         # Send all inputs as extra arguments
412         # Note that they will override existing arguments of the same names
413         for key, input in self._model.inputs.items():
414             self._model.arguments[key] = self._topology.instantiate(input.as_argument())
415
416         # Check for reserved arguments
417         used_reserved_names = set(decorators.OPERATION_DECORATOR_RESERVED_ARGUMENTS).intersection(
418             self._model.arguments.keys())
419         if used_reserved_names:
420             self._topology.report(
421                 'using reserved arguments in operation "{0}": {1}'.format(
422                     self._model.name, formatting.string_list_as_string(used_reserved_names)),
423                 level=self._topology.Issue.EXTERNAL)
424
425
426 class Policy(common.InstanceHandlerBase):
427     def coerce(self, **kwargs):
428         self._topology.coerce(self._model.properties, **kwargs)
429
430     def validate(self, **kwargs):
431         self._topology.validate(self._model.properties, **kwargs)
432
433     def dump(self, out_stream):
434         out_stream.write('Policy: {0}'.format(out_stream.node_style(self._model.name)))
435         with out_stream.indent():
436             out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
437             self._topology.dump(self._model.properties, out_stream, title='Properties')
438             if self._model.nodes:
439                 out_stream.write('Target nodes:')
440                 with out_stream.indent():
441                     for node in self._model.nodes:
442                         out_stream.write(out_stream.node_style(node.name))
443             if self._model.groups:
444                 out_stream.write('Target groups:')
445                 with out_stream.indent():
446                     for group in self._model.groups:
447                         out_stream.write(out_stream.node_style(group.name))
448
449
450 class Relationship(common.ActorHandlerBase):
451     def coerce(self, **kwargs):
452         self._coerce(self._model.properties,
453                      self._model.interfaces,
454                      **kwargs)
455
456     def validate(self, **kwargs):
457         self._validate(self._model.properties,
458                        self._model.interfaces,
459                        **kwargs)
460
461     def dump(self, out_stream):
462         if self._model.name:
463             out_stream.write('{0} ->'.format(out_stream.node_style(self._model.name)))
464         else:
465             out_stream.write('->')
466         with out_stream.indent():
467             out_stream.write('Node: {0}'.format(out_stream.node_style(
468                 self._model.target_node.name)))
469             if self._model.target_capability:
470                 out_stream.write('Capability: {0}'.format(out_stream.node_style(
471                     self._model.target_capability.name)))
472             if self._model.type is not None:
473                 out_stream.write('Relationship type: {0}'.format(
474                     out_stream.type_style(self._model.type.name)))
475             if (self._model.relationship_template is not None and
476                     self._model.relationship_template.name):
477                 out_stream.write('Relationship template: {0}'.format(
478                     out_stream.node_style(self._model.relationship_template.name)))
479             self._topology.dump(self._model.properties, out_stream, title='Properties')
480             self._topology.dump(self._model.interfaces, out_stream, title='Interfaces')
481
482     def configure_operations(self):
483         for interface in self._model.interfaces.values():
484             self._topology.configure_operations(interface)
485
486
487 class Service(common.ActorHandlerBase):
488     def coerce(self, **kwargs):
489         self._coerce(self._model.meta_data,
490                      self._model.nodes,
491                      self._model.groups,
492                      self._model.policies,
493                      self._model.substitution,
494                      self._model.inputs,
495                      self._model.outputs,
496                      self._model.workflows,
497                      **kwargs)
498
499     def validate(self, **kwargs):
500         self._validate(self._model.meta_data,
501                        self._model.nodes,
502                        self._model.groups,
503                        self._model.policies,
504                        self._model.substitution,
505                        self._model.inputs,
506                        self._model.outputs,
507                        self._model.workflows,
508                        **kwargs)
509
510     def dump(self, out_stream):
511         if self._model.description is not None:
512             out_stream.write(out_stream.meta_style(self._model.description))
513         self._topology.dump(self._model.meta_data, out_stream, title='Metadata')
514         self._topology.dump(self._model.nodes, out_stream)
515         self._topology.dump(self._model.groups, out_stream)
516         self._topology.dump(self._model.policies, out_stream)
517         self._topology.dump(self._model.substitution, out_stream)
518         self._topology.dump(self._model.inputs, out_stream, title='Inputs')
519         self._topology.dump(self._model.outputs, out_stream, title='Outputs')
520         self._topology.dump(self._model.workflows, out_stream, title='Workflows')
521
522     def configure_operations(self):
523         for node in self._model.nodes.itervalues():
524             self._topology.configure_operations(node)
525         for group in self._model.groups.itervalues():
526             self._topology.configure_operations(group)
527         for operation in self._model.workflows.itervalues():
528             self._topology.configure_operations(operation)
529
530     def validate_capabilities(self):
531         satisfied = True
532         for node in self._model.nodes.values():
533             if not self._topology.validate_capabilities(node):
534                 satisfied = False
535         return satisfied
536
537     def satisfy_requirements(self):
538         return all(self._topology.satisfy_requirements(node)
539                    for node in self._model.nodes.values())
540
541
542 class Substitution(common.InstanceHandlerBase):
543     def coerce(self, **kwargs):
544         self._topology.coerce(self._model.mappings, **kwargs)
545
546     def validate(self, **kwargs):
547         self._topology.validate(self._model.mappings, **kwargs)
548
549     def dump(self, out_stream):
550         out_stream.write('Substitution:')
551         with out_stream.indent():
552             out_stream.write('Node type: {0}'.format(out_stream.type_style(
553                 self._model.node_type.name)))
554             self._topology.dump(self._model.mappings, out_stream, title='Mappings')
555
556
557 class SubstitutionMapping(common.InstanceHandlerBase):
558
559     def coerce(self, **kwargs):
560         pass
561
562     def validate(self, **_):
563         if (self._model.capability is None) and (self._model.requirement_template is None):
564             self._topology.report(
565                 'mapping "{0}" refers to neither capability nor a requirement'
566                 ' in node: {1}'.format(
567                     self._model.name, formatting.safe_repr(self._model.node_style.name)),
568                 level=self._topology.Issue.BETWEEN_TYPES)
569
570     def dump(self, out_stream):
571         if self._model.capability is not None:
572             out_stream.write('{0} -> {1}.{2}'.format(
573                 out_stream.node_style(self._model.name),
574                 out_stream.node_style(self._model.capability.node.name),
575                 out_stream.node_style(self._model.capability.name)))
576         else:
577             out_stream.write('{0} -> {1}.{2}'.format(
578                 out_stream.node_style(self._model.name),
579                 out_stream.node_style(self._model.node.name),
580                 out_stream.node_style(self._model.requirement_template.name)))
581
582
583 class Metadata(common.InstanceHandlerBase):
584
585     def dump(self, out_stream):
586         out_stream.write('{0}: {1}'.format(
587             out_stream.property_style(self._model.name),
588             out_stream.literal_style(self._model.value)))
589
590     def coerce(self, **_):
591         pass
592
593     def instantiate(self, instance_cls):
594         return instance_cls(name=self._model.name, value=self._model.value)
595
596     def validate(self):
597         pass
598
599
600 class _Parameter(common.InstanceHandlerBase):
601
602     def dump(self, out_stream):
603         if self._model.type_name is not None:
604             out_stream.write('{0}: {1} ({2})'.format(
605                 out_stream.property_style(self._model.name),
606                 out_stream.literal_style(formatting.as_raw(self._model.value)),
607                 out_stream.type_style(self._model.type_name)))
608         else:
609             out_stream.write('{0}: {1}'.format(
610                 out_stream.property_style(self._model.name),
611                 out_stream.literal_style(formatting.as_raw(self._model.value))))
612         if self._model.description:
613             out_stream.write(out_stream.meta_style(self._model.description))
614
615     def instantiate(self, instance_cls, **kwargs):
616         return instance_cls(
617             name=self._model.name,                                                                  # pylint: disable=unexpected-keyword-arg
618             type_name=self._model.type_name,
619             _value=self._model._value,
620             description=self._model.description
621         )
622
623     def validate(self):
624         pass
625
626     def coerce(self, report_issues):                                                                # pylint: disable=arguments-differ
627         value = self._model._value
628         if value is not None:
629             evaluation = functions.evaluate(value, self._model, report_issues)
630             if (evaluation is not None) and evaluation.final:
631                 # A final evaluation can safely replace the existing value
632                 self._model._value = evaluation.value
633
634
635 class Attribute(_Parameter):
636     pass
637
638
639 class Input(_Parameter):
640     pass
641
642
643 class Output(_Parameter):
644     pass
645
646
647 class Argument(_Parameter):
648     pass
649
650
651 class Property(_Parameter):
652     pass
653
654
655 class Configuration(_Parameter):
656     pass
657
658
659 class Type(common.InstanceHandlerBase):
660     def coerce(self, **_):
661         pass
662
663     def dump(self, out_stream):
664         if self._model.name:
665             out_stream.write(out_stream.type_style(self._model.name))
666         with out_stream.indent():
667             for child in self._model.children:
668                 self._topology.dump(child, out_stream)
669
670     def validate(self, **kwargs):
671         pass