707ab7fbc4e98bee96969e570a07235e450b4951
[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, vnfd_files=None):
108 self.log = log
109 self.yangs = yangs
110 self.tosca_template = tosca_template
111 self.vnfd_files = vnfd_files
112 # list of all TOSCA resources generated
113 self.tosca_resources = []
114 self.metadata = {}
115 log.debug(_('Mapping between YANG nodetemplate and TOSCA resource.'))
116
117 def translate(self):
118 if TranslateDescriptors.YANG_TO_TOSCA_TYPE is None:
119 TranslateDescriptors.YANG_TO_TOSCA_TYPE = \
120 TranslateDescriptors._generate_type_map(self.log)
121 return self._translate_yang()
122
123 def translate_metadata(self):
124 """Translate and store the metadata in instance"""
125 FIELDS_MAP = {
126 'ID': 'name',
127 'vendor': 'vendor',
128 'version': 'version',
129 }
130 metadata = {}
131 # Initialize to default values
132 metadata['name'] = 'yang_to_tosca'
133 metadata['vendor'] = 'RIFT.io'
134 metadata['version'] = '1.0'
135 if 'nsd' in self.yangs:
136 yang_meta = self.yang['nsd'][0]
137 elif 'vnfd' in self.yangs:
138 yang_meta = self.yang['vnfd'][0]
139 for key in FIELDS_MAP:
140 if key in yang_meta.keys():
141 metadata[key] = str(yang_meta[FIELDS_MAP[key]])
142 self.log.debug(_("Metadata {0}").format(metadata))
143 self.metadata = metadata
144
145 def _translate_yang(self):
146 self.log.debug(_('Translating the descriptors.'))
147 if self.NSD in self.yangs:
148 for nsd in self.yangs[self.NSD]:
149 self.log.debug(_("Translate descriptor of type nsd: {}").
150 format(nsd))
151 tosca_node = TranslateDescriptors. \
152 YANG_TO_TOSCA_TYPE[self.NSD](
153 self.log,
154 nsd.pop(ToscaResource.NAME),
155 self.NSD,
156 nsd,
157 self.vnfd_files)
158 self.tosca_resources.append(tosca_node)
159
160 if self.VNFD in self.yangs:
161 for vnfd in self.yangs[self.VNFD]:
162 self.log.debug(_("Translate descriptor of type vnfd: {}").
163 format(vnfd))
164 tosca_node = TranslateDescriptors. \
165 YANG_TO_TOSCA_TYPE[self.VNFD](
166 self.log,
167 vnfd.pop(ToscaResource.NAME),
168 self.VNFD,
169 vnfd)
170 self.tosca_resources.append(tosca_node)
171
172 # First translate VNFDs
173 for node in self.tosca_resources:
174 if node.type == self.VNFD:
175 self.log.debug(_("Handle yang for {0} of type {1}").
176 format(node.name, node.type_))
177 node.handle_yang()
178
179 # Now translate NSDs
180 for node in self.tosca_resources:
181 if node.type == self.NSD:
182 self.log.debug(_("Handle yang for {0} of type {1}").
183 format(node.name, node.type_))
184 node.handle_yang(self.tosca_resources)
185
186 return self.tosca_resources
187
188 def find_tosca_resource(self, name):
189 for resource in self.tosca_resources:
190 if resource.name == name:
191 return resource
192
193 def _find_yang_node(self, yang_name):
194 for node in self.nodetemplates:
195 if node.name == yang_name:
196 return node