f0a68665a969a297da8b70e543dfe7ad6587d8af
[osm/SO.git] / common / python / rift / mano / yang_translator / rwmano / translate_descriptors.py
1 #
2 # Licensed under the Apache License, Version 2.0 (the "License"); you may
3 # not use this file except in compliance with the License. You may obtain
4 # a copy of the License at
5 #
6 # http://www.apache.org/licenses/LICENSE-2.0
7 #
8 # Unless required by applicable law or agreed to in writing, software
9 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
10 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
11 # License for the specific language governing permissions and limitations
12 # under the License.
13
14 # Copyright 2016 RIFT.io Inc
15
16
17 import importlib
18 import os
19
20 from rift.mano.yang_translator.common.exception import YangClassAttributeError
21 from rift.mano.yang_translator.common.exception import YangClassImportError
22 from rift.mano.yang_translator.common.exception import YangModImportError
23 from rift.mano.yang_translator.common.utils import _
24 from rift.mano.yang_translator.conf.config import ConfigProvider \
25 as translatorConfig
26 from rift.mano.yang_translator.rwmano.syntax.tosca_resource \
27 import ToscaResource
28
29
30 class TranslateDescriptors(object):
31 '''Translate YANG NodeTemplates to RIFT.io MANO Resources.'''
32
33 YANG_DESC = (NSD, VNFD) = ('nsd', 'vnfd')
34
35 ###########################
36 # Module utility Functions
37 # for dynamic class loading
38 ###########################
39
40 YANG_TO_TOSCA_TYPE = None
41
42 def _load_classes(log, locations, classes):
43 '''Dynamically load all the classes from the given locations.'''
44
45 for cls_path in locations:
46 # Use the absolute path of the class path
47 abs_path = os.path.dirname(os.path.abspath(__file__))
48 abs_path = abs_path.replace('rift/mano/yang_translator/rwmano',
49 cls_path)
50 log.debug(_("Loading classes from %s") % abs_path)
51
52 # Grab all the yang type module files in the given path
53 mod_files = [f for f in os.listdir(abs_path) if (
54 f.endswith('.py') and
55 not f.startswith('__init__') and
56 f.startswith('yang_'))]
57
58 # For each module, pick out the target translation class
59 for f in mod_files:
60 f_name, ext = f.rsplit('.', 1)
61 mod_name = cls_path + '/' + f_name
62 mod_name = mod_name.replace('/', '.')
63 try:
64 mod = importlib.import_module(mod_name)
65 target_name = getattr(mod, 'TARGET_CLASS_NAME')
66 clazz = getattr(mod, target_name)
67 classes.append(clazz)
68 except ImportError:
69 raise YangModImportError(mod_name=mod_name)
70 except AttributeError:
71 if target_name:
72 raise YangClassImportError(name=target_name,
73 mod_name=mod_name)
74 else:
75 # TARGET_CLASS_NAME is not defined in module.
76 # Re-raise the exception
77 raise
78
79 def _generate_type_map(log):
80 '''Generate YANG translation types map.
81
82 Load user defined classes from location path specified in conf file.
83 Base classes are located within the yang directory.
84 '''
85
86 # Base types directory
87 BASE_PATH = 'rift/mano/yang_translator/rwmano/yang'
88
89 # Custom types directory defined in conf file
90 custom_path = translatorConfig.get_value('DEFAULT',
91 'custom_types_location')
92
93 # First need to load the parent module, for example 'contrib.mano',
94 # for all of the dynamically loaded classes.
95 classes = []
96 TranslateDescriptors._load_classes(log,
97 (BASE_PATH, custom_path),
98 classes)
99 try:
100 types_map = {clazz.yangtype: clazz for clazz in classes}
101 log.debug(_("Type maps loaded: {}").format(types_map.keys()))
102 except AttributeError as e:
103 raise YangClassAttributeError(message=e.message)
104
105 return types_map
106
107 def __init__(self, log, yangs, tosca_template):
108 self.log = log
109 self.yangs = yangs
110 self.tosca_template = tosca_template
111 # list of all TOSCA resources generated
112 self.tosca_resources = []
113 self.metadata = {}
114 log.debug(_('Mapping between YANG nodetemplate and TOSCA resource.'))
115
116 def translate(self):
117 if TranslateDescriptors.YANG_TO_TOSCA_TYPE is None:
118 TranslateDescriptors.YANG_TO_TOSCA_TYPE = \
119 TranslateDescriptors._generate_type_map(self.log)
120 return self._translate_yang()
121
122 def translate_metadata(self):
123 """Translate and store the metadata in instance"""
124 FIELDS_MAP = {
125 'ID': 'name',
126 'vendor': 'vendor',
127 'version': 'version',
128 }
129 metadata = {}
130 # Initialize to default values
131 metadata['name'] = 'yang_to_tosca'
132 metadata['vendor'] = 'RIFT.io'
133 metadata['version'] = '1.0'
134 if 'nsd' in self.yangs:
135 yang_meta = self.yang['nsd'][0]
136 elif 'vnfd' in self.yangs:
137 yang_meta = self.yang['vnfd'][0]
138 for key in FIELDS_MAP:
139 if key in yang_meta.keys():
140 metadata[key] = str(yang_meta[FIELDS_MAP[key]])
141 self.log.debug(_("Metadata {0}").format(metadata))
142 self.metadata = metadata
143
144 def _translate_yang(self):
145 self.log.debug(_('Translating the descriptors.'))
146 for nsd in self.yangs[self.NSD]:
147 self.log.debug(_("Translate descriptor of type nsd: {}").
148 format(nsd))
149 tosca_node = TranslateDescriptors. \
150 YANG_TO_TOSCA_TYPE[self.NSD](
151 self.log,
152 nsd.pop(ToscaResource.NAME),
153 self.NSD,
154 nsd)
155 self.tosca_resources.append(tosca_node)
156
157 for vnfd in self.yangs[self.VNFD]:
158 self.log.debug(_("Translate descriptor of type vnfd: {}").
159 format(vnfd))
160 tosca_node = TranslateDescriptors. \
161 YANG_TO_TOSCA_TYPE[self.VNFD](
162 self.log,
163 vnfd.pop(ToscaResource.NAME),
164 self.VNFD,
165 vnfd)
166 self.tosca_resources.append(tosca_node)
167
168 # First translate VNFDs
169 for node in self.tosca_resources:
170 if node.type == self.VNFD:
171 self.log.debug(_("Handle yang for {0} of type {1}").
172 format(node.name, node.type_))
173 node.handle_yang()
174
175 # Now translate NSDs
176 for node in self.tosca_resources:
177 if node.type == self.NSD:
178 self.log.debug(_("Handle yang for {0} of type {1}").
179 format(node.name, node.type_))
180 node.handle_yang(self.tosca_resources)
181
182 return self.tosca_resources
183
184 def find_tosca_resource(self, name):
185 for resource in self.tosca_resources:
186 if resource.name == name:
187 return resource
188
189 def _find_yang_node(self, yang_name):
190 for node in self.nodetemplates:
191 if node.name == yang_name:
192 return node