vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / extensions / aria_extension_tosca / simple_v1_0 / modeling / __init__.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 """
17 Creates ARIA service template models based on the TOSCA presentation.
18
19 Relies on many helper methods in the presentation classes.
20 """
21
22 #pylint: disable=unsubscriptable-object
23
24 import os
25 import re
26 from types import FunctionType
27 from datetime import datetime
28
29 from ruamel import yaml
30
31 from aria.parser.validation import Issue
32 from aria.utils.formatting import string_list_as_string
33 from aria.utils.collections import (StrictDict, OrderedDict)
34 from aria.orchestrator import WORKFLOW_DECORATOR_RESERVED_ARGUMENTS
35 from aria.modeling.models import (Type, ServiceTemplate, NodeTemplate,
36                                   RequirementTemplate, RelationshipTemplate, CapabilityTemplate,
37                                   GroupTemplate, PolicyTemplate, SubstitutionTemplate,
38                                   SubstitutionTemplateMapping, InterfaceTemplate, OperationTemplate,
39                                   ArtifactTemplate, Metadata, Input, Output, Property,
40                                   Attribute, Configuration, PluginSpecification)
41
42 from .parameters import coerce_parameter_value
43 from .constraints import (Equal, GreaterThan, GreaterOrEqual, LessThan, LessOrEqual, InRange,
44                           ValidValues, Length, MinLength, MaxLength, Pattern)
45 from ..data_types import coerce_value
46
47
48 # These match the first un-escaped ">"
49 # See: http://stackoverflow.com/a/11819111/849021
50 IMPLEMENTATION_PREFIX_REGEX = re.compile(r'(?<!\\)(?:\\\\)*>')
51
52
53 def create_service_template_model(context): # pylint: disable=too-many-locals,too-many-branches
54     model = ServiceTemplate(created_at=datetime.now(),
55                             main_file_name=os.path.basename(str(context.presentation.location)))
56
57     model.description = context.presentation.get('service_template', 'description', 'value')
58
59     # Metadata
60     metadata = context.presentation.get('service_template', 'metadata')
61     if metadata is not None:
62         create_metadata_models(context, model, metadata)
63
64     # Types
65     model.node_types = Type(variant='node')
66     create_types(context,
67                  model.node_types,
68                  context.presentation.get('service_template', 'node_types'))
69     model.group_types = Type(variant='group')
70     create_types(context,
71                  model.group_types,
72                  context.presentation.get('service_template', 'group_types'))
73     model.policy_types = Type(variant='policy')
74     create_types(context,
75                  model.policy_types,
76                  context.presentation.get('service_template', 'policy_types'))
77     model.relationship_types = Type(variant='relationship')
78     create_types(context,
79                  model.relationship_types,
80                  context.presentation.get('service_template', 'relationship_types'))
81     model.capability_types = Type(variant='capability')
82     create_types(context,
83                  model.capability_types,
84                  context.presentation.get('service_template', 'capability_types'))
85     model.interface_types = Type(variant='interface')
86     create_types(context,
87                  model.interface_types,
88                  context.presentation.get('service_template', 'interface_types'))
89     model.artifact_types = Type(variant='artifact')
90     create_types(context,
91                  model.artifact_types,
92                  context.presentation.get('service_template', 'artifact_types'))
93
94     # Topology template
95     topology_template = context.presentation.get('service_template', 'topology_template')
96     if topology_template is not None:
97         model.inputs.update(
98             create_input_models_from_values(topology_template._get_input_values(context)))
99         model.outputs.update(
100             create_output_models_from_values(topology_template._get_output_values(context)))
101
102     # Plugin specifications
103     policies = context.presentation.get('service_template', 'topology_template', 'policies')
104     if policies:
105         for policy in policies.itervalues():
106             role = model.policy_types.get_descendant(policy.type).role
107             if role == 'plugin':
108                 plugin_specification = create_plugin_specification_model(context, policy)
109                 model.plugin_specifications[plugin_specification.name] = plugin_specification
110             elif role == 'workflow':
111                 operation_template = create_workflow_operation_template_model(context,
112                                                                               model, policy)
113                 model.workflow_templates[operation_template.name] = operation_template
114
115     # Node templates
116     node_templates = context.presentation.get('service_template', 'topology_template',
117                                               'node_templates')
118     if node_templates:
119         for node_template in node_templates.itervalues():
120             node_template_model = create_node_template_model(context, model, node_template)
121             model.node_templates[node_template_model.name] = node_template_model
122         for node_template in node_templates.itervalues():
123             fix_node_template_model(context, model, node_template)
124
125     # Group templates
126     groups = context.presentation.get('service_template', 'topology_template', 'groups')
127     if groups:
128         for group in groups.itervalues():
129             group_template_model = create_group_template_model(context, model, group)
130             model.group_templates[group_template_model.name] = group_template_model
131
132     # Policy templates
133     policies = context.presentation.get('service_template', 'topology_template', 'policies')
134     if policies:
135         for policy in policies.itervalues():
136             policy_template_model = create_policy_template_model(context, model, policy)
137             model.policy_templates[policy_template_model.name] = policy_template_model
138
139     # Substitution template
140     substitution_mappings = context.presentation.get('service_template', 'topology_template',
141                                                      'substitution_mappings')
142     if substitution_mappings:
143         model.substitution_template = create_substitution_template_model(context, model,
144                                                                          substitution_mappings)
145
146     return model
147
148
149 def create_metadata_models(context, service_template, metadata):
150     service_template.meta_data['template_name'] = Metadata(name='template_name',
151                                                            value=metadata.template_name)
152     service_template.meta_data['template_author'] = Metadata(name='template_author',
153                                                              value=metadata.template_author)
154     service_template.meta_data['template_version'] = Metadata(name='template_version',
155                                                               value=metadata.template_version)
156     custom = metadata.custom
157     if custom:
158         for name, value in custom.iteritems():
159             service_template.meta_data[name] = Metadata(name=name,
160                                                         value=value)
161
162
163 def create_node_template_model(context, service_template, node_template):
164     node_type = node_template._get_type(context)
165     node_type = service_template.node_types.get_descendant(node_type._name)
166     model = NodeTemplate(name=node_template._name, type=node_type)
167
168     if node_template.description:
169         model.description = node_template.description.value
170
171     if node_template.directives:
172         model.directives = node_template.directives
173
174     model.properties.update(create_property_models_from_values(
175         template_properties=node_template._get_property_values(context)))
176     model.attributes.update(create_attribute_models_from_values(
177         template_attributes=node_template._get_attribute_default_values(context)))
178
179     create_interface_template_models(context, service_template, model.interface_templates,
180                                      node_template._get_interfaces(context))
181
182     artifacts = node_template._get_artifacts(context)
183     if artifacts:
184         for artifact_name, artifact in artifacts.iteritems():
185             model.artifact_templates[artifact_name] = \
186                 create_artifact_template_model(context, service_template, artifact)
187
188     capabilities = node_template._get_capabilities(context)
189     if capabilities:
190         for capability_name, capability in capabilities.iteritems():
191             model.capability_templates[capability_name] = \
192                 create_capability_template_model(context, service_template, capability)
193
194     if node_template.node_filter:
195         model.target_node_template_constraints = []
196         create_node_filter_constraints(context, node_template.node_filter,
197                                        model.target_node_template_constraints)
198
199     return model
200
201
202 def fix_node_template_model(context, service_template, node_template):
203     # Requirements have to be created after all node templates have been created, because
204     # requirements might reference another node template
205     model = service_template.node_templates[node_template._name]
206     requirements = node_template._get_requirements(context)
207     if requirements:
208         for _, requirement in requirements:
209             model.requirement_templates.append(create_requirement_template_model(context,
210                                                                                  service_template,
211                                                                                  requirement))
212
213
214 def create_group_template_model(context, service_template, group):
215     group_type = group._get_type(context)
216     group_type = service_template.group_types.get_descendant(group_type._name)
217     model = GroupTemplate(name=group._name,
218                           type=group_type)
219
220     if group.description:
221         model.description = group.description.value
222
223     model.properties.update(create_property_models_from_values(group._get_property_values(context)))
224
225     create_interface_template_models(context, service_template, model.interface_templates,
226                                      group._get_interfaces(context))
227     members = group.members
228     if members:
229         for member in members:
230             node_template = service_template.node_templates[member]
231             assert node_template
232             model.node_templates.append(node_template)
233
234     return model
235
236
237 def create_policy_template_model(context, service_template, policy):
238     policy_type = policy._get_type(context)
239     policy_type = service_template.policy_types.get_descendant(policy_type._name)
240     model = PolicyTemplate(name=policy._name,
241                            type=policy_type)
242
243     if policy.description:
244         model.description = policy.description.value
245
246     model.properties.update(
247         create_property_models_from_values(policy._get_property_values(context)))
248
249     node_templates, groups = policy._get_targets(context)
250     if node_templates:
251         for target in node_templates:
252             node_template = service_template.node_templates[target._name]
253             assert node_template
254             model.node_templates.append(node_template)
255     if groups:
256         for target in groups:
257             group_template = service_template.group_templates[target._name]
258             assert group_template
259             model.group_templates.append(group_template)
260
261     return model
262
263
264 def create_requirement_template_model(context, service_template, requirement):
265     model = {'name': requirement._name}
266
267     node, node_variant = requirement._get_node(context)
268     if node is not None:
269         if node_variant == 'node_type':
270             node_type = service_template.node_types.get_descendant(node._name)
271             model['target_node_type'] = node_type
272         else:
273             node_template = service_template.node_templates[node._name]
274             model['target_node_template'] = node_template
275
276     capability, capability_variant = requirement._get_capability(context)
277     if capability is not None:
278         if capability_variant == 'capability_type':
279             capability_type = \
280                 service_template.capability_types.get_descendant(capability._name)
281             model['target_capability_type'] = capability_type
282         else:
283             model['target_capability_name'] = capability._name
284
285     model = RequirementTemplate(**model)
286
287     if requirement.node_filter:
288         model.target_node_template_constraints = []
289         create_node_filter_constraints(context, requirement.node_filter,
290                                        model.target_node_template_constraints)
291
292     relationship = requirement.relationship
293     if relationship is not None:
294         model.relationship_template = \
295             create_relationship_template_model(context, service_template, relationship)
296         model.relationship_template.name = requirement._name
297
298     return model
299
300
301 def create_relationship_template_model(context, service_template, relationship):
302     relationship_type, relationship_type_variant = relationship._get_type(context)
303     if relationship_type_variant == 'relationship_type':
304         relationship_type = service_template.relationship_types.get_descendant(
305             relationship_type._name)
306         model = RelationshipTemplate(type=relationship_type)
307     else:
308         relationship_template = relationship_type
309         relationship_type = relationship_template._get_type(context)
310         relationship_type = service_template.relationship_types.get_descendant(
311             relationship_type._name)
312         model = RelationshipTemplate(type=relationship_type)
313         if relationship_template.description:
314             model.description = relationship_template.description.value
315
316     create_parameter_models_from_assignments(model.properties,
317                                              relationship.properties,
318                                              model_cls=Property)
319     create_interface_template_models(context, service_template, model.interface_templates,
320                                      relationship.interfaces)
321
322     return model
323
324
325 def create_capability_template_model(context, service_template, capability):
326     capability_type = capability._get_type(context)
327     capability_type = service_template.capability_types.get_descendant(capability_type._name)
328     model = CapabilityTemplate(name=capability._name,
329                                type=capability_type)
330
331     capability_definition = capability._get_definition(context)
332     if capability_definition.description:
333         model.description = capability_definition.description.value
334     occurrences = capability_definition.occurrences
335     if occurrences is not None:
336         model.min_occurrences = occurrences.value[0]
337         if occurrences.value[1] != 'UNBOUNDED':
338             model.max_occurrences = occurrences.value[1]
339
340     valid_source_types = capability_definition.valid_source_types
341     if valid_source_types:
342         for valid_source_type in valid_source_types:
343             # TODO: handle shortcut type names
344             node_type = service_template.node_types.get_descendant(valid_source_type)
345             model.valid_source_node_types.append(node_type)
346
347     create_parameter_models_from_assignments(model.properties,
348                                              capability.properties,
349                                              model_cls=Property)
350
351     return model
352
353
354 def create_interface_template_model(context, service_template, interface):
355     interface_type = interface._get_type(context)
356     interface_type = service_template.interface_types.get_descendant(interface_type._name)
357     model = InterfaceTemplate(name=interface._name, type=interface_type)
358
359     if interface_type.description:
360         model.description = interface_type.description
361
362     create_parameter_models_from_assignments(model.inputs, interface.inputs, model_cls=Input)
363
364     operations = interface.operations
365     if operations:
366         for operation_name, operation in operations.iteritems():
367             model.operation_templates[operation_name] = \
368                 create_operation_template_model(context, service_template, operation)
369
370     return model if model.operation_templates else None
371
372
373 def create_operation_template_model(context, service_template, operation):
374     model = OperationTemplate(name=operation._name)
375
376     if operation.description:
377         model.description = operation.description.value
378
379     implementation = operation.implementation
380     if implementation is not None:
381         primary = implementation.primary
382         extract_implementation_primary(context, service_template, operation, model, primary)
383         relationship_edge = operation._get_extensions(context).get('relationship_edge')
384         if relationship_edge is not None:
385             if relationship_edge == 'source':
386                 model.relationship_edge = False
387             elif relationship_edge == 'target':
388                 model.relationship_edge = True
389
390         dependencies = implementation.dependencies
391         configuration = OrderedDict()
392         if dependencies:
393             for dependency in dependencies:
394                 key, value = split_prefix(dependency)
395                 if key is not None:
396                     # Special ARIA prefix: signifies configuration parameters
397
398                     # Parse as YAML
399                     try:
400                         value = yaml.load(value)
401                     except yaml.parser.MarkedYAMLError as e:
402                         context.validation.report(
403                             'YAML parser {0} in operation configuration: {1}'
404                             .format(e.problem, value),
405                             locator=implementation._locator,
406                             level=Issue.FIELD)
407                         continue
408
409                     # Coerce to intrinsic functions, if there are any
410                     value = coerce_parameter_value(context, implementation, None, value).value
411
412                     # Support dot-notation nesting
413                     set_nested(configuration, key.split('.'), value)
414                 else:
415                     if model.dependencies is None:
416                         model.dependencies = []
417                     model.dependencies.append(dependency)
418
419         # Convert configuration to Configuration models
420         for key, value in configuration.iteritems():
421             model.configurations[key] = Configuration.wrap(key, value,
422                                                            description='Operation configuration.')
423
424     create_parameter_models_from_assignments(model.inputs, operation.inputs, model_cls=Input)
425     return model
426
427
428 def create_artifact_template_model(context, service_template, artifact):
429     artifact_type = artifact._get_type(context)
430     artifact_type = service_template.artifact_types.get_descendant(artifact_type._name)
431     model = ArtifactTemplate(name=artifact._name,
432                              type=artifact_type,
433                              source_path=artifact.file)
434
435     if artifact.description:
436         model.description = artifact.description.value
437
438     model.target_path = artifact.deploy_path
439
440     repository = artifact._get_repository(context)
441     if repository is not None:
442         model.repository_url = repository.url
443         credential = repository._get_credential(context)
444         if credential:
445             model.repository_credential = {}
446             for k, v in credential.iteritems():
447                 model.repository_credential[k] = v
448
449     model.properties.update(
450         create_property_models_from_values(artifact._get_property_values(context)))
451
452     return model
453
454
455 def create_substitution_template_model(context, service_template, substitution_mappings):
456     node_type = service_template.node_types.get_descendant(substitution_mappings.node_type)
457     model = SubstitutionTemplate(node_type=node_type)
458
459     capabilities = substitution_mappings.capabilities
460     if capabilities:
461         for mapped_capability_name, capability in capabilities.iteritems():
462             name = 'capability.' + mapped_capability_name
463             node_template_model = service_template.node_templates[capability.node_template]
464             capability_template_model = \
465                 node_template_model.capability_templates[capability.capability]
466             model.mappings[name] = \
467                 SubstitutionTemplateMapping(name=name,
468                                             capability_template=capability_template_model)
469
470     requirements = substitution_mappings.requirements
471     if requirements:
472         for mapped_requirement_name, requirement in requirements.iteritems():
473             name = 'requirement.' + mapped_requirement_name
474             node_template_model = service_template.node_templates[requirement.node_template]
475             requirement_template_model = None
476             for a_model in node_template_model.requirement_templates:
477                 if a_model.name == requirement.requirement:
478                     requirement_template_model = a_model
479                     break
480             model.mappings[name] = \
481                 SubstitutionTemplateMapping(name=name,
482                                             requirement_template=requirement_template_model)
483
484     return model
485
486
487 def create_plugin_specification_model(context, policy):
488     properties = policy.properties
489
490     def get(name, default=None):
491         prop = properties.get(name)
492         return prop.value if prop is not None else default
493
494     model = PluginSpecification(name=policy._name,
495                                 version=get('version'),
496                                 enabled=get('enabled', True))
497
498     return model
499
500
501 def create_workflow_operation_template_model(context, service_template, policy):
502     model = OperationTemplate(name=policy._name)
503     # since we use backpopulates, these fields are populated upon commit, we get a weird(temporary)
504     # behavior where in previous code service_template.workflow_templates is a dict which has None
505     # as key for the value of model.
506     service_template.workflow_templates[model.name] = model
507
508     if policy.description:
509         model.description = policy.description.value
510
511     properties = policy._get_property_values(context)
512     for prop_name, prop in properties.iteritems():
513         if prop_name == 'implementation':
514             model.function = prop.value
515         else:
516             input_model = create_parameter_model_from_value(prop, prop_name, model_cls=Input)
517             input_model.required = prop.required
518             model.inputs[prop_name] = input_model
519
520     used_reserved_names = WORKFLOW_DECORATOR_RESERVED_ARGUMENTS.intersection(model.inputs.keys())
521     if used_reserved_names:
522         context.validation.report('using reserved arguments in workflow policy "{0}": {1}'
523                                   .format(
524                                       policy._name,
525                                       string_list_as_string(used_reserved_names)),
526                                   locator=policy._locator,
527                                   level=Issue.EXTERNAL)
528     return model
529
530
531 #
532 # Utils
533 #
534
535 def create_types(context, root, types):
536     if types is None:
537         return
538
539     def added_all():
540         for name in types:
541             if root.get_descendant(name) is None:
542                 return False
543         return True
544
545     while not added_all():
546         for name, the_type in types.iteritems():
547             if root.get_descendant(name) is None:
548                 parent_type = the_type._get_parent(context)
549                 model = Type(name=the_type._name,
550                              role=the_type._get_extension('role'))
551                 if the_type.description:
552                     model.description = the_type.description.value
553                 if parent_type is None:
554                     model.parent = root
555                     model.variant = root.variant
556                     root.children.append(model)
557                 else:
558                     container = root.get_descendant(parent_type._name)
559                     if container is not None:
560                         model.parent = container
561                         model.variant = container.variant
562                         container.children.append(model)
563
564
565 def create_input_models_from_values(template_inputs):
566     model_inputs = {}
567     if template_inputs:
568         for template_input_name, template_input in template_inputs.iteritems():
569             model_input = create_parameter_model_from_value(template_input, template_input_name,
570                                                             model_cls=Input)
571             model_input.required = template_input.required
572             model_inputs[model_input.name] = model_input
573     return model_inputs
574
575 def create_output_models_from_values(template_outputs):
576     model_outputs = {}
577     for template_output_name, template_output in template_outputs.iteritems():
578         model_outputs[template_output_name] = \
579             create_parameter_model_from_value(template_output,
580                                               template_output_name,
581                                               model_cls=Output)
582     return model_outputs
583
584
585 def create_property_models_from_values(template_properties):
586     model_properties = {}
587     for template_property_name, template_property in template_properties.iteritems():
588         model_properties[template_property_name] = \
589             create_parameter_model_from_value(template_property,
590                                               template_property_name,
591                                               model_cls=Property)
592     return model_properties
593
594 def create_attribute_models_from_values(template_attributes):
595     model_attributes = {}
596     for template_attribute_name, template_attribute in template_attributes.iteritems():
597         model_attributes[template_attribute_name] = \
598             create_parameter_model_from_value(template_attribute,
599                                               template_attribute_name,
600                                               model_cls=Attribute)
601     return model_attributes
602
603
604 def create_parameter_model_from_value(template_parameter, template_parameter_name, model_cls):
605     return model_cls(name=template_parameter_name,
606                      type_name=template_parameter.type,
607                      value=template_parameter.value,
608                      description=template_parameter.description)
609
610
611 def create_parameter_models_from_assignments(properties, source_properties, model_cls):
612     if source_properties:
613         for property_name, prop in source_properties.iteritems():
614             properties[property_name] = model_cls(name=property_name,                               # pylint: disable=unexpected-keyword-arg
615                                                   type_name=prop.value.type,
616                                                   value=prop.value.value,
617                                                   description=prop.value.description)
618
619
620 def create_interface_template_models(context, service_template, interfaces, source_interfaces):
621     if source_interfaces:
622         for interface_name, interface in source_interfaces.iteritems():
623             interface = create_interface_template_model(context, service_template, interface)
624             if interface is not None:
625                 interfaces[interface_name] = interface
626
627
628 def create_node_filter_constraints(context, node_filter, target_node_template_constraints):
629     properties = node_filter.properties
630     if properties is not None:
631         for property_name, constraint_clause in properties:
632             constraint = create_constraint(context, node_filter, constraint_clause, property_name,
633                                            None)
634             target_node_template_constraints.append(constraint)
635
636     capabilities = node_filter.capabilities
637     if capabilities is not None:
638         for capability_name, capability in capabilities:
639             properties = capability.properties
640             if properties is not None:
641                 for property_name, constraint_clause in properties:
642                     constraint = create_constraint(context, node_filter, constraint_clause,
643                                                    property_name, capability_name)
644                     target_node_template_constraints.append(constraint)
645
646
647 def create_constraint(context, node_filter, constraint_clause, property_name, capability_name): # pylint: disable=too-many-return-statements
648     constraint_key = constraint_clause._raw.keys()[0]
649
650     the_type = constraint_clause._get_type(context)
651
652     def coerce_constraint(constraint):
653         if the_type is not None:
654             return coerce_value(context, node_filter, the_type, None, None, constraint,
655                                 constraint_key)
656         else:
657             return constraint
658
659     def coerce_constraints(constraints):
660         if the_type is not None:
661             return tuple(coerce_constraint(constraint) for constraint in constraints)
662         else:
663             return constraints
664
665     if constraint_key == 'equal':
666         return Equal(property_name, capability_name,
667                      coerce_constraint(constraint_clause.equal))
668     elif constraint_key == 'greater_than':
669         return GreaterThan(property_name, capability_name,
670                            coerce_constraint(constraint_clause.greater_than))
671     elif constraint_key == 'greater_or_equal':
672         return GreaterOrEqual(property_name, capability_name,
673                               coerce_constraint(constraint_clause.greater_or_equal))
674     elif constraint_key == 'less_than':
675         return LessThan(property_name, capability_name,
676                         coerce_constraint(constraint_clause.less_than))
677     elif constraint_key == 'less_or_equal':
678         return LessOrEqual(property_name, capability_name,
679                            coerce_constraint(constraint_clause.less_or_equal))
680     elif constraint_key == 'in_range':
681         return InRange(property_name, capability_name,
682                        coerce_constraints(constraint_clause.in_range))
683     elif constraint_key == 'valid_values':
684         return ValidValues(property_name, capability_name,
685                            coerce_constraints(constraint_clause.valid_values))
686     elif constraint_key == 'length':
687         return Length(property_name, capability_name,
688                       coerce_constraint(constraint_clause.length))
689     elif constraint_key == 'min_length':
690         return MinLength(property_name, capability_name,
691                          coerce_constraint(constraint_clause.min_length))
692     elif constraint_key == 'max_length':
693         return MaxLength(property_name, capability_name,
694                          coerce_constraint(constraint_clause.max_length))
695     elif constraint_key == 'pattern':
696         return Pattern(property_name, capability_name,
697                        coerce_constraint(constraint_clause.pattern))
698     else:
699         raise ValueError('malformed node_filter: {0}'.format(constraint_key))
700
701
702 def split_prefix(string):
703     """
704     Splits the prefix on the first non-escaped ">".
705     """
706
707     split = IMPLEMENTATION_PREFIX_REGEX.split(string, 1)
708     if len(split) < 2:
709         return None, None
710     return split[0].strip(), split[1].strip()
711
712
713 def set_nested(the_dict, keys, value):
714     """
715     If the ``keys`` list has just one item, puts the value in the the dict. If there are more items,
716     puts the value in a sub-dict, creating sub-dicts as necessary for each key.
717
718     For example, if ``the_dict`` is an empty dict, keys is ``['first', 'second', 'third']`` and
719     value is ``'value'``, then the_dict will be: ``{'first':{'second':{'third':'value'}}}``.
720
721     :param the_dict: Dict to change
722     :type the_dict: {}
723     :param keys: Keys
724     :type keys: [basestring]
725     :param value: Value
726     """
727     key = keys.pop(0)
728     if len(keys) == 0:
729         the_dict[key] = value
730     else:
731         if key not in the_dict:
732             the_dict[key] = StrictDict(key_class=basestring)
733         set_nested(the_dict[key], keys, value)
734
735
736 def extract_implementation_primary(context, service_template, presentation, model, primary):
737     prefix, postfix = split_prefix(primary)
738     if prefix:
739         # Special ARIA prefix
740         model.plugin_specification = service_template.plugin_specifications.get(prefix)
741         model.function = postfix
742         if model.plugin_specification is None:
743             context.validation.report(
744                 'no policy for plugin "{0}" specified in operation implementation: {1}'
745                 .format(prefix, primary),
746                 locator=presentation._get_child_locator('properties', 'implementation'),
747                 level=Issue.BETWEEN_TYPES)
748     else:
749         # Standard TOSCA artifact with default plugin
750         model.implementation = primary