RIFT OSM R1 Initial Submission
[osm/SO.git] / common / python / rift / mano / tosca_translator / rwmano / syntax / mano_resource.py
diff --git a/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_resource.py b/common/python/rift/mano/tosca_translator/rwmano/syntax/mano_resource.py
new file mode 100644 (file)
index 0000000..1606f7f
--- /dev/null
@@ -0,0 +1,374 @@
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# Copyright 2016 RIFT.io Inc
+
+
+import uuid
+
+from collections import OrderedDict
+
+import six
+
+from rift.mano.tosca_translator.common.utils import _
+
+from toscaparser.common.exception import ValidationError
+from toscaparser.elements.interfaces import InterfacesDef
+from toscaparser.functions import GetInput
+
+
+SECTIONS = (TYPE, PROPERTIES, MEDADATA, DEPENDS_ON, UPDATE_POLICY,
+            DELETION_POLICY) = \
+           ('type', 'properties', 'metadata',
+            'depends_on', 'update_policy', 'deletion_policy')
+
+
+class ManoResource(object):
+    '''Base class for TOSCA node type translation to RIFT.io MANO type.'''
+
+    def __init__(self,
+                 log,
+                 nodetemplate,
+                 name=None,
+                 type_=None,
+                 properties=None,
+                 metadata=None,
+                 artifacts=None,
+                 depends_on=None,
+                 update_policy=None,
+                 deletion_policy=None):
+        self.log = log
+        self.nodetemplate = nodetemplate
+        if name:
+            self.name = name
+        else:
+            self.name = nodetemplate.name
+        self.type_ = type_
+        self._id = None
+        self._version = None
+        self.properties = properties or {}
+        self.metadata = metadata
+        self._artifacts = artifacts
+
+        # The difference between depends_on and depends_on_nodes is
+        # that depends_on defines dependency in the context of the
+        # HOT template and it is used during the template output.
+        # Depends_on_nodes defines the direct dependency between the
+        # tosca nodes and is not used during the output of the
+        # HOT template but for internal processing only. When a tosca
+        # node depends on another node it will be always added to
+        # depends_on_nodes but not always to depends_on. For example
+        # if the source of dependency is a server, the dependency will
+        # be added as properties.get_resource and not depends_on
+        if depends_on:
+            self.depends_on = depends_on
+            self.depends_on_nodes = depends_on
+        else:
+            self.depends_on = []
+            self.depends_on_nodes = []
+        self.update_policy = update_policy
+        self.deletion_policy = deletion_policy
+        self.group_dependencies = {}
+        self.operations = {}
+        # if hide_resource is set to true, then this resource will not be
+        # generated in the output yaml.
+        self.hide_resource = False
+        log.debug(_('Translating TOSCA node %(name)s of type %(type)s') %
+                  {'name': self.name,
+                   'type': self.type_})
+
+    # Added the below property menthods to support methods that
+    # works on both toscaparser.NodeType and translator.ManoResource
+    @property
+    def type(self):
+        return self.type_
+
+    @type.setter
+    def type(self, value):
+        self.type_ = value
+
+    def get_type(self):
+        return self.type_
+
+    @property
+    def id(self):
+        if self._id is None:
+            self._id = str(uuid.uuid1())
+        return self._id
+
+    @property
+    def description(self):
+        return _("Translated from TOSCA")
+
+    @property
+    def vendor(self):
+        if self._vendor is None:
+            if self.metadata and 'vendor' in self.metadata:
+                self._vendor = self.metadata['vendor']
+            else:
+                self._vendor = "RIFT.io"
+        return self._vendor
+
+    @property
+    def version(self):
+        if self._version is None:
+            if self.metadata and 'version' in self.metadata:
+                self._version = str(self.metadata['version'])
+            else:
+                self._version = '1.0'
+        return self._version
+
+    @property
+    def artifacts(self):
+        return self._artifacts
+
+    @artifacts.setter
+    def artifacts(self, value):
+        self._artifacts = value
+
+    def __str__(self):
+        return "%s(%s)"%(self.name, self.type)
+
+    def map_tosca_name_to_mano(self, name):
+        new_name = name.replace("_", "-")
+        return new_name
+
+    def map_keys_to_mano(self, d):
+        if isinstance(d, dict):
+            for key in d.keys():
+                d[self.map_tosca_name_to_mano(key)] = \
+                                    self.map_keys_to_mano(d.pop(key))
+            return d
+        elif isinstance(d, list):
+            arr = []
+            for memb in d:
+                arr.append(self.map_keys_to_mano(memb))
+            return arr
+        else:
+            return d
+
+    def validate_properties(self, properties, required=None, optional=None):
+        if not isinstance(properties, dict):
+            err_msg = _("Properties for {0}({1}) is not right type"). \
+                      format(self.name, self.type_)
+            self.log.error(err_msg)
+            raise ValidationError(message=err_msg)
+
+        if required:
+            # Check if the required properties are present
+            if not set(required).issubset(properties.keys()):
+                for key in required:
+                    if key not in properties:
+                        err_msg = _("Property {0} is not defined "
+                                    "for {1}({2})"). \
+                                  format(key, self.name, self.type_)
+                        self.log.error(err_msg)
+                        raise ValidationError(message=err_msg)
+
+            # Check for unknown properties
+            for key in properties.keys():
+                if (key not in required or
+                    key not in optional):
+                    self.log.warn(_("Property {0} not supported for {1}({2}), "
+                                    "will be ignored.").
+                                  format(key, self.name, self.type_))
+
+    def handle_properties(self):
+        pass
+
+    def handle_artifacts(self):
+        pass
+
+    def handle_capabilities(self):
+        pass
+
+    def handle_requirements(self, nodes):
+        pass
+
+    def handle_interfaces(self):
+        pass
+
+    def update_image_checksum(self, in_file):
+        pass
+
+    def generate_yang_model(self, nsd, vnfds, use_gi=False):
+        """Generate yang model for the node"""
+        self.log.debug(_("{0}: Not doing anything for YANG model generation").
+                       format(self))
+
+    def get_supporting_files(self, files, desc_id=None):
+        pass
+
+    def top_of_chain(self):
+        dependent = self.group_dependencies.get(self)
+        if dependent is None:
+            return self
+        else:
+            return dependent.top_of_chain()
+
+    def get_dict_output(self):
+        resource_sections = OrderedDict()
+        resource_sections[TYPE] = self.type
+        if self.properties:
+            resource_sections[PROPERTIES] = self.properties
+        if self.metadata:
+            resource_sections[MEDADATA] = self.metadata
+        if self.depends_on:
+            resource_sections[DEPENDS_ON] = []
+            for depend in self.depends_on:
+                resource_sections[DEPENDS_ON].append(depend.name)
+        if self.update_policy:
+            resource_sections[UPDATE_POLICY] = self.update_policy
+        if self.deletion_policy:
+            resource_sections[DELETION_POLICY] = self.deletion_policy
+
+        return {self.name: resource_sections}
+
+    def get_tosca_props(self):
+        tosca_props = {}
+        for prop in self.nodetemplate.get_properties_objects():
+            if isinstance(prop.value, GetInput):
+                tosca_props[prop.name] = {'get_param': prop.value.input_name}
+            else:
+                tosca_props[prop.name] = prop.value
+        return tosca_props
+
+    def get_tosca_caps(self):
+        tosca_caps = {}
+        for cap in self.nodetemplate.get_capabilities_objects():
+            properties = cap.get_properties()
+            if len(properties):
+                tosca_caps[cap.name] = {}
+                for name in properties:
+                    tosca_caps[cap.name][name] = properties[name].value
+        return tosca_caps
+
+    def get_tosca_reqs(self):
+        tosca_reqs = []
+        for requirement in self.nodetemplate.requirements:
+            for endpoint, details in six.iteritems(requirement):
+                req = {}
+                relation = None
+                interfaces = None
+                if isinstance(details, dict):
+                    target = details.get('node')
+                    relation = details.get('relationship')
+                else:
+                    target = details
+                if (target and relation and
+                    not isinstance(relation, six.string_types)):
+                    interfaces = relation.get('interfaces')
+                req[endpoint] = {'target': target}
+                if relation:
+                    req[endpoint] = {'relation': relation}
+                if interfaces:
+                    req[endpoint] = {'interfaces': interfaces}
+            tosca_reqs.append(req)
+        return tosca_reqs
+
+    def get_property(self, args):
+        # TODO(Philip): Should figure out how to get this resolved
+        # by tosca-parser using GetProperty
+        if isinstance(args, list):
+            if len(args) == 2 and \
+               args[0] == 'SELF':
+                if args[1] in self.properties:
+                    return self.properties[args[1]]
+                else:
+                    self.log.error(_("{0}, property {} not defined").
+                                   format(self.name, args[1]))
+                    return
+        self.log.error(_("Get property for {0} of type {1} not supported").
+                       format(self.name, args))
+
+    def get_node_with_name(self, name, nodes):
+        """Get the node instance with specified name"""
+        for node in nodes:
+            if node.name == name:
+                return node
+
+    def get_nodes_related(self, target, type_, nodes):
+        """Get list of nodes related to target node"""
+        dep_nodes = []
+        for node in nodes:
+            if (node.name == target.name or
+                type_ != node.type):
+                continue
+            for rel in node.nodetemplate.related_nodes:
+                if rel.name == target.name:
+                    dep_nodes.append(node)
+                    break
+        return dep_nodes
+
+    def get_mano_attribute(self, attribute, args):
+        # this is a place holder and should be implemented by the subclass
+        # if translation is needed for the particular attribute
+        raise Exception(_("No translation in TOSCA type {0} for attribute "
+                          "{1}").format(self.nodetemplate.type, attribute))
+
+    @staticmethod
+    def _get_all_operations(node):
+        operations = {}
+        for operation in node.interfaces:
+            operations[operation.name] = operation
+
+        node_type = node.type_definition
+        if (isinstance(node_type, str) or
+            node_type.type == "tosca.policies.Placement"):
+            return operations
+
+        while True:
+            type_operations = ManoResource._get_interface_operations_from_type(
+                node_type, node, 'Standard')
+            type_operations.update(operations)
+            operations = type_operations
+
+            if node_type.parent_type is not None:
+                node_type = node_type.parent_type
+            else:
+                return operations
+
+    @staticmethod
+    def _get_interface_operations_from_type(node_type, node, lifecycle_name):
+        operations = {}
+        if (isinstance(node_type, str) or
+            node_type.type == "tosca.policies.Placement"):
+            return operations
+        if node_type.interfaces and lifecycle_name in node_type.interfaces:
+            for name, elems in node_type.interfaces[lifecycle_name].items():
+                # ignore empty operations (only type)
+                # ignore global interface inputs,
+                # concrete inputs are on the operations themselves
+                if name != 'type' and name != 'inputs':
+                    operations[name] = InterfacesDef(node_type,
+                                                     lifecycle_name,
+                                                     node, name, elems)
+        return operations
+
+    @staticmethod
+    def get_parent_type(node_type):
+        if node_type.parent_type is not None:
+            return node_type.parent_type
+        else:
+            return None
+
+    @staticmethod
+    def get_base_type(node_type):
+        parent_type = ManoResource.get_parent_type(node_type)
+        if parent_type is not None:
+            if parent_type.type.endswith('.Root'):
+                return node_type
+            else:
+                return ManoResource.get_base_type(parent_type)
+        else:
+            return node_type