vFW and vDNS support added to azure-plugin
[multicloud/azure.git] / azure / aria / aria-extension-cloudify / src / aria / aria / modeling / service_template.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 ARIA modeling service template module
18 """
19
20 # pylint: disable=too-many-lines, no-self-argument, no-member, abstract-method
21
22 from __future__ import absolute_import  # so we can import standard 'types'
23
24 from sqlalchemy import (
25     Column,
26     Text,
27     Integer,
28     Boolean,
29     DateTime,
30     PickleType
31 )
32 from sqlalchemy.ext.declarative import declared_attr
33
34 from ..utils import (collections, formatting)
35 from .mixins import TemplateModelMixin
36 from . import (
37     relationship,
38     types as modeling_types
39 )
40
41
42 class ServiceTemplateBase(TemplateModelMixin):
43     """
44     Template for creating :class:`Service` instances.
45
46     Usually created by various DSL parsers, such as ARIA's TOSCA extension. However, it can also be
47     created programmatically.
48     """
49
50     __tablename__ = 'service_template'
51
52     __private_fields__ = ('substitution_template_fk',
53                           'node_type_fk',
54                           'group_type_fk',
55                           'policy_type_fk',
56                           'relationship_type_fk',
57                           'capability_type_fk',
58                           'interface_type_fk',
59                           'artifact_type_fk')
60
61     # region one_to_one relationships
62
63     @declared_attr
64     def substitution_template(cls):
65         """
66         Exposes an entire service as a single node.
67
68         :type: :class:`SubstitutionTemplate`
69         """
70         return relationship.one_to_one(
71             cls, 'substitution_template', back_populates=relationship.NO_BACK_POP)
72
73     @declared_attr
74     def node_types(cls):
75         """
76         Base for the node type hierarchy,
77
78         :type: :class:`Type`
79         """
80         return relationship.one_to_one(
81             cls, 'type', fk='node_type_fk', back_populates=relationship.NO_BACK_POP)
82
83     @declared_attr
84     def group_types(cls):
85         """
86         Base for the group type hierarchy,
87
88         :type: :class:`Type`
89         """
90         return relationship.one_to_one(
91             cls, 'type', fk='group_type_fk', back_populates=relationship.NO_BACK_POP)
92
93     @declared_attr
94     def policy_types(cls):
95         """
96         Base for the policy type hierarchy,
97
98         :type: :class:`Type`
99         """
100         return relationship.one_to_one(
101             cls, 'type', fk='policy_type_fk', back_populates=relationship.NO_BACK_POP)
102
103     @declared_attr
104     def relationship_types(cls):
105         """
106         Base for the relationship type hierarchy,
107
108         :type: :class:`Type`
109         """
110         return relationship.one_to_one(
111             cls, 'type', fk='relationship_type_fk', back_populates=relationship.NO_BACK_POP)
112
113     @declared_attr
114     def capability_types(cls):
115         """
116         Base for the capability type hierarchy,
117
118         :type: :class:`Type`
119         """
120         return relationship.one_to_one(
121             cls, 'type', fk='capability_type_fk', back_populates=relationship.NO_BACK_POP)
122
123     @declared_attr
124     def interface_types(cls):
125         """
126         Base for the interface type hierarchy,
127
128         :type: :class:`Type`
129         """
130         return relationship.one_to_one(
131             cls, 'type', fk='interface_type_fk', back_populates=relationship.NO_BACK_POP)
132
133     @declared_attr
134     def artifact_types(cls):
135         """
136         Base for the artifact type hierarchy,
137
138         :type: :class:`Type`
139         """
140         return relationship.one_to_one(
141             cls, 'type', fk='artifact_type_fk', back_populates=relationship.NO_BACK_POP)
142
143     # endregion
144
145     # region one_to_many relationships
146
147     @declared_attr
148     def services(cls):
149         """
150         Instantiated services.
151
152         :type: [:class:`Service`]
153         """
154         return relationship.one_to_many(cls, 'service', dict_key='name')
155
156     @declared_attr
157     def node_templates(cls):
158         """
159         Templates for creating nodes.
160
161         :type: {:obj:`basestring`, :class:`NodeTemplate`}
162         """
163         return relationship.one_to_many(cls, 'node_template', dict_key='name')
164
165     @declared_attr
166     def group_templates(cls):
167         """
168         Templates for creating groups.
169
170         :type: {:obj:`basestring`, :class:`GroupTemplate`}
171         """
172         return relationship.one_to_many(cls, 'group_template', dict_key='name')
173
174     @declared_attr
175     def policy_templates(cls):
176         """
177         Templates for creating policies.
178
179         :type: {:obj:`basestring`, :class:`PolicyTemplate`}
180         """
181         return relationship.one_to_many(cls, 'policy_template', dict_key='name')
182
183     @declared_attr
184     def workflow_templates(cls):
185         """
186         Templates for creating workflows.
187
188         :type: {:obj:`basestring`, :class:`OperationTemplate`}
189         """
190         return relationship.one_to_many(cls, 'operation_template', dict_key='name')
191
192     @declared_attr
193     def outputs(cls):
194         """
195         Declarations for output parameters are filled in after service installation.
196
197         :type: {:obj:`basestring`: :class:`Output`}
198         """
199         return relationship.one_to_many(cls, 'output', dict_key='name')
200
201     @declared_attr
202     def inputs(cls):
203         """
204         Declarations for externally provided parameters.
205
206         :type: {:obj:`basestring`: :class:`Input`}
207         """
208         return relationship.one_to_many(cls, 'input', dict_key='name')
209
210     @declared_attr
211     def plugin_specifications(cls):
212         """
213         Required plugins for instantiated services.
214
215         :type: {:obj:`basestring`: :class:`PluginSpecification`}
216         """
217         return relationship.one_to_many(cls, 'plugin_specification', dict_key='name')
218
219     # endregion
220
221     # region many_to_many relationships
222
223     @declared_attr
224     def meta_data(cls):
225         """
226         Associated metadata.
227
228         :type: {:obj:`basestring`: :class:`Metadata`}
229         """
230         # Warning! We cannot use the attr name "metadata" because it's used by SQLAlchemy!
231         return relationship.many_to_many(cls, 'metadata', dict_key='name')
232
233     # endregion
234
235     # region foreign keys
236
237     @declared_attr
238     def substitution_template_fk(cls):
239         """For ServiceTemplate one-to-one to SubstitutionTemplate"""
240         return relationship.foreign_key('substitution_template', nullable=True)
241
242     @declared_attr
243     def node_type_fk(cls):
244         """For ServiceTemplate one-to-one to Type"""
245         return relationship.foreign_key('type', nullable=True)
246
247     @declared_attr
248     def group_type_fk(cls):
249         """For ServiceTemplate one-to-one to Type"""
250         return relationship.foreign_key('type', nullable=True)
251
252     @declared_attr
253     def policy_type_fk(cls):
254         """For ServiceTemplate one-to-one to Type"""
255         return relationship.foreign_key('type', nullable=True)
256
257     @declared_attr
258     def relationship_type_fk(cls):
259         """For ServiceTemplate one-to-one to Type"""
260         return relationship.foreign_key('type', nullable=True)
261
262     @declared_attr
263     def capability_type_fk(cls):
264         """For ServiceTemplate one-to-one to Type"""
265         return relationship.foreign_key('type', nullable=True)
266
267     @declared_attr
268     def interface_type_fk(cls):
269         """For ServiceTemplate one-to-one to Type"""
270         return relationship.foreign_key('type', nullable=True)
271
272     @declared_attr
273     def artifact_type_fk(cls):
274         """For ServiceTemplate one-to-one to Type"""
275         return relationship.foreign_key('type', nullable=True)
276
277     # endregion
278
279     description = Column(Text, doc="""
280     Human-readable description.
281
282     :type: :obj:`basestring`
283     """)
284
285     main_file_name = Column(Text, doc="""
286     Filename of CSAR or YAML file from which this service template was parsed.
287     
288     :type: :obj:`basestring`
289     """)
290
291     created_at = Column(DateTime, nullable=False, index=True, doc="""
292     Creation timestamp.
293
294     :type: :class:`~datetime.datetime`
295     """)
296
297     updated_at = Column(DateTime, doc="""
298     Update timestamp.
299
300     :type: :class:`~datetime.datetime`
301     """)
302
303     @property
304     def as_raw(self):
305         return collections.OrderedDict((
306             ('description', self.description),
307             ('metadata', formatting.as_raw_dict(self.meta_data)),
308             ('node_templates', formatting.as_raw_list(self.node_templates)),
309             ('group_templates', formatting.as_raw_list(self.group_templates)),
310             ('policy_templates', formatting.as_raw_list(self.policy_templates)),
311             ('substitution_template', formatting.as_raw(self.substitution_template)),
312             ('inputs', formatting.as_raw_dict(self.inputs)),
313             ('outputs', formatting.as_raw_dict(self.outputs)),
314             ('workflow_templates', formatting.as_raw_list(self.workflow_templates))))
315
316     @property
317     def types_as_raw(self):
318         return collections.OrderedDict((
319             ('node_types', formatting.as_raw(self.node_types)),
320             ('group_types', formatting.as_raw(self.group_types)),
321             ('policy_types', formatting.as_raw(self.policy_types)),
322             ('relationship_types', formatting.as_raw(self.relationship_types)),
323             ('capability_types', formatting.as_raw(self.capability_types)),
324             ('interface_types', formatting.as_raw(self.interface_types)),
325             ('artifact_types', formatting.as_raw(self.artifact_types))))
326
327
328 class NodeTemplateBase(TemplateModelMixin):
329     """
330     Template for creating zero or more :class:`Node` instances, which are typed vertices in the
331     service topology.
332     """
333
334     __tablename__ = 'node_template'
335
336     __private_fields__ = ('type_fk',
337                           'service_template_fk')
338
339     # region one_to_many relationships
340
341     @declared_attr
342     def nodes(cls):
343         """
344         Instantiated nodes.
345
346         :type: [:class:`Node`]
347         """
348         return relationship.one_to_many(cls, 'node')
349
350     @declared_attr
351     def interface_templates(cls):
352         """
353         Associated interface templates.
354
355         :type: {:obj:`basestring`: :class:`InterfaceTemplate`}
356         """
357         return relationship.one_to_many(cls, 'interface_template', dict_key='name')
358
359     @declared_attr
360     def artifact_templates(cls):
361         """
362         Associated artifacts.
363
364         :type: {:obj:`basestring`: :class:`ArtifactTemplate`}
365         """
366         return relationship.one_to_many(cls, 'artifact_template', dict_key='name')
367
368     @declared_attr
369     def capability_templates(cls):
370         """
371         Associated exposed capability templates.
372
373         :type: {:obj:`basestring`: :class:`CapabilityTemplate`}
374         """
375         return relationship.one_to_many(cls, 'capability_template', dict_key='name')
376
377     @declared_attr
378     def requirement_templates(cls):
379         """
380         Associated potential relationships with other nodes.
381
382         :type: [:class:`RequirementTemplate`]
383         """
384         return relationship.one_to_many(cls, 'requirement_template', other_fk='node_template_fk')
385
386     @declared_attr
387     def properties(cls):
388         """
389         Declarations for associated immutable parameters.
390
391         :type: {:obj:`basestring`: :class:`Property`}
392         """
393         return relationship.one_to_many(cls, 'property', dict_key='name')
394
395     @declared_attr
396     def attributes(cls):
397         """
398         Declarations for associated mutable parameters.
399
400         :type: {:obj:`basestring`: :class:`Attribute`}
401         """
402         return relationship.one_to_many(cls, 'attribute', dict_key='name')
403
404     # endregion
405
406     # region many_to_one relationships
407
408     @declared_attr
409     def type(cls):
410         """
411         Node type.
412
413         :type: :class:`Type`
414         """
415         return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
416
417     @declared_attr
418     def service_template(cls):
419         """
420         Containing service template.
421
422         :type: :class:`ServiceTemplate`
423         """
424         return relationship.many_to_one(cls, 'service_template')
425
426     # endregion
427
428     # region association proxies
429
430     @declared_attr
431     def service_template_name(cls):
432         return relationship.association_proxy('service_template', 'name')
433
434     @declared_attr
435     def type_name(cls):
436         return relationship.association_proxy('type', 'name')
437
438     # endregion
439
440     # region foreign_keys
441
442     @declared_attr
443     def type_fk(cls):
444         """For NodeTemplate many-to-one to Type"""
445         return relationship.foreign_key('type')
446
447     @declared_attr
448     def service_template_fk(cls):
449         """For ServiceTemplate one-to-many to NodeTemplate"""
450         return relationship.foreign_key('service_template')
451
452     # endregion
453
454     description = Column(Text, doc="""
455     Human-readable description.
456
457     :type: :obj:`basestring`
458     """)
459
460     directives = Column(PickleType, doc="""
461     Directives that apply to this node template.
462
463     :type: [:obj:`basestring`]
464     """)
465
466     default_instances = Column(Integer, default=1, doc="""
467     Default number nodes that will appear in the service.
468
469     :type: :obj:`int`
470     """)
471
472     min_instances = Column(Integer, default=0, doc="""
473     Minimum number nodes that will appear in the service.
474
475     :type: :obj:`int`
476     """)
477
478     max_instances = Column(Integer, default=None, doc="""
479     Maximum number nodes that will appear in the service.
480
481     :type: :obj:`int`
482     """)
483
484     target_node_template_constraints = Column(PickleType, doc="""
485     Constraints for filtering relationship targets.
486
487     :type: [:class:`NodeTemplateConstraint`]
488     """)
489
490     @property
491     def as_raw(self):
492         return collections.OrderedDict((
493             ('name', self.name),
494             ('description', self.description),
495             ('type_name', self.type.name),
496             ('properties', formatting.as_raw_dict(self.properties)),
497             ('attributes', formatting.as_raw_dict(self.properties)),
498             ('interface_templates', formatting.as_raw_list(self.interface_templates)),
499             ('artifact_templates', formatting.as_raw_list(self.artifact_templates)),
500             ('capability_templates', formatting.as_raw_list(self.capability_templates)),
501             ('requirement_templates', formatting.as_raw_list(self.requirement_templates))))
502
503     def is_target_node_template_valid(self, target_node_template):
504         """
505         Checks if ``target_node_template`` matches all our ``target_node_template_constraints``.
506         """
507
508         if self.target_node_template_constraints:
509             for node_template_constraint in self.target_node_template_constraints:
510                 if not node_template_constraint.matches(self, target_node_template):
511                     return False
512         return True
513
514     @property
515     def _next_index(self):
516         """
517         Next available node index.
518
519         :returns: node index
520         :rtype: int
521         """
522
523         max_index = 0
524         if self.nodes:
525             max_index = max(int(n.name.rsplit('_', 1)[-1]) for n in self.nodes)
526         return max_index + 1
527
528     @property
529     def _next_name(self):
530         """
531         Next available node name.
532
533         :returns: node name
534         :rtype: basestring
535         """
536
537         return '{name}_{index}'.format(name=self.name, index=self._next_index)
538
539     @property
540     def scaling(self):
541         scaling = {}
542
543         def extract_property(properties, name):
544             if name in scaling:
545                 return
546             prop = properties.get(name)
547             if (prop is not None) and (prop.type_name == 'integer') and (prop.value is not None):
548                 scaling[name] = prop.value
549
550         def extract_properties(properties):
551             extract_property(properties, 'min_instances')
552             extract_property(properties, 'max_instances')
553             extract_property(properties, 'default_instances')
554
555         # From our scaling capabilities
556         for capability_template in self.capability_templates.itervalues():
557             if capability_template.type.role == 'scaling':
558                 extract_properties(capability_template.properties)
559
560         # From service scaling policies
561         for policy_template in self.service_template.policy_templates.itervalues():
562             if policy_template.type.role == 'scaling':
563                 if policy_template.is_for_node_template(self.name):
564                     extract_properties(policy_template.properties)
565
566         # Defaults
567         scaling.setdefault('min_instances', 0)
568         scaling.setdefault('max_instances', 1)
569         scaling.setdefault('default_instances', 1)
570
571         return scaling
572
573
574 class GroupTemplateBase(TemplateModelMixin):
575     """
576     Template for creating a :class:`Group` instance, which is a typed logical container for zero or
577     more :class:`Node` instances.
578     """
579
580     __tablename__ = 'group_template'
581
582     __private_fields__ = ('type_fk',
583                           'service_template_fk')
584
585     # region one_to_many relationships
586
587     @declared_attr
588     def groups(cls):
589         """
590         Instantiated groups.
591
592         :type: [:class:`Group`]
593         """
594         return relationship.one_to_many(cls, 'group')
595
596     @declared_attr
597     def interface_templates(cls):
598         """
599         Associated interface templates.
600
601         :type: {:obj:`basestring`: :class:`InterfaceTemplate`}
602         """
603         return relationship.one_to_many(cls, 'interface_template', dict_key='name')
604
605     @declared_attr
606     def properties(cls):
607         """
608         Declarations for associated immutable parameters.
609
610         :type: {:obj:`basestring`: :class:`Property`}
611         """
612         return relationship.one_to_many(cls, 'property', dict_key='name')
613
614     # endregion
615
616     # region many_to_one relationships
617
618     @declared_attr
619     def service_template(cls):
620         """
621         Containing service template.
622
623         :type: :class:`ServiceTemplate`
624         """
625         return relationship.many_to_one(cls, 'service_template')
626
627     @declared_attr
628     def type(cls):
629         """
630         Group type.
631
632         :type: :class:`Type`
633         """
634         return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
635
636     # endregion
637
638     # region many_to_many relationships
639
640     @declared_attr
641     def node_templates(cls):
642         """
643         Nodes instantiated by these templates will be members of the group.
644
645         :type: [:class:`NodeTemplate`]
646         """
647         return relationship.many_to_many(cls, 'node_template')
648
649     # endregion
650
651     # region foreign keys
652
653     @declared_attr
654     def type_fk(cls):
655         """For GroupTemplate many-to-one to Type"""
656         return relationship.foreign_key('type')
657
658     @declared_attr
659     def service_template_fk(cls):
660         """For ServiceTemplate one-to-many to GroupTemplate"""
661         return relationship.foreign_key('service_template')
662
663     # endregion
664
665     description = Column(Text, doc="""
666     Human-readable description.
667
668     :type: :obj:`basestring`
669     """)
670
671     @property
672     def as_raw(self):
673         return collections.OrderedDict((
674             ('name', self.name),
675             ('description', self.description),
676             ('type_name', self.type.name),
677             ('properties', formatting.as_raw_dict(self.properties)),
678             ('interface_templates', formatting.as_raw_list(self.interface_templates))))
679
680     def contains_node_template(self, name):
681         for node_template in self.node_templates:
682             if node_template.name == name:
683                 return True
684         return False
685
686
687 class PolicyTemplateBase(TemplateModelMixin):
688     """
689     Template for creating a :class:`Policy` instance, which is a typed set of orchestration hints
690     applied to zero or more :class:`Node` or :class:`Group` instances.
691     """
692
693     __tablename__ = 'policy_template'
694
695     __private_fields__ = ('type_fk',
696                           'service_template_fk')
697
698     # region one_to_many relationships
699
700     @declared_attr
701     def policies(cls):
702         """
703         Instantiated policies.
704
705         :type: [:class:`Policy`]
706         """
707         return relationship.one_to_many(cls, 'policy')
708
709     @declared_attr
710     def properties(cls):
711         """
712         Declarations for associated immutable parameters.
713
714         :type: {:obj:`basestring`: :class:`Property`}
715         """
716         return relationship.one_to_many(cls, 'property', dict_key='name')
717
718     # endregion
719
720     # region many_to_one relationships
721
722     @declared_attr
723     def service_template(cls):
724         """
725         Containing service template.
726
727         :type: :class:`ServiceTemplate`
728         """
729         return relationship.many_to_one(cls, 'service_template')
730
731     @declared_attr
732     def type(cls):
733         """
734         Policy type.
735
736         :type: :class:`Type`
737         """
738         return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
739
740     # endregion
741
742     # region many_to_many relationships
743
744     @declared_attr
745     def node_templates(cls):
746         """
747         Policy will be enacted on all nodes instantiated by these templates.
748
749         :type: {:obj:`basestring`: :class:`NodeTemplate`}
750         """
751         return relationship.many_to_many(cls, 'node_template')
752
753     @declared_attr
754     def group_templates(cls):
755         """
756         Policy will be enacted on all nodes in all groups instantiated by these templates.
757
758         :type: {:obj:`basestring`: :class:`GroupTemplate`}
759         """
760         return relationship.many_to_many(cls, 'group_template')
761
762     # endregion
763
764     # region foreign keys
765
766     @declared_attr
767     def type_fk(cls):
768         """For PolicyTemplate many-to-one to Type"""
769         return relationship.foreign_key('type')
770
771     @declared_attr
772     def service_template_fk(cls):
773         """For ServiceTemplate one-to-many to PolicyTemplate"""
774         return relationship.foreign_key('service_template')
775
776     # endregion
777
778     description = Column(Text, doc="""
779     Human-readable description.
780
781     :type: :obj:`basestring`
782     """)
783
784     @property
785     def as_raw(self):
786         return collections.OrderedDict((
787             ('name', self.name),
788             ('description', self.description),
789             ('type_name', self.type.name),
790             ('properties', formatting.as_raw_dict(self.properties))))
791
792     def is_for_node_template(self, name):
793         for node_template in self.node_templates:
794             if node_template.name == name:
795                 return True
796         for group_template in self.group_templates:
797             if group_template.contains_node_template(name):
798                 return True
799         return False
800
801     def is_for_group_template(self, name):
802         for group_template in self.group_templates:
803             if group_template.name == name:
804                 return True
805         return False
806
807
808 class SubstitutionTemplateBase(TemplateModelMixin):
809     """
810     Template for creating a :class:`Substitution` instance, which exposes an entire instantiated
811     service as a single node.
812     """
813
814     __tablename__ = 'substitution_template'
815
816     __private_fields__ = ('node_type_fk',)
817
818     # region one_to_many relationships
819
820     @declared_attr
821     def substitutions(cls):
822         """
823         Instantiated substitutions.
824
825         :type: [:class:`Substitution`]
826         """
827         return relationship.one_to_many(cls, 'substitution')
828
829     @declared_attr
830     def mappings(cls):
831         """
832         Map requirement and capabilities to exposed node.
833
834         :type: {:obj:`basestring`: :class:`SubstitutionTemplateMapping`}
835         """
836         return relationship.one_to_many(cls, 'substitution_template_mapping', dict_key='name')
837
838     # endregion
839
840     # region many_to_one relationships
841
842     @declared_attr
843     def node_type(cls):
844         """
845         Exposed node type.
846
847         :type: :class:`Type`
848         """
849         return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
850
851     # endregion
852
853     # region foreign keys
854
855     @declared_attr
856     def node_type_fk(cls):
857         """For SubstitutionTemplate many-to-one to Type"""
858         return relationship.foreign_key('type')
859
860     # endregion
861
862     @property
863     def as_raw(self):
864         return collections.OrderedDict((
865             ('node_type_name', self.node_type.name),
866             ('mappings', formatting.as_raw_dict(self.mappings))))
867
868
869 class SubstitutionTemplateMappingBase(TemplateModelMixin):
870     """
871     Used by :class:`SubstitutionTemplate` to map a capability template or a requirement template to
872     the exposed node.
873
874     The :attr:`name` field should match the capability or requirement name on the exposed node's
875     type.
876
877     Only one of :attr:`capability_template` and :attr:`requirement_template` can be set.
878     """
879
880     __tablename__ = 'substitution_template_mapping'
881
882     __private_fields__ = ('substitution_template_fk',
883                           'capability_template_fk',
884                           'requirement_template_fk')
885
886     # region one_to_one relationships
887
888     @declared_attr
889     def capability_template(cls):
890         """
891         Capability template to expose (can be ``None``).
892
893         :type: :class:`CapabilityTemplate`
894         """
895         return relationship.one_to_one(
896             cls, 'capability_template', back_populates=relationship.NO_BACK_POP)
897
898     @declared_attr
899     def requirement_template(cls):
900         """
901         Requirement template to expose (can be ``None``).
902
903         :type: :class:`RequirementTemplate`
904         """
905         return relationship.one_to_one(
906             cls, 'requirement_template', back_populates=relationship.NO_BACK_POP)
907
908     # endregion
909
910     # region many_to_one relationships
911
912     @declared_attr
913     def substitution_template(cls):
914         """
915         Containing substitution template.
916
917         :type: :class:`SubstitutionTemplate`
918         """
919         return relationship.many_to_one(cls, 'substitution_template', back_populates='mappings')
920
921     # endregion
922
923     # region foreign keys
924
925     @declared_attr
926     def substitution_template_fk(cls):
927         """For SubstitutionTemplate one-to-many to SubstitutionTemplateMapping"""
928         return relationship.foreign_key('substitution_template')
929
930     @declared_attr
931     def capability_template_fk(cls):
932         """For SubstitutionTemplate one-to-one to CapabilityTemplate"""
933         return relationship.foreign_key('capability_template', nullable=True)
934
935     @declared_attr
936     def requirement_template_fk(cls):
937         """For SubstitutionTemplate one-to-one to RequirementTemplate"""
938         return relationship.foreign_key('requirement_template', nullable=True)
939
940     # endregion
941
942     @property
943     def as_raw(self):
944         return collections.OrderedDict((
945             ('name', self.name),))
946
947
948 class RequirementTemplateBase(TemplateModelMixin):
949     """
950     Template for creating :class:`Relationship` instances, which are optionally-typed edges in the
951     service topology, connecting a :class:`Node` to a :class:`Capability` of another node.
952
953     Note that there is no equivalent "Requirement" instance model. Instead, during instantiation a
954     requirement template is matched with a capability and a :class:`Relationship` is instantiated.
955
956     A requirement template *must* target a :class:`CapabilityType` or a capability name. It can
957     optionally target a specific :class:`NodeType` or :class:`NodeTemplate`.
958
959     Requirement templates may optionally contain a :class:`RelationshipTemplate`. If they do not,
960     a :class:`Relationship` will be instantiated with default values.
961     """
962
963     __tablename__ = 'requirement_template'
964
965     __private_fields__ = ('target_capability_type_fk',
966                           'target_node_template_fk',
967                           'target_node_type_fk',
968                           'relationship_template_fk',
969                           'node_template_fk')
970
971     # region one_to_one relationships
972
973     @declared_attr
974     def target_capability_type(cls):
975         """
976         Target capability type.
977
978         :type: :class:`CapabilityType`
979         """
980         return relationship.one_to_one(cls,
981                                        'type',
982                                        fk='target_capability_type_fk',
983                                        back_populates=relationship.NO_BACK_POP)
984
985     @declared_attr
986     def target_node_template(cls):
987         """
988         Target node template (can be ``None``).
989
990         :type: :class:`NodeTemplate`
991         """
992         return relationship.one_to_one(cls,
993                                        'node_template',
994                                        fk='target_node_template_fk',
995                                        back_populates=relationship.NO_BACK_POP)
996
997     @declared_attr
998     def relationship_template(cls):
999         """
1000         Associated relationship template (can be ``None``).
1001
1002         :type: :class:`RelationshipTemplate`
1003         """
1004         return relationship.one_to_one(cls, 'relationship_template')
1005
1006     # endregion
1007
1008     # region one_to_many relationships
1009
1010     @declared_attr
1011     def relationships(cls):
1012         """
1013         Instantiated relationships.
1014
1015         :type: [:class:`Relationship`]
1016         """
1017         return relationship.one_to_many(cls, 'relationship')
1018
1019     # endregion
1020
1021     # region many_to_one relationships
1022
1023     @declared_attr
1024     def node_template(cls):
1025         """
1026         Containing node template.
1027
1028         :type: :class:`NodeTemplate`
1029         """
1030         return relationship.many_to_one(cls, 'node_template', fk='node_template_fk')
1031
1032     @declared_attr
1033     def target_node_type(cls):
1034         """
1035         Target node type (can be ``None``).
1036
1037         :type: :class:`Type`
1038         """
1039         return relationship.many_to_one(
1040             cls, 'type', fk='target_node_type_fk', back_populates=relationship.NO_BACK_POP)
1041
1042     # endregion
1043
1044     # region foreign keys
1045
1046     @declared_attr
1047     def target_node_type_fk(cls):
1048         """For RequirementTemplate many-to-one to Type"""
1049         return relationship.foreign_key('type', nullable=True)
1050
1051     @declared_attr
1052     def target_node_template_fk(cls):
1053         """For RequirementTemplate one-to-one to NodeTemplate"""
1054         return relationship.foreign_key('node_template', nullable=True)
1055
1056     @declared_attr
1057     def target_capability_type_fk(cls):
1058         """For RequirementTemplate one-to-one to Type"""
1059         return relationship.foreign_key('type', nullable=True)
1060
1061     @declared_attr
1062     def node_template_fk(cls):
1063         """For NodeTemplate one-to-many to RequirementTemplate"""
1064         return relationship.foreign_key('node_template')
1065
1066     @declared_attr
1067     def relationship_template_fk(cls):
1068         """For RequirementTemplate one-to-one to RelationshipTemplate"""
1069         return relationship.foreign_key('relationship_template', nullable=True)
1070
1071     # endregion
1072
1073     target_capability_name = Column(Text, doc="""
1074     Target capability name in node template or node type (can be ``None``).
1075
1076     :type: :obj:`basestring`
1077     """)
1078
1079     target_node_template_constraints = Column(PickleType, doc="""
1080     Constraints for filtering relationship targets.
1081
1082     :type: [:class:`NodeTemplateConstraint`]
1083     """)
1084
1085     @property
1086     def as_raw(self):
1087         return collections.OrderedDict((
1088             ('name', self.name),
1089             ('target_node_type_name', self.target_node_type.name
1090              if self.target_node_type is not None else None),
1091             ('target_node_template_name', self.target_node_template.name
1092              if self.target_node_template is not None else None),
1093             ('target_capability_type_name', self.target_capability_type.name
1094              if self.target_capability_type is not None else None),
1095             ('target_capability_name', self.target_capability_name),
1096             ('relationship_template', formatting.as_raw(self.relationship_template))))
1097
1098
1099 class RelationshipTemplateBase(TemplateModelMixin):
1100     """
1101     Optional addition to a :class:`RequirementTemplate`.
1102
1103     Note that a relationship template here is not exactly equivalent to a relationship template
1104     entity in TOSCA. For example, a TOSCA requirement specifying a relationship type rather than a
1105     relationship template would still be represented here as a relationship template.
1106     """
1107
1108     __tablename__ = 'relationship_template'
1109
1110     __private_fields__ = ('type_fk',)
1111
1112     # region one_to_many relationships
1113
1114     @declared_attr
1115     def relationships(cls):
1116         """
1117         Instantiated relationships.
1118
1119         :type: [:class:`Relationship`]
1120         """
1121         return relationship.one_to_many(cls, 'relationship')
1122
1123     @declared_attr
1124     def interface_templates(cls):
1125         """
1126         Associated interface templates.
1127
1128         :type: {:obj:`basestring`: :class:`InterfaceTemplate`}
1129         """
1130         return relationship.one_to_many(cls, 'interface_template', dict_key='name')
1131
1132     @declared_attr
1133     def properties(cls):
1134         """
1135         Declarations for associated immutable parameters.
1136
1137         :type: {:obj:`basestring`: :class:`Property`}
1138         """
1139         return relationship.one_to_many(cls, 'property', dict_key='name')
1140
1141     # endregion
1142
1143     # region many_to_one relationships
1144
1145     @declared_attr
1146     def type(cls):
1147         """
1148         Relationship type.
1149
1150         :type: :class:`Type`
1151         """
1152         return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
1153
1154     # endregion
1155
1156     # region foreign keys
1157
1158     @declared_attr
1159     def type_fk(cls):
1160         """For RelationshipTemplate many-to-one to Type"""
1161         return relationship.foreign_key('type', nullable=True)
1162
1163     # endregion
1164
1165     description = Column(Text, doc="""
1166     Human-readable description.
1167
1168     :type: :obj:`basestring`
1169     """)
1170
1171     @property
1172     def as_raw(self):
1173         return collections.OrderedDict((
1174             ('type_name', self.type.name if self.type is not None else None),
1175             ('name', self.name),
1176             ('description', self.description),
1177             ('properties', formatting.as_raw_dict(self.properties)),
1178             ('interface_templates', formatting.as_raw_list(self.interface_templates))))
1179
1180
1181 class CapabilityTemplateBase(TemplateModelMixin):
1182     """
1183     Template for creating :class:`Capability` instances, typed attachments which serve two purposes:
1184     to provide extra properties and attributes to :class:`Node` instances, and to expose targets for
1185     :class:`Relationship` instances from other nodes.
1186     """
1187
1188     __tablename__ = 'capability_template'
1189
1190     __private_fields__ = ('type_fk',
1191                           'node_template_fk')
1192
1193     # region one_to_many relationships
1194
1195     @declared_attr
1196     def capabilities(cls):
1197         """
1198         Instantiated capabilities.
1199
1200         :type: [:class:`Capability`]
1201         """
1202         return relationship.one_to_many(cls, 'capability')
1203
1204     @declared_attr
1205     def properties(cls):
1206         """
1207         Declarations for associated immutable parameters.
1208
1209         :type: {:obj:`basestring`: :class:`Property`}
1210         """
1211         return relationship.one_to_many(cls, 'property', dict_key='name')
1212
1213     # endregion
1214
1215     # region many_to_one relationships
1216
1217     @declared_attr
1218     def node_template(cls):
1219         """
1220         Containing node template.
1221
1222         :type: :class:`NodeTemplate`
1223         """
1224         return relationship.many_to_one(cls, 'node_template')
1225
1226     @declared_attr
1227     def type(cls):
1228         """
1229         Capability type.
1230
1231         :type: :class:`Type`
1232         """
1233         return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
1234
1235     # endregion
1236
1237     # region many_to_many relationships
1238
1239     @declared_attr
1240     def valid_source_node_types(cls):
1241         """
1242         Reject requirements that are not from these node types.
1243
1244         :type: [:class:`Type`]
1245         """
1246         return relationship.many_to_many(cls, 'type', prefix='valid_sources')
1247
1248     # endregion
1249
1250     # region foreign keys
1251
1252     @declared_attr
1253     def type_fk(cls):
1254         """For CapabilityTemplate many-to-one to Type"""
1255         return relationship.foreign_key('type')
1256
1257     @declared_attr
1258     def node_template_fk(cls):
1259         """For NodeTemplate one-to-many to CapabilityTemplate"""
1260         return relationship.foreign_key('node_template')
1261
1262     # endregion
1263
1264     description = Column(Text, doc="""
1265     Human-readable description.
1266
1267     :type: :obj:`basestring`
1268     """)
1269
1270     min_occurrences = Column(Integer, default=None, doc="""
1271     Minimum number of requirement matches required.
1272
1273     :type: :obj:`int`
1274     """)
1275
1276     max_occurrences = Column(Integer, default=None, doc="""
1277     Maximum number of requirement matches allowed.
1278
1279     :type: :obj:`int`
1280     """)
1281
1282     @property
1283     def as_raw(self):
1284         return collections.OrderedDict((
1285             ('name', self.name),
1286             ('description', self.description),
1287             ('type_name', self.type.name),
1288             ('min_occurrences', self.min_occurrences),
1289             ('max_occurrences', self.max_occurrences),
1290             ('valid_source_node_types', [v.name for v in self.valid_source_node_types]),
1291             ('properties', formatting.as_raw_dict(self.properties))))
1292
1293
1294 class InterfaceTemplateBase(TemplateModelMixin):
1295     """
1296     Template for creating :class:`Interface` instances, which are typed bundles of
1297     :class:`Operation` instances.
1298
1299     Can be associated with a :class:`NodeTemplate`, a :class:`GroupTemplate`, or a
1300     :class:`RelationshipTemplate`.
1301     """
1302
1303     __tablename__ = 'interface_template'
1304
1305     __private_fields__ = ('type_fk',
1306                           'node_template_fk',
1307                           'group_template_fk',
1308                           'relationship_template_fk')
1309
1310     # region one_to_many relationships
1311
1312     @declared_attr
1313     def inputs(cls):
1314         """
1315         Declarations for externally provided parameters that can be used by all operations of the
1316         interface.
1317
1318         :type: {:obj:`basestring`: :class:`Input`}
1319         """
1320         return relationship.one_to_many(cls, 'input', dict_key='name')
1321
1322     @declared_attr
1323     def interfaces(cls):
1324         """
1325         Instantiated interfaces.
1326
1327         :type: [:class:`Interface`]
1328         """
1329         return relationship.one_to_many(cls, 'interface')
1330
1331     @declared_attr
1332     def operation_templates(cls):
1333         """
1334         Associated operation templates.
1335
1336         :type: {:obj:`basestring`: :class:`OperationTemplate`}
1337         """
1338         return relationship.one_to_many(cls, 'operation_template', dict_key='name')
1339
1340     # endregion
1341
1342     # region many_to_one relationships
1343
1344     @declared_attr
1345     def node_template(cls):
1346         """
1347         Containing node template (can be ``None``).
1348
1349         :type: :class:`NodeTemplate`
1350         """
1351         return relationship.many_to_one(cls, 'node_template')
1352
1353     @declared_attr
1354     def group_template(cls):
1355         """
1356         Containing group template (can be ``None``).
1357
1358         :type: :class:`GroupTemplate`
1359         """
1360         return relationship.many_to_one(cls, 'group_template')
1361
1362     @declared_attr
1363     def relationship_template(cls):
1364         """
1365         Containing relationship template (can be ``None``).
1366
1367         :type: :class:`RelationshipTemplate`
1368         """
1369         return relationship.many_to_one(cls, 'relationship_template')
1370
1371     @declared_attr
1372     def type(cls):
1373         """
1374         Interface type.
1375
1376         :type: :class:`Type`
1377         """
1378         return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
1379
1380     # endregion
1381
1382     # region foreign keys
1383
1384     @declared_attr
1385     def type_fk(cls):
1386         """For InterfaceTemplate many-to-one to Type"""
1387         return relationship.foreign_key('type')
1388
1389     @declared_attr
1390     def node_template_fk(cls):
1391         """For NodeTemplate one-to-many to InterfaceTemplate"""
1392         return relationship.foreign_key('node_template', nullable=True)
1393
1394     @declared_attr
1395     def group_template_fk(cls):
1396         """For GroupTemplate one-to-many to InterfaceTemplate"""
1397         return relationship.foreign_key('group_template', nullable=True)
1398
1399     @declared_attr
1400     def relationship_template_fk(cls):
1401         """For RelationshipTemplate one-to-many to InterfaceTemplate"""
1402         return relationship.foreign_key('relationship_template', nullable=True)
1403
1404     # endregion
1405
1406     description = Column(Text, doc="""
1407     Human-readable description.
1408
1409     :type: :obj:`basestring`
1410     """)
1411
1412     @property
1413     def as_raw(self):
1414         return collections.OrderedDict((
1415             ('name', self.name),
1416             ('description', self.description),
1417             ('type_name', self.type.name),
1418             ('inputs', formatting.as_raw_dict(self.inputs)),  # pylint: disable=no-member
1419             # TODO fix self.properties reference
1420             ('operation_templates', formatting.as_raw_list(self.operation_templates))))
1421
1422
1423 class OperationTemplateBase(TemplateModelMixin):
1424     """
1425     Template for creating :class:`Operation` instances, which are entry points to Python functions
1426     called as part of a workflow execution.
1427     """
1428
1429     __tablename__ = 'operation_template'
1430
1431     __private_fields__ = ('service_template_fk',
1432                           'interface_template_fk',
1433                           'plugin_fk')
1434
1435     # region one_to_one relationships
1436
1437     @declared_attr
1438     def plugin_specification(cls):
1439         """
1440         Associated plugin specification.
1441
1442         :type: :class:`PluginSpecification`
1443         """
1444         return relationship.one_to_one(
1445             cls, 'plugin_specification', back_populates=relationship.NO_BACK_POP)
1446
1447     # endregion
1448
1449     # region one_to_many relationships
1450
1451     @declared_attr
1452     def operations(cls):
1453         """
1454         Instantiated operations.
1455
1456         :type: [:class:`Operation`]
1457         """
1458         return relationship.one_to_many(cls, 'operation')
1459
1460     @declared_attr
1461     def inputs(cls):
1462         """
1463         Declarations for parameters provided to the :attr:`implementation`.
1464
1465         :type: {:obj:`basestring`: :class:`Input`}
1466         """
1467         return relationship.one_to_many(cls, 'input', dict_key='name')
1468
1469     @declared_attr
1470     def configurations(cls):
1471         """
1472         Configuration parameters for the operation instance Python :attr:`function`.
1473
1474         :type: {:obj:`basestring`: :class:`Configuration`}
1475         """
1476         return relationship.one_to_many(cls, 'configuration', dict_key='name')
1477
1478     # endregion
1479
1480     # region many_to_one relationships
1481
1482     @declared_attr
1483     def service_template(cls):
1484         """
1485         Containing service template (can be ``None``). For workflow operation templates.
1486
1487         :type: :class:`ServiceTemplate`
1488         """
1489         return relationship.many_to_one(cls, 'service_template',
1490                                         back_populates='workflow_templates')
1491
1492     @declared_attr
1493     def interface_template(cls):
1494         """
1495         Containing interface template (can be ``None``).
1496
1497         :type: :class:`InterfaceTemplate`
1498         """
1499         return relationship.many_to_one(cls, 'interface_template')
1500
1501     # endregion
1502
1503     # region foreign keys
1504
1505     @declared_attr
1506     def service_template_fk(cls):
1507         """For ServiceTemplate one-to-many to OperationTemplate"""
1508         return relationship.foreign_key('service_template', nullable=True)
1509
1510     @declared_attr
1511     def interface_template_fk(cls):
1512         """For InterfaceTemplate one-to-many to OperationTemplate"""
1513         return relationship.foreign_key('interface_template', nullable=True)
1514
1515     @declared_attr
1516     def plugin_specification_fk(cls):
1517         """For OperationTemplate one-to-one to PluginSpecification"""
1518         return relationship.foreign_key('plugin_specification', nullable=True)
1519
1520     # endregion
1521
1522     description = Column(Text, doc="""
1523     Human-readable description.
1524
1525     :type: :obj:`basestring`
1526     """)
1527
1528     relationship_edge = Column(Boolean, doc="""
1529     When ``True`` specifies that the operation is on the relationship's target edge; ``False`` is
1530     the source edge (only used by operations on relationships)
1531
1532     :type: :obj:`bool`
1533     """)
1534
1535     implementation = Column(Text, doc="""
1536     Implementation (usually the name of an artifact).
1537
1538     :type: :obj:`basestring`
1539     """)
1540
1541     dependencies = Column(modeling_types.StrictList(item_cls=basestring), doc="""
1542     Dependencies (usually names of artifacts).
1543
1544     :type: [:obj:`basestring`]
1545     """)
1546
1547     function = Column(Text, doc="""
1548     Full path to Python function.
1549
1550     :type: :obj:`basestring`
1551     """)
1552
1553     executor = Column(Text, doc="""
1554     Name of executor.
1555
1556     :type: :obj:`basestring`
1557     """)
1558
1559     max_attempts = Column(Integer, doc="""
1560     Maximum number of attempts allowed in case of task failure.
1561
1562     :type: :obj:`int`
1563     """)
1564
1565     retry_interval = Column(Integer, doc="""
1566     Interval between task retry attemps (in seconds).
1567
1568     :type: :obj:`float`
1569     """)
1570
1571     @property
1572     def as_raw(self):
1573         return collections.OrderedDict((
1574             ('name', self.name),
1575             ('description', self.description),
1576             ('implementation', self.implementation),
1577             ('dependencies', self.dependencies),
1578             ('inputs', formatting.as_raw_dict(self.inputs))))
1579
1580
1581 class ArtifactTemplateBase(TemplateModelMixin):
1582     """
1583     Template for creating an :class:`Artifact` instance, which is a typed file, either provided in a
1584     CSAR or downloaded from a repository.
1585     """
1586
1587     __tablename__ = 'artifact_template'
1588
1589     __private_fields__ = ('type_fk',
1590                           'node_template_fk')
1591
1592     # region one_to_many relationships
1593
1594     @declared_attr
1595     def artifacts(cls):
1596         """
1597         Instantiated artifacts.
1598
1599         :type: [:class:`Artifact`]
1600         """
1601         return relationship.one_to_many(cls, 'artifact')
1602
1603     @declared_attr
1604     def properties(cls):
1605         """
1606         Declarations for associated immutable parameters.
1607
1608         :type: {:obj:`basestring`: :class:`Property`}
1609         """
1610         return relationship.one_to_many(cls, 'property', dict_key='name')
1611
1612     # endregion
1613
1614     # region many_to_one relationships
1615
1616     @declared_attr
1617     def node_template(cls):
1618         """
1619         Containing node template.
1620
1621         :type: :class:`NodeTemplate`
1622         """
1623         return relationship.many_to_one(cls, 'node_template')
1624
1625     @declared_attr
1626     def type(cls):
1627         """
1628         Artifact type.
1629
1630         :type: :class:`Type`
1631         """
1632         return relationship.many_to_one(cls, 'type', back_populates=relationship.NO_BACK_POP)
1633
1634     # endregion
1635
1636     # region foreign keys
1637
1638     @declared_attr
1639     def type_fk(cls):
1640         """For ArtifactTemplate many-to-one to Type"""
1641         return relationship.foreign_key('type')
1642
1643     @declared_attr
1644     def node_template_fk(cls):
1645         """For NodeTemplate one-to-many to ArtifactTemplate"""
1646         return relationship.foreign_key('node_template')
1647
1648     # endregion
1649
1650     description = Column(Text, doc="""
1651     Human-readable description.
1652
1653     :type: :obj:`basestring`
1654     """)
1655
1656     source_path = Column(Text, doc="""
1657     Source path (in CSAR or repository).
1658
1659     :type: :obj:`basestring`
1660     """)
1661
1662     target_path = Column(Text, doc="""
1663     Path at which to install at destination.
1664
1665     :type: :obj:`basestring`
1666     """)
1667
1668     repository_url = Column(Text, doc="""
1669     Repository URL.
1670
1671     :type: :obj:`basestring`
1672     """)
1673
1674     repository_credential = Column(modeling_types.StrictDict(basestring, basestring), doc="""
1675     Credentials for accessing the repository.
1676
1677     :type: {:obj:`basestring`, :obj:`basestring`}
1678     """)
1679
1680     @property
1681     def as_raw(self):
1682         return collections.OrderedDict((
1683             ('name', self.name),
1684             ('description', self.description),
1685             ('type_name', self.type.name),
1686             ('source_path', self.source_path),
1687             ('target_path', self.target_path),
1688             ('repository_url', self.repository_url),
1689             ('repository_credential', formatting.as_agnostic(self.repository_credential)),
1690             ('properties', formatting.as_raw_dict(self.properties))))
1691
1692
1693 class PluginSpecificationBase(TemplateModelMixin):
1694     """
1695     Requirement for a :class:`Plugin`.
1696
1697     The actual plugin to be selected depends on those currently installed in ARIA.
1698     """
1699
1700     __tablename__ = 'plugin_specification'
1701
1702     __private_fields__ = ('service_template_fk',
1703                           'plugin_fk')
1704
1705     # region many_to_one relationships
1706
1707     @declared_attr
1708     def service_template(cls):
1709         """
1710         Containing service template.
1711
1712         :type: :class:`ServiceTemplate`
1713         """
1714         return relationship.many_to_one(cls, 'service_template')
1715
1716     @declared_attr
1717     def plugin(cls): # pylint: disable=method-hidden
1718         """
1719         Matched plugin.
1720
1721         :type: :class:`Plugin`
1722         """
1723         return relationship.many_to_one(cls, 'plugin', back_populates=relationship.NO_BACK_POP)
1724
1725     # endregion
1726
1727     # region foreign keys
1728
1729     @declared_attr
1730     def service_template_fk(cls):
1731         """For ServiceTemplate one-to-many to PluginSpecification"""
1732         return relationship.foreign_key('service_template', nullable=True)
1733
1734     @declared_attr
1735     def plugin_fk(cls):
1736         """For PluginSpecification many-to-one to Plugin"""
1737         return relationship.foreign_key('plugin', nullable=True)
1738
1739     # endregion
1740
1741     version = Column(Text, doc="""
1742     Minimum plugin version.
1743
1744     :type: :obj:`basestring`
1745     """)
1746
1747     enabled = Column(Boolean, nullable=False, default=True, doc="""
1748     Whether the plugin is enabled.
1749
1750     :type: :obj:`bool`
1751     """)
1752
1753     @property
1754     def as_raw(self):
1755         return collections.OrderedDict((
1756             ('name', self.name),
1757             ('version', self.version),
1758             ('enabled', self.enabled)))