vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / orchestrator / topology / template_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 datetime import datetime
17
18 from ...utils import (
19     formatting,
20     versions
21 )
22 from ...modeling import utils as modeling_utils
23 from . import utils, common
24
25
26 class ServiceTemplate(common.TemplateHandlerBase):
27     def dump(self, out_stream):
28         if self._model.description is not None:
29             out_stream.write(out_stream.meta_style(self._model.description))
30         self._topology.dump(self._model.meta_data, out_stream, title='Metadata')
31         self._topology.dump(self._model.node_templates, out_stream)
32         self._topology.dump(self._model.group_templates, out_stream)
33         self._topology.dump(self._model.policy_templates, out_stream)
34         self._topology.dump(self._model.substitution_template, out_stream)
35         self._topology.dump(self._model.inputs, out_stream, title='Inputs')
36         self._topology.dump(self._model.outputs, out_stream, title='Outputs')
37         self._topology.dump(self._model.workflow_templates, out_stream, title='Workflow templates')
38
39     def coerce(self, **kwargs):
40         self._coerce(self._model.meta_data,
41                      self._model.node_templates,
42                      self._model.group_templates,
43                      self._model.policy_templates,
44                      self._model.substitution_template,
45                      self._model.inputs,
46                      self._model.outputs,
47                      self._model.workflow_templates,
48                      **kwargs)
49
50     def instantiate(self, instance_cls, inputs=None, plugins=None):                                 # pylint: disable=arguments-differ
51         now = datetime.now()
52
53         # modeling_utils.validate_no_undeclared_inputs(
54         #     declared_inputs=self._model.inputs, supplied_inputs=inputs or {})
55         modeling_utils.validate_required_inputs_are_supplied(
56             declared_inputs=self._model.inputs, supplied_inputs=inputs or {})
57
58         service = instance_cls(
59             created_at=now,
60             updated_at=now,
61             description=utils.deepcopy_with_locators(self._model.description),
62             service_template=self._model,
63             inputs=modeling_utils.merge_parameter_values(inputs, self._model.inputs)
64         )
65
66         for plugin_specification in self._model.plugin_specifications.itervalues():
67             if plugin_specification.enabled and plugins:
68                 if self._resolve_plugin_specification(plugin_specification, plugins):
69                     plugin = plugin_specification.plugin
70                     service.plugins[plugin.name] = plugin
71                 else:
72                     self._topology.report('specified plugin not found: {0}'.format(
73                         plugin_specification.name), level=self._topology.Issue.EXTERNAL)
74         service.meta_data = self._topology.instantiate(self._model.meta_data)
75
76         for node_template in self._model.node_templates.itervalues():
77             for _ in range(self._scaling(node_template)['default_instances']):
78                 node = self._topology.instantiate(node_template)
79                 service.nodes[node.name] = node
80
81         service.groups = self._topology.instantiate(self._model.group_templates)
82         service.policies = self._topology.instantiate(self._model.policy_templates)
83         service.workflows = self._topology.instantiate(self._model.workflow_templates)
84         service.substitution = self._topology.instantiate(self._model.substitution_template)
85         service.outputs = self._topology.instantiate(self._model.outputs)
86
87         return service
88
89     @staticmethod
90     def _resolve_plugin_specification(plugin_specification, plugins):
91         matching_plugins = []
92         if plugins:
93             for plugin in plugins:
94                 if (plugin.name == plugin_specification.name and
95                         (plugin_specification.version is None or
96                          versions.VersionString(plugin.package_version) >=
97                          plugin_specification.version)
98                    ):
99                     matching_plugins.append(plugin)
100         plugin_specification.plugin = None
101         if matching_plugins:
102             # Return highest version of plugin
103             plugin_specification.plugin = \
104                 max(matching_plugins,
105                     key=lambda plugin: versions.VersionString(plugin.package_version).key)
106         return plugin_specification.plugin is not None
107
108     def _scaling(self, node_template):
109         scaling = node_template.scaling
110
111         if any([scaling['min_instances'] < 0,
112                 scaling['max_instances'] < scaling['min_instances'],
113                 scaling['max_instances'] < 0,
114
115                 scaling['default_instances'] < 0,
116                 scaling['default_instances'] < scaling['min_instances'],
117                 scaling['default_instances'] > scaling['max_instances']
118                ]):
119             self._topology.report(
120                 'invalid scaling parameters for node template "{0}": min={min_instances}, max='
121                 '{max_instances}, default={default_instances}'.format(self._model.name, **scaling),
122                 level=self._topology.Issue.BETWEEN_TYPES)
123
124         return scaling
125
126     def validate(self, **kwargs):
127         self._validate(
128             self._model.meta_data,
129             self._model.node_templates,
130             self._model.group_templates,
131             self._model.policy_templates,
132             self._model.substitution_template,
133             self._model.inputs,
134             self._model.outputs,
135             self._model.workflow_templates,
136             self._model.node_types,
137             self._model.group_types,
138             self._model.policy_types,
139             self._model.relationship_types,
140             self._model.capability_types,
141             self._model.interface_types,
142             self._model.artifact_types,
143             **kwargs
144         )
145
146
147 class ArtifactTemplate(common.TemplateHandlerBase):
148     def dump(self, out_stream):
149         out_stream.write(out_stream.node_style(self._model.name))
150         if self._model.description:
151             out_stream.write(out_stream.meta_style(self._model.description))
152         with out_stream.indent():
153             out_stream.write('Artifact type: {0}'.format(out_stream.type_style(
154                 self._model.type.name)))
155             out_stream.write('Source path: {0}'.format(out_stream.literal_style(
156                 self._model.source_path)))
157             if self._model.target_path is not None:
158                 out_stream.write('Target path: {0}'.format(out_stream.literal_style(
159                     self._model.target_path)))
160             if self._model.repository_url is not None:
161                 out_stream.write('Repository URL: {0}'.format(
162                     out_stream.literal_style(self._model.repository_url)))
163             if self._model.repository_credential:
164                 out_stream.write('Repository credential: {0}'.format(
165                     out_stream.literal_style(self._model.repository_credential)))
166             self._topology.dump(self._model.properties, out_stream, title='Properties')
167
168     def coerce(self, **kwargs):
169         self._topology.coerce(self._model.properties, **kwargs)
170
171     def instantiate(self, instance_cls, **_):
172         return instance_cls(
173             name=self._model.name,
174             type=self._model.type,
175             description=utils.deepcopy_with_locators(self._model.description),
176             source_path=self._model.source_path,
177             target_path=self._model.target_path,
178             repository_url=self._model.repository_url,
179             repository_credential=self._model.repository_credential,
180             artifact_template=self._model)
181
182     def validate(self, **kwargs):
183         self._topology.validate(self._model.properties, **kwargs)
184
185
186 class CapabilityTemplate(common.TemplateHandlerBase):
187     def dump(self, out_stream):
188         out_stream.write(out_stream.node_style(self._model.name))
189         if self._model.description:
190             out_stream.write(out_stream.meta_style(self._model.description))
191         with out_stream.indent():
192             out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
193             out_stream.write(
194                 'Occurrences: {0:d}{1}'.format(
195                     self._model.min_occurrences or 0,
196                     ' to {0:d}'.format(self._model.max_occurrences)
197                     if self._model.max_occurrences is not None
198                     else ' or more'))
199             if self._model.valid_source_node_types:
200                 out_stream.write('Valid source node types: {0}'.format(
201                     ', '.join((str(out_stream.type_style(v.name))
202                                for v in self._model.valid_source_node_types))))
203             self._topology.dump(self._model.properties, out_stream, title='Properties')
204
205     def coerce(self, **kwargs):
206         self._topology.coerce(self._model.properties, **kwargs)
207
208     def instantiate(self, instance_cls, **_):
209         capability = instance_cls(
210             name=self._model.name,
211             type=self._model.type,
212             min_occurrences=self._model.min_occurrences,
213             max_occurrences=self._model.max_occurrences,
214             occurrences=0,
215             capability_template=self._model)
216         capability.properties = self._topology.instantiate(self._model.properties)
217         return capability
218
219     def validate(self, **kwargs):
220         self._topology.validate(self._model.properties, **kwargs)
221
222
223 class RequirementTemplate(common.TemplateHandlerBase):
224     def dump(self, out_stream):
225         if self._model.name:
226             out_stream.write(out_stream.node_style(self._model.name))
227         else:
228             out_stream.write('Requirement:')
229         with out_stream.indent():
230             if self._model.target_node_type is not None:
231                 out_stream.write('Target node type: {0}'.format(
232                     out_stream.type_style(self._model.target_node_type.name)))
233             elif self._model.target_node_template is not None:
234                 out_stream.write('Target node template: {0}'.format(
235                     out_stream.node_style(self._model.target_node_template.name)))
236             if self._model.target_capability_type is not None:
237                 out_stream.write('Target capability type: {0}'.format(
238                     out_stream.type_style(self._model.target_capability_type.name)))
239             elif self._model.target_capability_name is not None:
240                 out_stream.write('Target capability name: {0}'.format(
241                     out_stream.node_style(self._model.target_capability_name)))
242             if self._model.target_node_template_constraints:
243                 out_stream.write('Target node template constraints:')
244                 with out_stream.indent():
245                     for constraint in self._model.target_node_template_constraints:
246                         out_stream.write(out_stream.literal_style(constraint))
247             if self._model.relationship_template:
248                 out_stream.write('Relationship:')
249                 with out_stream.indent():
250                     self._topology.dump(self._model.relationship_template, out_stream)
251
252     def coerce(self, **kwargs):
253         self._topology.coerce(self._model.relationship_template, **kwargs)
254
255     def instantiate(self, instance_cls, **_):
256         pass
257
258     def validate(self, **kwargs):
259         self._topology.validate(self._model.relationship_template, **kwargs)
260
261
262 class GroupTemplate(common.TemplateHandlerBase):
263     def dump(self, out_stream):
264         out_stream.write('Group template: {0}'.format(out_stream.node_style(self._model.name)))
265         if self._model.description:
266             out_stream.write(out_stream.meta_style(self._model.description))
267         with out_stream.indent():
268             out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
269             self._topology.dump(self._model.properties, out_stream, title='Properties')
270             self._topology.dump(self._model.interface_templates, out_stream,
271                                 title='Interface Templates')
272             if self._model.node_templates:
273                 out_stream.write('Member node templates: {0}'.format(', '.join(
274                     (str(out_stream.node_style(v.name)) for v in self._model.node_templates))))
275
276     def coerce(self, **kwargs):
277         self._coerce(self._model.properties,
278                      self._model.interface_templates,
279                      **kwargs)
280
281     def instantiate(self, instance_cls, **_):
282         group = instance_cls(
283             name=self._model.name,
284             type=self._model.type,
285             description=utils.deepcopy_with_locators(self._model.description),
286             group_template=self._model)
287         group.properties = self._topology.instantiate(self._model.properties)
288         group.interfaces = self._topology.instantiate(self._model.interface_templates)
289         if self._model.node_templates:
290             for node_template in self._model.node_templates:
291                 group.nodes += node_template.nodes
292         return group
293
294     def validate(self, **kwargs):
295         self._validate(self._model.properties,
296                        self._model.interface_templates,
297                        **kwargs)
298
299
300 class InterfaceTemplate(common.TemplateHandlerBase):
301     def dump(self, out_stream):
302         out_stream.write(out_stream.node_style(self._model.name))
303         if self._model.description:
304             out_stream.write(out_stream.meta_style(self._model.description))
305         with out_stream.indent():
306             out_stream.write('Interface type: {0}'.format(out_stream.type_style(
307                 self._model.type.name)))
308             self._topology.dump(self._model.inputs, out_stream, title='Inputs')
309             self._topology.dump(self._model.operation_templates, out_stream,
310                                 title='Operation templates')
311
312     def coerce(self, **kwargs):
313         self._coerce(self._model.inputs,
314                      self._model.operation_templates,
315                      **kwargs)
316
317     def instantiate(self, instance_cls, **_):
318         interface = instance_cls(
319             name=self._model.name,
320             type=self._model.type,
321             description=utils.deepcopy_with_locators(self._model.description),
322             interface_template=self._model)
323         interface.inputs = self._topology.instantiate(self._model.inputs)
324         interface.operations = self._topology.instantiate(self._model.operation_templates)
325         return interface
326
327     def validate(self, **kwargs):
328         self._validate(self._model.inputs,
329                        self._model.operation_templates,
330                        **kwargs)
331
332
333 class NodeTemplate(common.TemplateHandlerBase):
334     def dump(self, out_stream):
335         out_stream.write('Node template: {0}'.format(out_stream.node_style(self._model.name)))
336         with out_stream.indent():
337             if self._model.description:
338                 out_stream.write(out_stream.meta_style(self._model.description))
339             out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
340             self._topology.dump(self._model.properties, out_stream, title='Properties')
341             self._topology.dump(self._model.attributes, out_stream, title='Attributes')
342             self._topology.dump(
343                 self._model.interface_templates, out_stream, title='Interface Templates')
344             self._topology.dump(
345                 self._model.artifact_templates, out_stream, title='Artifact Templates')
346             self._topology.dump(
347                 self._model.capability_templates, out_stream, title='Capability Templates')
348             self._topology.dump(
349                 self._model.requirement_templates, out_stream, title='Requirement Templates')
350
351     def coerce(self, **kwargs):
352         self._coerce(self._model.properties,
353                      self._model.attributes,
354                      self._model.interface_templates,
355                      self._model.artifact_templates,
356                      self._model.capability_templates,
357                      self._model.requirement_templates,
358                      **kwargs)
359
360     def instantiate(self, instance_cls, **_):
361         node = instance_cls(
362             name=self._model._next_name,
363             type=self._model.type,
364             description=utils.deepcopy_with_locators(self._model.description),
365             node_template=self._model
366         )
367
368         node.properties = self._topology.instantiate(self._model.properties)
369         node.attributes = self._topology.instantiate(self._model.attributes)
370         node.interfaces = self._topology.instantiate(self._model.interface_templates)
371         node.artifacts = self._topology.instantiate(self._model.artifact_templates)
372         node.capabilities = self._topology.instantiate(self._model.capability_templates)
373
374         # Default attributes
375         if 'tosca_name' in node.attributes and node.attributes['tosca_name'].type_name == 'string':
376             node.attributes['tosca_name'].value = self._model.name
377         if 'tosca_id' in node.attributes and node.attributes['tosca_id'].type_name == 'string':
378             node.attributes['tosca_id'].value = node.name
379
380         return node
381
382     def validate(self, **kwargs):
383         self._validate(self._model.properties,
384                        self._model.attributes,
385                        self._model.interface_templates,
386                        self._model.artifact_templates,
387                        self._model.capability_templates,
388                        self._model.requirement_templates,
389                        **kwargs)
390
391
392 class PolicyTemplate(common.TemplateHandlerBase):
393     def dump(self, out_stream):
394         out_stream.write('Policy template: {0}'.format(out_stream.node_style(self._model.name)))
395         if self._model.description:
396             out_stream.write(out_stream.meta_style(self._model.description))
397         with out_stream.indent():
398             out_stream.write('Type: {0}'.format(out_stream.type_style(self._model.type.name)))
399             self._topology.dump(self._model.properties, out_stream, title='Properties')
400             if self._model.node_templates:
401                 out_stream.write('Target node templates: {0}'.format(', '.join(
402                     (str(out_stream.node_style(v.name)) for v in self._model.node_templates))))
403             if self._model.group_templates:
404                 out_stream.write('Target group templates: {0}'.format(', '.join(
405                     (str(out_stream.node_style(v.name)) for v in self._model.group_templates))))
406
407     def coerce(self, **kwargs):
408         self._topology.coerce(self._model.properties, **kwargs)
409
410     def instantiate(self, instance_cls, **_):
411         policy = instance_cls(
412             name=self._model.name,
413             type=self._model.type,
414             description=utils.deepcopy_with_locators(self._model.description),
415             policy_template=self._model)
416
417         policy.properties = self._topology.instantiate(self._model.properties)
418         if self._model.node_templates:
419             for node_template in self._model.node_templates:
420                 policy.nodes += node_template.nodes
421         if self._model.group_templates:
422             for group_template in self._model.group_templates:
423                 policy.groups += group_template.groups
424         return policy
425
426     def validate(self, **kwargs):
427         self._topology.validate(self._model.properties, **kwargs)
428
429
430 class SubstitutionTemplate(common.TemplateHandlerBase):
431
432     def dump(self, out_stream):
433         out_stream.write('Substitution template:')
434         with out_stream.indent():
435             out_stream.write('Node type: {0}'.format(out_stream.type_style(
436                 self._model.node_type.name)))
437             self._topology.dump(self._model.mappings, out_stream, title='Mappings')
438
439     def coerce(self, **kwargs):
440         self._topology.coerce(self._model.mappings, **kwargs)
441
442     def instantiate(self, instance_cls, **_):
443         return instance_cls(node_type=self._model.node_type, substitution_template=self._model)
444
445     def validate(self, **kwargs):
446         self._topology.validate(self._model.mappings, **kwargs)
447
448
449 class SubstitutionTemplateMapping(common.TemplateHandlerBase):
450
451     def dump(self, out_stream):
452         if self._model.capability_template is not None:
453             node_template = self._model.capability_template.node_template
454         else:
455             node_template = self._model.requirement_template.node_template
456         out_stream.write('{0} -> {1}.{2}'.format(
457             out_stream.node_style(self._model.name),
458             out_stream.node_style(node_template.name),
459             out_stream.node_style(self._model.capability_template.name
460                                   if self._model.capability_template
461                                   else self._model.requirement_template.name)))
462
463     def coerce(self, **_):
464         pass
465
466     def instantiate(self, instance_cls, **_):
467         substitution_mapping = instance_cls(
468             name=self._model.name,
469             requirement_template=self._model.requirement_template)
470
471         if self._model.capability_template is not None:
472             node_template = self._model.capability_template.node_template
473         else:
474             node_template = self._model.requirement_template.node_template
475         nodes = node_template.nodes
476         if len(nodes) == 0:
477             self._topology.report(
478                 'mapping "{0}" refers to node template "{1}" but there are no node instances'.
479                 format(self._model.mapped_name, self._model.node_template.name),
480                 level=self._topology.Issue.BETWEEN_INSTANCES)
481             return None
482         # The TOSCA spec does not provide a way to choose the node,
483         # so we will just pick the first one
484         substitution_mapping.node_style = nodes[0]
485         if self._model.capability_template:
486             for a_capability in substitution_mapping.node_style.capabilities.itervalues():
487                 if a_capability.capability_template.name == \
488                         self._model.capability_template.name:
489                     substitution_mapping.capability = a_capability
490
491         return substitution_mapping
492
493     def validate(self, **_):
494         if self._model.capability_template is None and self._model.requirement_template is None:
495             self._topology.report(
496                 'mapping "{0}" refers to neither capability nor a requirement '
497                 'in node template: {1}'.format(
498                     self._model.name, formatting.safe_repr(self._model.node_template.name)),
499                 level=self._topology.Issue.BETWEEN_TYPES)
500
501
502 class RelationshipTemplate(common.TemplateHandlerBase):
503     def dump(self, out_stream):
504         if self._model.type is not None:
505             out_stream.write('Relationship type: {0}'.format(out_stream.type_style(
506                 self._model.type.name)))
507         else:
508             out_stream.write('Relationship template: {0}'.format(
509                 out_stream.node_style(self._model.name)))
510         if self._model.description:
511             out_stream.write(out_stream.meta_style(self._model.description))
512         with out_stream.indent():
513             self._topology.dump(self._model.properties, out_stream, title='Properties')
514             self._topology.dump(self._model.interface_templates, out_stream,
515                                 title='Interface Templates')
516
517     def coerce(self, **kwargs):
518         self._coerce(self._model.properties, self._model.interface_templates, **kwargs)
519
520     def instantiate(self, instance_cls, **_):
521         relationship = instance_cls(
522             name=self._model.name,
523             type=self._model.type,
524             relationship_template=self._model)
525
526         relationship.properties = self._topology.instantiate(self._model.properties)
527         relationship.interfaces = self._topology.instantiate(self._model.interface_templates)
528         return relationship
529
530     def validate(self, **kwargs):
531         self._validate(self._model.properties, self._model.interface_templates, **kwargs)
532
533
534 class OperationTemplate(common.TemplateHandlerBase):
535
536     def dump(self, out_stream):
537         out_stream.write(out_stream.node_style(self._model.name))
538         if self._model.description:
539             out_stream.write(out_stream.meta_style(self._model.description))
540         with out_stream.indent():
541             if self._model.implementation is not None:
542                 out_stream.write('Implementation: {0}'.format(
543                     out_stream.literal_style(self._model.implementation)))
544             if self._model.dependencies:
545                 out_stream.write('Dependencies: {0}'.format(', '.join(
546                     (str(out_stream.literal_style(v)) for v in self._model.dependencies))))
547             self._topology.dump(self._model.inputs, out_stream, title='Inputs')
548             if self._model.executor is not None:
549                 out_stream.write('Executor: {0}'.format(
550                     out_stream.literal_style(self._model.executor)))
551             if self._model.max_attempts is not None:
552                 out_stream.write('Max attempts: {0}'.format(out_stream.literal_style(
553                     self._model.max_attempts)))
554             if self._model.retry_interval is not None:
555                 out_stream.write('Retry interval: {0}'.format(
556                     out_stream.literal_style(self._model.retry_interval)))
557             if self._model.plugin_specification is not None:
558                 out_stream.write('Plugin specification: {0}'.format(
559                     out_stream.literal_style(self._model.plugin_specification.name)))
560             self._topology.dump(self._model.configurations, out_stream, title='Configuration')
561             if self._model.function is not None:
562                 out_stream.write('Function: {0}'.format(out_stream.literal_style(
563                     self._model.function)))
564
565     def coerce(self, **kwargs):
566         self._coerce(self._model.inputs,
567                      self._model.configurations,
568                      **kwargs)
569
570     def instantiate(self, instance_cls, **_):
571         operation = instance_cls(
572             name=self._model.name,
573             description=utils.deepcopy_with_locators(self._model.description),
574             relationship_edge=self._model.relationship_edge,
575             implementation=self._model.implementation,
576             dependencies=self._model.dependencies,
577             executor=self._model.executor,
578             function=self._model.function,
579             max_attempts=self._model.max_attempts,
580             retry_interval=self._model.retry_interval,
581             operation_template=self._model)
582
583         if (self._model.plugin_specification is not None and
584                 self._model.plugin_specification.enabled):
585             operation.plugin = self._model.plugin_specification.plugin
586
587         operation.inputs = self._topology.instantiate(self._model.inputs)
588         operation.configurations = self._topology.instantiate(self._model.configurations)
589
590         return operation
591
592     def validate(self, **kwargs):
593         self._validate(self._model.inputs,
594                        self._model.configurations,
595                        **kwargs)
596
597
598 class PluginSpecification(common.HandlerBase):
599     def validate(self, **kwargs):
600         pass
601
602     def coerce(self, **kwargs):
603         pass
604
605     def instantiate(self, **_):
606         pass
607
608     def dump(self, out_stream):
609         pass