RIFT OSM R1 Initial Submission
[osm/SO.git] / common / python / rift / mano / yang_translator / rwmano / translate_descriptors.py
diff --git a/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py b/common/python/rift/mano/yang_translator/rwmano/translate_descriptors.py
new file mode 100644 (file)
index 0000000..f0a6866
--- /dev/null
@@ -0,0 +1,192 @@
+#
+# 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 importlib
+import os
+
+from rift.mano.yang_translator.common.exception import YangClassAttributeError
+from rift.mano.yang_translator.common.exception import YangClassImportError
+from rift.mano.yang_translator.common.exception import YangModImportError
+from rift.mano.yang_translator.common.utils import _
+from rift.mano.yang_translator.conf.config import ConfigProvider \
+    as translatorConfig
+from rift.mano.yang_translator.rwmano.syntax.tosca_resource \
+    import ToscaResource
+
+
+class TranslateDescriptors(object):
+    '''Translate YANG NodeTemplates to RIFT.io MANO Resources.'''
+
+    YANG_DESC = (NSD, VNFD) = ('nsd', 'vnfd')
+
+    ###########################
+    # Module utility Functions
+    # for dynamic class loading
+    ###########################
+
+    YANG_TO_TOSCA_TYPE = None
+
+    def _load_classes(log, locations, classes):
+        '''Dynamically load all the classes from the given locations.'''
+
+        for cls_path in locations:
+            # Use the absolute path of the class path
+            abs_path = os.path.dirname(os.path.abspath(__file__))
+            abs_path = abs_path.replace('rift/mano/yang_translator/rwmano',
+                                        cls_path)
+            log.debug(_("Loading classes from %s") % abs_path)
+
+            # Grab all the yang type module files in the given path
+            mod_files = [f for f in os.listdir(abs_path) if (
+                f.endswith('.py') and
+                not f.startswith('__init__') and
+                f.startswith('yang_'))]
+
+            # For each module, pick out the target translation class
+            for f in mod_files:
+                f_name, ext = f.rsplit('.', 1)
+                mod_name = cls_path + '/' + f_name
+                mod_name = mod_name.replace('/', '.')
+                try:
+                    mod = importlib.import_module(mod_name)
+                    target_name = getattr(mod, 'TARGET_CLASS_NAME')
+                    clazz = getattr(mod, target_name)
+                    classes.append(clazz)
+                except ImportError:
+                    raise YangModImportError(mod_name=mod_name)
+                except AttributeError:
+                    if target_name:
+                        raise YangClassImportError(name=target_name,
+                                                    mod_name=mod_name)
+                    else:
+                        # TARGET_CLASS_NAME is not defined in module.
+                        # Re-raise the exception
+                        raise
+
+    def _generate_type_map(log):
+        '''Generate YANG translation types map.
+
+        Load user defined classes from location path specified in conf file.
+        Base classes are located within the yang directory.
+        '''
+
+        # Base types directory
+        BASE_PATH = 'rift/mano/yang_translator/rwmano/yang'
+
+        # Custom types directory defined in conf file
+        custom_path = translatorConfig.get_value('DEFAULT',
+                                                 'custom_types_location')
+
+        # First need to load the parent module, for example 'contrib.mano',
+        # for all of the dynamically loaded classes.
+        classes = []
+        TranslateDescriptors._load_classes(log,
+                                             (BASE_PATH, custom_path),
+                                             classes)
+        try:
+            types_map = {clazz.yangtype: clazz for clazz in classes}
+            log.debug(_("Type maps loaded: {}").format(types_map.keys()))
+        except AttributeError as e:
+            raise YangClassAttributeError(message=e.message)
+
+        return types_map
+
+    def __init__(self, log, yangs, tosca_template):
+        self.log = log
+        self.yangs = yangs
+        self.tosca_template = tosca_template
+        # list of all TOSCA resources generated
+        self.tosca_resources = []
+        self.metadata = {}
+        log.debug(_('Mapping between YANG nodetemplate and TOSCA resource.'))
+
+    def translate(self):
+        if TranslateDescriptors.YANG_TO_TOSCA_TYPE is None:
+            TranslateDescriptors.YANG_TO_TOSCA_TYPE = \
+                TranslateDescriptors._generate_type_map(self.log)
+        return self._translate_yang()
+
+    def translate_metadata(self):
+        """Translate and store the metadata in instance"""
+        FIELDS_MAP = {
+            'ID': 'name',
+            'vendor': 'vendor',
+            'version': 'version',
+        }
+        metadata = {}
+        # Initialize to default values
+        metadata['name'] = 'yang_to_tosca'
+        metadata['vendor'] = 'RIFT.io'
+        metadata['version'] = '1.0'
+        if 'nsd' in self.yangs:
+            yang_meta = self.yang['nsd'][0]
+        elif 'vnfd' in self.yangs:
+            yang_meta = self.yang['vnfd'][0]
+            for key in FIELDS_MAP:
+                if key in yang_meta.keys():
+                    metadata[key] = str(yang_meta[FIELDS_MAP[key]])
+        self.log.debug(_("Metadata {0}").format(metadata))
+        self.metadata = metadata
+
+    def _translate_yang(self):
+        self.log.debug(_('Translating the descriptors.'))
+        for nsd in self.yangs[self.NSD]:
+            self.log.debug(_("Translate descriptor of type nsd: {}").
+                           format(nsd))
+            tosca_node = TranslateDescriptors. \
+                         YANG_TO_TOSCA_TYPE[self.NSD](
+                             self.log,
+                             nsd.pop(ToscaResource.NAME),
+                             self.NSD,
+                             nsd)
+            self.tosca_resources.append(tosca_node)
+
+        for vnfd in self.yangs[self.VNFD]:
+            self.log.debug(_("Translate descriptor of type vnfd: {}").
+                           format(vnfd))
+            tosca_node = TranslateDescriptors. \
+                         YANG_TO_TOSCA_TYPE[self.VNFD](
+                             self.log,
+                             vnfd.pop(ToscaResource.NAME),
+                             self.VNFD,
+                             vnfd)
+            self.tosca_resources.append(tosca_node)
+
+        # First translate VNFDs
+        for node in self.tosca_resources:
+            if node.type == self.VNFD:
+                self.log.debug(_("Handle yang for {0} of type {1}").
+                               format(node.name, node.type_))
+                node.handle_yang()
+
+        # Now translate NSDs
+        for node in self.tosca_resources:
+            if node.type == self.NSD:
+                self.log.debug(_("Handle yang for {0} of type {1}").
+                               format(node.name, node.type_))
+                node.handle_yang(self.tosca_resources)
+
+        return self.tosca_resources
+
+    def find_tosca_resource(self, name):
+        for resource in self.tosca_resources:
+            if resource.name == name:
+                return resource
+
+    def _find_yang_node(self, yang_name):
+        for node in self.nodetemplates:
+            if node.name == yang_name:
+                return node