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
8 # http://www.apache.org/licenses/LICENSE-2.0
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.
17 ARIA modeling service instance module
20 # pylint: disable=too-many-lines, no-self-argument, no-member, abstract-method
22 from sqlalchemy import (
29 from sqlalchemy import DateTime
30 from sqlalchemy.ext.declarative import declared_attr
31 from sqlalchemy.ext.orderinglist import ordering_list
35 types as modeling_types
37 from .mixins import InstanceModelMixin
45 class ServiceBase(InstanceModelMixin):
47 Usually an instance of a :class:`ServiceTemplate` and its many associated templates (node
48 templates, group templates, policy templates, etc.). However, it can also be created
52 __tablename__ = 'service'
54 __private_fields__ = ('substitution_fk',
55 'service_template_fk')
57 # region one_to_one relationships
60 def substitution(cls):
62 Exposes the entire service as a single node.
64 :type: :class:`Substitution`
66 return relationship.one_to_one(cls, 'substitution', back_populates=relationship.NO_BACK_POP)
70 # region one_to_many relationships
77 :type: {:obj:`basestring`: :class:`Output`}
79 return relationship.one_to_many(cls, 'output', dict_key='name')
84 Externally provided parameters.
86 :type: {:obj:`basestring`: :class:`Input`}
88 return relationship.one_to_many(cls, 'input', dict_key='name')
95 :type: [:class:`ServiceUpdate`]
97 return relationship.one_to_many(cls, 'service_update')
100 def modifications(cls):
102 Service modifications.
104 :type: [:class:`ServiceModification`]
106 return relationship.one_to_many(cls, 'service_modification')
113 :type: [:class:`Execution`]
115 return relationship.one_to_many(cls, 'execution')
122 :type: {:obj:`basestring`, :class:`Node`}
124 return relationship.one_to_many(cls, 'node', dict_key='name')
131 :type: {:obj:`basestring`, :class:`Group`}
133 return relationship.one_to_many(cls, 'group', dict_key='name')
140 :type: {:obj:`basestring`, :class:`Policy`}
142 return relationship.one_to_many(cls, 'policy', dict_key='name')
149 :type: {:obj:`basestring`, :class:`Operation`}
151 return relationship.one_to_many(cls, 'operation', dict_key='name')
155 # region many_to_one relationships
158 def service_template(cls):
160 Source service template (can be ``None``).
162 :type: :class:`ServiceTemplate`
164 return relationship.many_to_one(cls, 'service_template')
168 # region many_to_many relationships
175 :type: {:obj:`basestring`, :class:`Metadata`}
177 # Warning! We cannot use the attr name "metadata" because it's used by SQLAlchemy!
178 return relationship.many_to_many(cls, 'metadata', dict_key='name')
185 :type: {:obj:`basestring`, :class:`Plugin`}
187 return relationship.many_to_many(cls, 'plugin', dict_key='name')
191 # region association proxies
194 def service_template_name(cls):
195 return relationship.association_proxy('service_template', 'name', type=':obj:`basestring`')
199 # region foreign keys
202 def substitution_fk(cls):
203 """Service one-to-one to Substitution"""
204 return relationship.foreign_key('substitution', nullable=True)
207 def service_template_fk(cls):
208 """For Service many-to-one to ServiceTemplate"""
209 return relationship.foreign_key('service_template', nullable=True)
213 description = Column(Text, doc="""
214 Human-readable description.
216 :type: :obj:`basestring`
219 created_at = Column(DateTime, nullable=False, index=True, doc="""
222 :type: :class:`~datetime.datetime`
225 updated_at = Column(DateTime, doc="""
228 :type: :class:`~datetime.datetime`
231 def get_node_by_type(self, type_name):
233 Finds the first node of a type (or descendent type).
235 service_template = self.service_template
237 if service_template is not None:
238 node_types = service_template.node_types
239 if node_types is not None:
240 for node in self.nodes.itervalues():
241 if node_types.is_descendant(type_name, node.type.name):
246 def get_policy_by_type(self, type_name):
248 Finds the first policy of a type (or descendent type).
250 service_template = self.service_template
252 if service_template is not None:
253 policy_types = service_template.policy_types
254 if policy_types is not None:
255 for policy in self.policies.itervalues():
256 if policy_types.is_descendant(type_name, policy.type.name):
263 return collections.OrderedDict((
264 ('description', self.description),
265 ('metadata', formatting.as_raw_dict(self.meta_data)),
266 ('nodes', formatting.as_raw_list(self.nodes)),
267 ('groups', formatting.as_raw_list(self.groups)),
268 ('policies', formatting.as_raw_list(self.policies)),
269 ('substitution', formatting.as_raw(self.substitution)),
270 ('inputs', formatting.as_raw_dict(self.inputs)),
271 ('outputs', formatting.as_raw_dict(self.outputs)),
272 ('workflows', formatting.as_raw_list(self.workflows))))
275 class NodeBase(InstanceModelMixin):
277 Typed vertex in the service topology.
279 Nodes may have zero or more :class:`Relationship` instances to other nodes, together forming
280 a many-to-many node graph.
282 Usually an instance of a :class:`NodeTemplate`.
285 __tablename__ = 'node'
287 __private_fields__ = ('type_fk',
293 CREATING = 'creating'
295 CONFIGURING = 'configuring'
296 CONFIGURED = 'configured'
297 STARTING = 'starting'
299 STOPPING = 'stopping'
300 DELETING = 'deleting'
304 # Note: 'deleted' isn't actually part of the TOSCA spec, since according the description of the
305 # 'deleting' state: "Node is transitioning from its current state to one where it is deleted and
306 # its state is no longer tracked by the instance model." However, we prefer to be able to
307 # retrieve information about deleted nodes, so we chose to add this 'deleted' state to enable us
310 STATES = (INITIAL, CREATING, CREATED, CONFIGURING, CONFIGURED, STARTING, STARTED, STOPPING,
311 DELETING, DELETED, ERROR)
313 _OP_TO_STATE = {'create': {'transitional': CREATING, 'finished': CREATED},
314 'configure': {'transitional': CONFIGURING, 'finished': CONFIGURED},
315 'start': {'transitional': STARTING, 'finished': STARTED},
316 'stop': {'transitional': STOPPING, 'finished': CONFIGURED},
317 'delete': {'transitional': DELETING, 'finished': DELETED}}
319 # region one_to_one relationships
322 def host(cls): # pylint: disable=method-hidden
324 Node in which we are hosted (can be ``None``).
326 Normally the host node is found by following the relationship graph (relationships with
327 ``host`` roles) to final nodes (with ``host`` roles).
331 return relationship.one_to_one_self(cls, 'host_fk')
335 # region one_to_many relationships
342 :type: [:class:`Task`]
344 return relationship.one_to_many(cls, 'task')
349 Associated interfaces.
351 :type: {:obj:`basestring`: :class:`Interface`}
353 return relationship.one_to_many(cls, 'interface', dict_key='name')
358 Associated immutable parameters.
360 :type: {:obj:`basestring`: :class:`Property`}
362 return relationship.one_to_many(cls, 'property', dict_key='name')
367 Associated mutable parameters.
369 :type: {:obj:`basestring`: :class:`Attribute`}
371 return relationship.one_to_many(cls, 'attribute', dict_key='name')
376 Associated artifacts.
378 :type: {:obj:`basestring`: :class:`Artifact`}
380 return relationship.one_to_many(cls, 'artifact', dict_key='name')
383 def capabilities(cls):
385 Associated exposed capabilities.
387 :type: {:obj:`basestring`: :class:`Capability`}
389 return relationship.one_to_many(cls, 'capability', dict_key='name')
392 def outbound_relationships(cls):
394 Relationships to other nodes.
396 :type: [:class:`Relationship`]
398 return relationship.one_to_many(
399 cls, 'relationship', other_fk='source_node_fk', back_populates='source_node',
401 order_by='Relationship.source_position',
402 collection_class=ordering_list('source_position', count_from=0)
407 def inbound_relationships(cls):
409 Relationships from other nodes.
411 :type: [:class:`Relationship`]
413 return relationship.one_to_many(
414 cls, 'relationship', other_fk='target_node_fk', back_populates='target_node',
416 order_by='Relationship.target_position',
417 collection_class=ordering_list('target_position', count_from=0)
423 # region many_to_one relationships
430 :type: :class:`Service`
432 return relationship.many_to_one(cls, 'service')
435 def node_template(cls):
437 Source node template (can be ``None``).
439 :type: :class:`NodeTemplate`
441 return relationship.many_to_one(cls, 'node_template')
450 return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
454 # region association proxies
457 def service_name(cls):
458 return relationship.association_proxy('service', 'name', type=':obj:`basestring`')
461 def node_template_name(cls):
462 return relationship.association_proxy('node_template', 'name', type=':obj:`basestring`')
466 # region foreign_keys
470 """For Node many-to-one to Type"""
471 return relationship.foreign_key('type')
475 """For Node one-to-one to Node"""
476 return relationship.foreign_key('node', nullable=True)
480 """For Service one-to-many to Node"""
481 return relationship.foreign_key('service')
484 def node_template_fk(cls):
485 """For Node many-to-one to NodeTemplate"""
486 return relationship.foreign_key('node_template')
490 description = Column(Text, doc="""
491 Human-readable description.
493 :type: :obj:`basestring`
496 state = Column(Enum(*STATES, name='node_state'), nullable=False, default=INITIAL, doc="""
499 :type: :obj:`basestring`
502 version = Column(Integer, default=1, doc="""
503 Used by :mod:`aria.storage.instrumentation`.
508 __mapper_args__ = {'version_id_col': version} # Enable SQLAlchemy automatic version counting
511 def determine_state(cls, op_name, is_transitional):
513 :returns the state the node should be in as a result of running the operation on this node.
515 E.g. if we are running tosca.interfaces.node.lifecycle.Standard.create, then
516 the resulting state should either 'creating' (if the task just started) or 'created'
519 If the operation is not a standard TOSCA lifecycle operation, then we return None.
522 state_type = 'transitional' if is_transitional else 'finished'
524 return cls._OP_TO_STATE[op_name][state_type]
528 def is_available(self):
529 return self.state not in (self.INITIAL, self.DELETED, self.ERROR)
531 def get_outbound_relationship_by_name(self, name):
532 for the_relationship in self.outbound_relationships:
533 if the_relationship.name == name:
534 return the_relationship
537 def get_inbound_relationship_by_name(self, name):
538 for the_relationship in self.inbound_relationships:
539 if the_relationship.name == name:
540 return the_relationship
544 def host_address(self):
545 if self.host and self.host.attributes:
546 attribute = self.host.attributes.get('ip')
547 if attribute is not None:
548 return attribute.value
553 return collections.OrderedDict((
555 ('type_name', self.type.name),
556 ('properties', formatting.as_raw_dict(self.properties)),
557 ('attributes', formatting.as_raw_dict(self.properties)),
558 ('interfaces', formatting.as_raw_list(self.interfaces)),
559 ('artifacts', formatting.as_raw_list(self.artifacts)),
560 ('capabilities', formatting.as_raw_list(self.capabilities)),
561 ('relationships', formatting.as_raw_list(self.outbound_relationships))))
564 class GroupBase(InstanceModelMixin):
566 Typed logical container for zero or more :class:`Node` instances.
568 Usually an instance of a :class:`GroupTemplate`.
571 __tablename__ = 'group'
573 __private_fields__ = ('type_fk',
577 # region one_to_many relationships
582 Associated immutable parameters.
584 :type: {:obj:`basestring`: :class:`Property`}
586 return relationship.one_to_many(cls, 'property', dict_key='name')
591 Associated interfaces.
593 :type: {:obj:`basestring`: :class:`Interface`}
595 return relationship.one_to_many(cls, 'interface', dict_key='name')
599 # region many_to_one relationships
606 :type: :class:`Service`
608 return relationship.many_to_one(cls, 'service')
611 def group_template(cls):
613 Source group template (can be ``None``).
615 :type: :class:`GroupTemplate`
617 return relationship.many_to_one(cls, 'group_template')
626 return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
630 # region many_to_many relationships
637 :type: [:class:`Node`]
639 return relationship.many_to_many(cls, 'node')
643 # region foreign_keys
647 """For Group many-to-one to Type"""
648 return relationship.foreign_key('type')
652 """For Service one-to-many to Group"""
653 return relationship.foreign_key('service')
656 def group_template_fk(cls):
657 """For Group many-to-one to GroupTemplate"""
658 return relationship.foreign_key('group_template', nullable=True)
662 description = Column(Text, doc="""
663 Human-readable description.
665 :type: :obj:`basestring`
670 return collections.OrderedDict((
672 ('properties', formatting.as_raw_dict(self.properties)),
673 ('interfaces', formatting.as_raw_list(self.interfaces))))
676 class PolicyBase(InstanceModelMixin):
678 Typed set of orchestration hints applied to zero or more :class:`Node` or :class:`Group`
681 Usually an instance of a :class:`PolicyTemplate`.
684 __tablename__ = 'policy'
686 __private_fields__ = ('type_fk',
688 'policy_template_fk')
690 # region one_to_many relationships
695 Associated immutable parameters.
697 :type: {:obj:`basestring`: :class:`Property`}
699 return relationship.one_to_many(cls, 'property', dict_key='name')
703 # region many_to_one relationships
710 :type: :class:`Service`
712 return relationship.many_to_one(cls, 'service')
715 def policy_template(cls):
717 Source policy template (can be ``None``).
719 :type: :class:`PolicyTemplate`
721 return relationship.many_to_one(cls, 'policy_template')
730 return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
734 # region many_to_many relationships
739 Policy is enacted on these nodes.
741 :type: {:obj:`basestring`: :class:`Node`}
743 return relationship.many_to_many(cls, 'node')
748 Policy is enacted on nodes in these groups.
750 :type: {:obj:`basestring`: :class:`Group`}
752 return relationship.many_to_many(cls, 'group')
756 # region foreign_keys
760 """For Policy many-to-one to Type"""
761 return relationship.foreign_key('type')
765 """For Service one-to-many to Policy"""
766 return relationship.foreign_key('service')
769 def policy_template_fk(cls):
770 """For Policy many-to-one to PolicyTemplate"""
771 return relationship.foreign_key('policy_template', nullable=True)
775 description = Column(Text, doc="""
776 Human-readable description.
778 :type: :obj:`basestring`
783 return collections.OrderedDict((
785 ('type_name', self.type.name),
786 ('properties', formatting.as_raw_dict(self.properties))))
789 class SubstitutionBase(InstanceModelMixin):
791 Exposes the entire service as a single node.
793 Usually an instance of a :class:`SubstitutionTemplate`.
796 __tablename__ = 'substitution'
798 __private_fields__ = ('node_type_fk',
799 'substitution_template_fk')
801 # region one_to_many relationships
806 Map requirement and capabilities to exposed node.
808 :type: {:obj:`basestring`: :class:`SubstitutionMapping`}
810 return relationship.one_to_many(cls, 'substitution_mapping', dict_key='name')
814 # region many_to_one relationships
821 :type: :class:`Service`
823 return relationship.one_to_one(cls, 'service', back_populates=relationship.NO_BACK_POP)
826 def substitution_template(cls):
828 Source substitution template (can be ``None``).
830 :type: :class:`SubstitutionTemplate`
832 return relationship.many_to_one(cls, 'substitution_template')
841 return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
845 # region foreign_keys
848 def node_type_fk(cls):
849 """For Substitution many-to-one to Type"""
850 return relationship.foreign_key('type')
853 def substitution_template_fk(cls):
854 """For Substitution many-to-one to SubstitutionTemplate"""
855 return relationship.foreign_key('substitution_template', nullable=True)
861 return collections.OrderedDict((
862 ('node_type_name', self.node_type.name),
863 ('mappings', formatting.as_raw_dict(self.mappings))))
866 class SubstitutionMappingBase(InstanceModelMixin):
868 Used by :class:`Substitution` to map a capability or a requirement to the exposed node.
870 The :attr:`name` field should match the capability or requirement template name on the exposed
873 Only one of :attr:`capability` and :attr:`requirement_template` can be set. If the latter is
874 set, then :attr:`node` must also be set.
876 Usually an instance of a :class:`SubstitutionMappingTemplate`.
879 __tablename__ = 'substitution_mapping'
881 __private_fields__ = ('substitution_fk',
884 'requirement_template_fk')
886 # region one_to_one relationships
891 Capability to expose (can be ``None``).
893 :type: :class:`Capability`
895 return relationship.one_to_one(cls, 'capability', back_populates=relationship.NO_BACK_POP)
898 def requirement_template(cls):
900 Requirement template to expose (can be ``None``).
902 :type: :class:`RequirementTemplate`
904 return relationship.one_to_one(cls, 'requirement_template',
905 back_populates=relationship.NO_BACK_POP)
910 Node for which to expose :attr:`requirement_template` (can be ``None``).
914 return relationship.one_to_one(cls, 'node', back_populates=relationship.NO_BACK_POP)
918 # region many_to_one relationships
921 def substitution(cls):
923 Containing substitution.
925 :type: :class:`Substitution`
927 return relationship.many_to_one(cls, 'substitution', back_populates='mappings')
931 # region foreign keys
934 def substitution_fk(cls):
935 """For Substitution one-to-many to SubstitutionMapping"""
936 return relationship.foreign_key('substitution')
939 def capability_fk(cls):
940 """For Substitution one-to-one to Capability"""
941 return relationship.foreign_key('capability', nullable=True)
945 """For Substitution one-to-one to Node"""
946 return relationship.foreign_key('node', nullable=True)
949 def requirement_template_fk(cls):
950 """For Substitution one-to-one to RequirementTemplate"""
951 return relationship.foreign_key('requirement_template', nullable=True)
957 return collections.OrderedDict((
958 ('name', self.name),))
961 class RelationshipBase(InstanceModelMixin):
963 Optionally-typed edge in the service topology, connecting a :class:`Node` to a
964 :class:`Capability` of another node.
966 Might be an instance of :class:`RelationshipTemplate` and/or :class:`RequirementTemplate`.
969 __tablename__ = 'relationship'
971 __private_fields__ = ('type_fk',
974 'target_capability_fk',
975 'requirement_template_fk',
976 'relationship_template_fk',
980 # region one_to_one relationships
983 def target_capability(cls):
987 :type: :class:`Capability`
989 return relationship.one_to_one(cls, 'capability', back_populates=relationship.NO_BACK_POP)
993 # region one_to_many relationships
1000 :type: [:class:`Task`]
1002 return relationship.one_to_many(cls, 'task')
1005 def interfaces(cls):
1007 Associated interfaces.
1009 :type: {:obj:`basestring`: :class:`Interface`}
1011 return relationship.one_to_many(cls, 'interface', dict_key='name')
1014 def properties(cls):
1016 Associated immutable parameters.
1018 :type: {:obj:`basestring`: :class:`Property`}
1020 return relationship.one_to_many(cls, 'property', dict_key='name')
1024 # region many_to_one relationships
1027 def source_node(cls):
1031 :type: :class:`Node`
1033 return relationship.many_to_one(
1034 cls, 'node', fk='source_node_fk', back_populates='outbound_relationships')
1037 def target_node(cls):
1041 :type: :class:`Node`
1043 return relationship.many_to_one(
1044 cls, 'node', fk='target_node_fk', back_populates='inbound_relationships')
1047 def relationship_template(cls):
1049 Source relationship template (can be ``None``).
1051 :type: :class:`RelationshipTemplate`
1053 return relationship.many_to_one(cls, 'relationship_template')
1056 def requirement_template(cls):
1058 Source requirement template (can be ``None``).
1060 :type: :class:`RequirementTemplate`
1062 return relationship.many_to_one(cls, 'requirement_template')
1069 :type: :class:`Type`
1071 return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
1075 # region association proxies
1078 def source_node_name(cls):
1079 return relationship.association_proxy('source_node', 'name')
1082 def target_node_name(cls):
1083 return relationship.association_proxy('target_node', 'name')
1087 # region foreign keys
1091 """For Relationship many-to-one to Type"""
1092 return relationship.foreign_key('type', nullable=True)
1095 def source_node_fk(cls):
1096 """For Node one-to-many to Relationship"""
1097 return relationship.foreign_key('node')
1100 def target_node_fk(cls):
1101 """For Node one-to-many to Relationship"""
1102 return relationship.foreign_key('node')
1105 def target_capability_fk(cls):
1106 """For Relationship one-to-one to Capability"""
1107 return relationship.foreign_key('capability', nullable=True)
1110 def requirement_template_fk(cls):
1111 """For Relationship many-to-one to RequirementTemplate"""
1112 return relationship.foreign_key('requirement_template', nullable=True)
1115 def relationship_template_fk(cls):
1116 """For Relationship many-to-one to RelationshipTemplate"""
1117 return relationship.foreign_key('relationship_template', nullable=True)
1121 source_position = Column(Integer, doc="""
1127 target_position = Column(Integer, doc="""
1135 return collections.OrderedDict((
1136 ('name', self.name),
1137 ('target_node_id', self.target_node.name),
1138 ('type_name', self.type.name
1139 if self.type is not None else None),
1140 ('template_name', self.relationship_template.name
1141 if self.relationship_template is not None else None),
1142 ('properties', formatting.as_raw_dict(self.properties)),
1143 ('interfaces', formatting.as_raw_list(self.interfaces))))
1146 class CapabilityBase(InstanceModelMixin):
1148 Typed attachment serving two purposes: to provide extra properties and attributes to a
1149 :class:`Node`, and to expose targets for :class:`Relationship` instances from other nodes.
1151 Usually an instance of a :class:`CapabilityTemplate`.
1154 __tablename__ = 'capability'
1156 __private_fields__ = ('capability_fk',
1158 'capability_template_fk')
1160 # region one_to_many relationships
1163 def properties(cls):
1165 Associated immutable parameters.
1167 :type: {:obj:`basestring`: :class:`Property`}
1169 return relationship.one_to_many(cls, 'property', dict_key='name')
1173 # region many_to_one relationships
1180 :type: :class:`Node`
1182 return relationship.many_to_one(cls, 'node')
1185 def capability_template(cls):
1187 Source capability template (can be ``None``).
1189 :type: :class:`CapabilityTemplate`
1191 return relationship.many_to_one(cls, 'capability_template')
1198 :type: :class:`Type`
1200 return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
1204 # region foreign_keys
1208 """For Capability many-to-one to Type"""
1209 return relationship.foreign_key('type')
1213 """For Node one-to-many to Capability"""
1214 return relationship.foreign_key('node')
1217 def capability_template_fk(cls):
1218 """For Capability many-to-one to CapabilityTemplate"""
1219 return relationship.foreign_key('capability_template', nullable=True)
1223 min_occurrences = Column(Integer, default=None, doc="""
1224 Minimum number of requirement matches required.
1229 max_occurrences = Column(Integer, default=None, doc="""
1230 Maximum number of requirement matches allowed.
1235 occurrences = Column(Integer, default=0, doc="""
1236 Number of requirement matches.
1242 def has_enough_relationships(self):
1243 if self.min_occurrences is not None:
1244 return self.occurrences >= self.min_occurrences
1248 if self.max_occurrences is not None:
1249 if self.occurrences == self.max_occurrences:
1251 self.occurrences += 1
1256 return collections.OrderedDict((
1257 ('name', self.name),
1258 ('type_name', self.type.name),
1259 ('properties', formatting.as_raw_dict(self.properties))))
1262 class InterfaceBase(InstanceModelMixin):
1264 Typed bundle of :class:`Operation` instances.
1266 Can be associated with a :class:`Node`, a :class:`Group`, or a :class:`Relationship`.
1268 Usually an instance of a :class:`InterfaceTemplate`.
1271 __tablename__ = 'interface'
1273 __private_fields__ = ('type_fk',
1277 'interface_template_fk')
1279 # region one_to_many relationships
1284 Parameters for all operations of the interface.
1286 :type: {:obj:`basestring`: :class:`Input`}
1288 return relationship.one_to_many(cls, 'input', dict_key='name')
1291 def operations(cls):
1293 Associated operations.
1295 :type: {:obj:`basestring`: :class:`Operation`}
1297 return relationship.one_to_many(cls, 'operation', dict_key='name')
1301 # region many_to_one relationships
1306 Containing node (can be ``None``).
1308 :type: :class:`Node`
1310 return relationship.many_to_one(cls, 'node')
1315 Containing group (can be ``None``).
1317 :type: :class:`Group`
1319 return relationship.many_to_one(cls, 'group')
1322 def relationship(cls):
1324 Containing relationship (can be ``None``).
1326 :type: :class:`Relationship`
1328 return relationship.many_to_one(cls, 'relationship')
1331 def interface_template(cls):
1333 Source interface template (can be ``None``).
1335 :type: :class:`InterfaceTemplate`
1337 return relationship.many_to_one(cls, 'interface_template')
1344 :type: :class:`Type`
1346 return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
1350 # region foreign_keys
1354 """For Interface many-to-one to Type"""
1355 return relationship.foreign_key('type')
1359 """For Node one-to-many to Interface"""
1360 return relationship.foreign_key('node', nullable=True)
1364 """For Group one-to-many to Interface"""
1365 return relationship.foreign_key('group', nullable=True)
1368 def relationship_fk(cls):
1369 """For Relationship one-to-many to Interface"""
1370 return relationship.foreign_key('relationship', nullable=True)
1373 def interface_template_fk(cls):
1374 """For Interface many-to-one to InterfaceTemplate"""
1375 return relationship.foreign_key('interface_template', nullable=True)
1379 description = Column(Text, doc="""
1380 Human-readable description.
1382 :type: :obj:`basestring`
1387 return collections.OrderedDict((
1388 ('name', self.name),
1389 ('description', self.description),
1390 ('type_name', self.type.name),
1391 ('inputs', formatting.as_raw_dict(self.inputs)),
1392 ('operations', formatting.as_raw_list(self.operations))))
1395 class OperationBase(InstanceModelMixin):
1397 Entry points to Python functions called as part of a workflow execution.
1399 The operation signature (its :attr:`name` and its :attr:`inputs`'s names and types) is declared
1400 by the type of the :class:`Interface`, however each operation can provide its own
1401 :attr:`implementation` as well as additional inputs.
1403 The Python :attr:`function` is usually provided by an associated :class:`Plugin`. Its purpose is
1404 to execute the implementation, providing it with both the operation's and interface's inputs.
1405 The :attr:`arguments` of the function should be set according to the specific signature of the
1408 Additionally, :attr:`configuration` parameters can be provided as hints to configure the
1409 function's behavior. For example, they can be used to configure remote execution credentials.
1411 Might be an instance of :class:`OperationTemplate`.
1414 __tablename__ = 'operation'
1416 __private_fields__ = ('service_fk',
1419 'operation_template_fk')
1421 # region one_to_one relationships
1428 :type: :class:`Plugin`
1430 return relationship.one_to_one(cls, 'plugin', back_populates=relationship.NO_BACK_POP)
1434 # region one_to_many relationships
1439 Parameters provided to the :attr:`implementation`.
1441 :type: {:obj:`basestring`: :class:`Input`}
1443 return relationship.one_to_many(cls, 'input', dict_key='name')
1448 Arguments sent to the Python :attr:`function`.
1450 :type: {:obj:`basestring`: :class:`Argument`}
1452 return relationship.one_to_many(cls, 'argument', dict_key='name')
1455 def configurations(cls):
1457 Configuration parameters for the Python :attr:`function`.
1459 :type: {:obj:`basestring`: :class:`Configuration`}
1461 return relationship.one_to_many(cls, 'configuration', dict_key='name')
1465 # region many_to_one relationships
1470 Containing service (can be ``None``). For workflow operations.
1472 :type: :class:`Service`
1474 return relationship.many_to_one(cls, 'service', back_populates='workflows')
1479 Containing interface (can be ``None``).
1481 :type: :class:`Interface`
1483 return relationship.many_to_one(cls, 'interface')
1486 def operation_template(cls):
1488 Source operation template (can be ``None``).
1490 :type: :class:`OperationTemplate`
1492 return relationship.many_to_one(cls, 'operation_template')
1496 # region foreign_keys
1499 def service_fk(cls):
1500 """For Service one-to-many to Operation"""
1501 return relationship.foreign_key('service', nullable=True)
1504 def interface_fk(cls):
1505 """For Interface one-to-many to Operation"""
1506 return relationship.foreign_key('interface', nullable=True)
1510 """For Operation one-to-one to Plugin"""
1511 return relationship.foreign_key('plugin', nullable=True)
1514 def operation_template_fk(cls):
1515 """For Operation many-to-one to OperationTemplate"""
1516 return relationship.foreign_key('operation_template', nullable=True)
1520 description = Column(Text, doc="""
1521 Human-readable description.
1523 :type: :obj:`basestring`
1526 relationship_edge = Column(Boolean, doc="""
1527 When ``True`` specifies that the operation is on the relationship's target edge; ``False`` is
1528 the source edge (only used by operations on relationships)
1533 implementation = Column(Text, doc="""
1534 Implementation (usually the name of an artifact).
1536 :type: :obj:`basestring`
1539 dependencies = Column(modeling_types.StrictList(item_cls=basestring), doc="""
1540 Dependencies (usually names of artifacts).
1542 :type: [:obj:`basestring`]
1545 function = Column(Text, doc="""
1546 Full path to Python function.
1548 :type: :obj:`basestring`
1551 executor = Column(Text, doc="""
1554 :type: :obj:`basestring`
1557 max_attempts = Column(Integer, doc="""
1558 Maximum number of attempts allowed in case of task failure.
1563 retry_interval = Column(Integer, doc="""
1564 Interval between task retry attempts (in seconds).
1571 return collections.OrderedDict((
1572 ('name', self.name),
1573 ('description', self.description),
1574 ('implementation', self.implementation),
1575 ('dependencies', self.dependencies),
1576 ('inputs', formatting.as_raw_dict(self.inputs))))
1579 class ArtifactBase(InstanceModelMixin):
1581 Typed file, either provided in a CSAR or downloaded from a repository.
1583 Usually an instance of :class:`ArtifactTemplate`.
1586 __tablename__ = 'artifact'
1588 __private_fields__ = ('type_fk',
1590 'artifact_template_fk')
1592 # region one_to_many relationships
1595 def properties(cls):
1597 Associated immutable parameters.
1599 :type: {:obj:`basestring`: :class:`Property`}
1601 return relationship.one_to_many(cls, 'property', dict_key='name')
1605 # region many_to_one relationships
1612 :type: :class:`Node`
1614 return relationship.many_to_one(cls, 'node')
1617 def artifact_template(cls):
1619 Source artifact template (can be ``None``).
1621 :type: :class:`ArtifactTemplate`
1623 return relationship.many_to_one(cls, 'artifact_template')
1630 :type: :class:`Type`
1632 return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
1636 # region foreign_keys
1640 """For Artifact many-to-one to Type"""
1641 return relationship.foreign_key('type')
1645 """For Node one-to-many to Artifact"""
1646 return relationship.foreign_key('node')
1649 def artifact_template_fk(cls):
1650 """For Artifact many-to-one to ArtifactTemplate"""
1651 return relationship.foreign_key('artifact_template', nullable=True)
1655 description = Column(Text, doc="""
1656 Human-readable description.
1658 :type: :obj:`basestring`
1661 source_path = Column(Text, doc="""
1662 Source path (in CSAR or repository).
1664 :type: :obj:`basestring`
1667 target_path = Column(Text, doc="""
1668 Path at which to install at destination.
1670 :type: :obj:`basestring`
1673 repository_url = Column(Text, doc="""
1676 :type: :obj:`basestring`
1679 repository_credential = Column(modeling_types.StrictDict(basestring, basestring), doc="""
1680 Credentials for accessing the repository.
1682 :type: {:obj:`basestring`, :obj:`basestring`}
1687 return collections.OrderedDict((
1688 ('name', self.name),
1689 ('description', self.description),
1690 ('type_name', self.type.name),
1691 ('source_path', self.source_path),
1692 ('target_path', self.target_path),
1693 ('repository_url', self.repository_url),
1694 ('repository_credential', formatting.as_agnostic(self.repository_credential)),
1695 ('properties', formatting.as_raw_dict(self.properties))))