2023db5a8ce00b6b1b6982b49c0b0047939c92fb
[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 node_name = nsd.pop(ToscaResource.NAME).replace(' ','_')
152 node_name = node_name if node_name.endswith('nsd') else ''.join([node_name, '_nsd'])
153 tosca_node = TranslateDescriptors. \
154 YANG_TO_TOSCA_TYPE[self.NSD](
155 self.log,
156 node_name,
157 self.NSD,
158 nsd,
159 self.vnfd_files)
160 self.tosca_resources.append(tosca_node)
161
162 vnfd_name_list = []
163 if self.VNFD in self.yangs:
164 for vnfd in self.yangs[self.VNFD]:
165 if vnfd['name'] not in vnfd_name_list:
166 self.log.debug(_("Translate descriptor of type vnfd: {}").
167 format(vnfd))
168 vnfd_name_list.append(vnfd['name'])
169 tosca_node = TranslateDescriptors. \
170 YANG_TO_TOSCA_TYPE[self.VNFD](
171 self.log,
172 vnfd.pop(ToscaResource.NAME),
173 self.VNFD,
174 vnfd)
175 self.tosca_resources.append(tosca_node)
176
177 # First translate VNFDs
178 for node in self.tosca_resources:
179 if node.type == self.VNFD:
180 self.log.debug(_("Handle yang for {0} of type {1}").
181 format(node.name, node.type_))
182 node.handle_yang()
183
184 # Now translate NSDs
185 for node in self.tosca_resources:
186 if node.type == self.NSD:
187 self.log.debug(_("Handle yang for {0} of type {1}").
188 format(node.name, node.type_))
189 node.handle_yang(self.tosca_resources)
190
191 return self.tosca_resources
192
193 def find_tosca_resource(self, name):
194 for resource in self.tosca_resources:
195 if resource.name == name:
196 return resource
197
198 def _find_yang_node(self, yang_name):
199 for node in self.nodetemplates:
200 if node.name == yang_name:
201 return node