RIFT OSM R1 Initial Submission
[osm/SO.git] / common / python / rift / mano / tosca_translator / rwmano / tosca / tosca_nfv_vnf.py
diff --git a/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py b/common/python/rift/mano/tosca_translator/rwmano/tosca/tosca_nfv_vnf.py
new file mode 100644 (file)
index 0000000..3e52967
--- /dev/null
@@ -0,0 +1,305 @@
+#
+# Copyright 2016 RIFT.io Inc
+#
+# 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.
+#
+
+
+from rift.mano.tosca_translator.common.utils import _
+from rift.mano.tosca_translator.common.utils import convert_keys_to_python
+from rift.mano.tosca_translator.rwmano.syntax.mano_resource import ManoResource
+
+from toscaparser.common.exception import ValidationError
+
+try:
+    import gi
+    gi.require_version('RwVnfdYang', '1.0')
+
+    from gi.repository import RwVnfdYang
+except ImportError:
+    pass
+except ValueError:
+    pass
+
+
+# Name used to dynamically load appropriate map class.
+TARGET_CLASS_NAME = 'ToscaNfvVnf'
+
+
+class ToscaNfvVnf(ManoResource):
+    '''Translate TOSCA node type tosca.nodes.nfv.vnf.'''
+
+    toscatype = 'tosca.nodes.nfv.VNF'
+
+    REQUIRED_PROPS = ['name', 'short-name', 'id', 'short-name', 'description',
+                      'mgmt-interface']
+    OPTIONAL_PROPS = ['version', 'vendor', 'http-endpoint', 'monitoring-param',
+                      'connection-point']
+    IGNORE_PROPS = ['port']
+    TOSCA_CAPS = ['mgmt_interface', 'http_endpoint', 'monitoring_param_0',
+                  'monitoring_param_1', 'connection_point']
+
+    def __init__(self, log, nodetemplate, metadata=None):
+        super(ToscaNfvVnf, self).__init__(log,
+                                          nodetemplate,
+                                          type_="vnfd",
+                                          metadata=metadata)
+        self._const_vnfd = {}
+        self._vnf_config = {}
+        self._vdus = []
+
+    def map_tosca_name_to_mano(self, name):
+        new_name = super().map_tosca_name_to_mano(name)
+        if new_name.startswith('monitoring-param'):
+            new_name = 'monitoring-param'
+        if new_name == 'polling-interval':
+            new_name = 'polling_interval_secs'
+        return new_name
+
+    def handle_properties(self):
+        tosca_props = self.get_tosca_props()
+        self.log.debug(_("VNF {0} with tosca properties: {1}").
+                       format(self.name, tosca_props))
+
+        def get_vnf_config(config):
+            vnf_config = {}
+            for key, value in config.items():
+                new_key = self.map_tosca_name_to_mano(key)
+                if isinstance(value, dict):
+                    sub_config = {}
+                    for subkey, subvalue in value.items():
+                        sub_config[self.map_tosca_name_to_mano(subkey)] = \
+                                        subvalue
+                    vnf_config[new_key] = sub_config
+                else:
+                    vnf_config[new_key] = value
+
+            if vnf_config['config-type'] != 'script':
+                err_msg = _("{}, Only script config supported "
+                             "for now: {}"). \
+                           format(self, vnf_config['config-type'])
+                self.log.error(err_msg)
+                raise ValidationError(message=err_msg)
+
+            # Replace config-details with actual name (config-type)
+            if ('config-type' in vnf_config and
+                'config-details' in vnf_config):
+                vnf_config[vnf_config['config-type']] = \
+                                    vnf_config.pop('config-details')
+                vnf_config.pop('config-type')
+
+            # Update config-delay and confgig-priortiy to correct struct
+            vnf_config['config-attributes'] = {}
+            if 'config-delay' in vnf_config:
+                vnf_config['config-attributes']['config-delay'] = \
+                            vnf_config.pop('config-delay')
+            else:
+                vnf_config['config-attributes']['config-delay'] = 0
+            if 'config-priority' in vnf_config:
+                vnf_config['config-attributes']['config-priority'] = \
+                            vnf_config.pop('config-priority')
+            return vnf_config
+
+        vnf_props = {}
+        for key, value in tosca_props.items():
+            if key == 'id':
+                self._const_vnfd['member-vnf-index'] = int(value)
+                self._const_vnfd['vnfd-id-ref'] = self.id
+            elif key == 'vnf_configuration':
+                self._vnf_config = get_vnf_config(value)
+            else:
+                vnf_props[key] = value
+
+        if 'name' not in vnf_props:
+            vnf_props['name'] = self.name
+
+        if 'short-name' not in vnf_props:
+            vnf_props['short-name'] = self.name
+
+        if 'id' not in vnf_props:
+            vnf_props['id'] = self.id
+
+        if 'vendor' not in vnf_props:
+            vnf_props['vendor'] = self.vendor
+
+        if 'description' not in vnf_props:
+            vnf_props['description'] = self.description
+
+        if 'start_by_default' in vnf_props:
+            self._const_vnfd['start-by-default'] = \
+                                        vnf_props.pop('start_by_default')
+
+        self.log.debug(_("VNF {0} with constituent vnf: {1}").
+                       format(self.name, self._const_vnfd))
+        self.log.debug(_("VNF {0} with properties: {1}").
+                       format(self.name, vnf_props))
+        self.properties = vnf_props
+
+    def handle_capabilities(self):
+        tosca_caps = self.get_tosca_caps()
+        self.log.debug(_("VDU {0} tosca capabilites: {1}").
+                       format(self.name, tosca_caps))
+
+        def get_props(props):
+            properties = {}
+            for key in props.keys():
+                value = props[key]
+                if isinstance(value, dict):
+                    if 'get_property' in value:
+                        val = self.get_property(value['get_property'])
+                        value = val
+                properties[self.map_tosca_name_to_mano(key)] = value
+            return properties
+
+        for key, value in tosca_caps.items():
+            if key in ToscaNfvVnf.TOSCA_CAPS:
+                new_key = self.map_tosca_name_to_mano(key)
+                props = get_props(value)
+                if 'id' in props:
+                    props['id'] = str(props['id'])
+                if 'protocol' in props:
+                    props.pop('protocol')
+
+                # There is only one instance of mgmt interface, but others
+                # are a list
+                if key == 'mgmt_interface':
+                    self.properties[new_key] = props
+                elif key == 'http_endpoint':
+                    if new_key not in self.properties:
+                        self.properties[new_key] = []
+                    self.properties[new_key].append(props)
+                else:
+                    if new_key not in self.properties:
+                        self.properties[new_key] = []
+                    self.properties[new_key].append(props)
+
+        self.log.debug(_("VDU {0} properties: {1}").
+                       format(self.name, self.properties))
+
+    def handle_requirements(self, nodes):
+        tosca_reqs = self.get_tosca_reqs()
+        self.log.debug("VNF {0} requirements: {1}".
+                       format(self.name, tosca_reqs))
+
+        try:
+            for req in tosca_reqs:
+                if 'vdus' in req:
+                    target = req['vdus']['target']
+                    node = self.get_node_with_name(target, nodes)
+                    if node:
+                        self._vdus.append(node)
+                        node._vnf = self
+                        # Add the VDU id to mgmt-intf
+                        if 'mgmt-interface' in self.properties:
+                            self.properties['mgmt-interface']['vdu-id'] = \
+                                            node.id
+                            if 'vdu' in self.properties['mgmt-interface']:
+                                # Older yang
+                                self.properties['mgmt-interface'].pop('vdu')
+                    else:
+                        err_msg = _("VNF {0}, VDU {1} specified not found"). \
+                                  format(self.name, target)
+                        self.log.error(err_msg)
+                        raise ValidationError(message=err_msg)
+
+        except Exception as e:
+            err_msg = _("Exception getting VDUs for VNF {0}: {1}"). \
+                      format(self.name, e)
+            self.log.error(err_msg)
+            raise e
+
+        self.log.debug(_("VNF {0} properties: {1}").
+                       format(self.name, self.properties))
+
+    def generate_yang_model_gi(self, nsd, vnfds):
+        vnfd_cat = RwVnfdYang.YangData_Vnfd_VnfdCatalog()
+        vnfd = vnfd_cat.vnfd.add()
+        props = convert_keys_to_python(self.properties)
+        try:
+            vnfd.from_dict(props)
+        except Exception as e:
+            err_msg = _("{0} Exception updating vnfd from dict {1}: {2}"). \
+                      format(self, props, e)
+            self.log.error(err_msg)
+            raise e
+        vnfds.append(vnfd_cat)
+
+        # Update the VDU properties
+        for vdu in self._vdus:
+            vdu.generate_yang_submodel_gi(vnfd)
+
+        # Update constituent vnfd in nsd
+        try:
+            props = convert_keys_to_python(self._const_vnfd)
+            nsd.constituent_vnfd.add().from_dict(props)
+        except Exception as e:
+            err_msg = _("{0} Exception constituent vnfd from dict {1}: {2}"). \
+                      format(self, props, e)
+            self.log.error(err_msg)
+            raise e
+
+        # Update the vnf configuration info in mgmt_interface
+        props = convert_keys_to_python(self._vnf_config)
+        try:
+            vnfd.vnf_configuration.from_dict(props)
+        except Exception as e:
+            err_msg = _("{0} Exception vnfd mgmt intf from dict {1}: {2}"). \
+                      format(self, props, e)
+            self.log.error(err_msg)
+            raise e
+
+    def generate_yang_model(self, nsd, vnfds, use_gi=False):
+        """Generate yang model for the node"""
+        self.log.debug(_("Generate YANG model for {0}").
+                       format(self))
+
+        for key in ToscaNfvVnf.IGNORE_PROPS:
+            if key in self.properties:
+                self.properties.pop(key)
+
+        if use_gi:
+            return self.generate_yang_model_gi(nsd, vnfds)
+
+        vnfd = {}
+        vnfd.update(self.properties)
+        # Update vnf configuration on mgmt interface
+        vnfd['mgmt-interface']['vnf-configuration'] = self._vnf_config
+
+        # Update the VDU properties
+        vnfd['vdu'] = []
+        for vdu in self._vdus:
+            vnfd['vdu'].append(vdu.generate_yang_submodel())
+
+        vnfds.append(vnfd)
+
+        # Update constituent vnfd in nsd
+        if 'constituent-vnfd' not in nsd:
+            nsd['constituent-vnfd'] = []
+        nsd['constituent-vnfd'].append(self._const_vnfd)
+
+    def get_member_vnf_index(self):
+        return self._const_vnfd['member-vnf-index']
+
+    def get_supporting_files(self, files, desc_id=None):
+        files[self.id] = []
+        for vdu in self._vdus:
+            if vdu.image:
+                files[self.id].append({
+                    'type': 'image',
+                    'name': vdu.image,
+                },)
+            if vdu.cloud_init:
+                files[self.id].append({
+                    'type': 'cloud_init',
+                    'name': vdu.cloud_init,
+                },)