* YANG to TOSCA translator
[osm/SO.git] / common / python / rift / mano / tosca_translator / rwmano / translate_node_templates.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.tosca_translator.common.utils import _
21 from rift.mano.tosca_translator.common.exception import ToscaClassAttributeError
22 from rift.mano.tosca_translator.common.exception import ToscaClassImportError
23 from rift.mano.tosca_translator.common.exception import ToscaModImportError
24 from rift.mano.tosca_translator.conf.config import ConfigProvider as translatorConfig
25 from rift.mano.tosca_translator.rwmano.syntax.mano_resource import ManoResource
26 from toscaparser.tosca_template import ToscaTemplate
27
28
29 class TranslateNodeTemplates(object):
30 '''Translate TOSCA NodeTemplates to RIFT.io MANO Resources.'''
31
32 ##################
33 # Module constants
34 ##################
35
36 TOSCA_TO_MANO_REQUIRES = {'container': 'server',
37 'host': 'server',
38 'dependency': 'depends_on',
39 'connects': 'depends_on'}
40
41 TOSCA_TO_MANO_PROPERTIES = {'properties': 'input'}
42
43 TOSCA_TO_MANO_TYPE = None
44
45 ###########################
46 # Module utility Functions
47 # for dynamic class loading
48 ###########################
49
50 def _load_classes(log, locations, classes):
51 '''Dynamically load all the classes from the given locations.'''
52
53 for cls_path in locations:
54 # Use the absolute path of the class path
55 abs_path = os.path.dirname(os.path.abspath(__file__))
56 abs_path = abs_path.replace('rift/mano/tosca_translator/rwmano', cls_path)
57 log.debug(_("Loading classes from %s") % abs_path)
58
59 # Grab all the tosca type module files in the given path
60 mod_files = [f for f in os.listdir(abs_path) if (
61 f.endswith('.py') and
62 not f.startswith('__init__') and
63 f.startswith('tosca_'))]
64
65 # For each module, pick out the target translation class
66 for f in mod_files:
67 # NOTE: For some reason the existing code does not use
68 # the map to instantiate
69 # ToscaBlockStorageAttachment. Don't add it to the map
70 # here until the dependent code is fixed to use the
71 # map.
72 if f == 'tosca_block_storage_attachment.py':
73 continue
74
75 # mod_name = cls_path + '/' + f.rstrip('.py')
76 # Above have an issue if the mod name ends with p or y
77 f_name, ext = f.rsplit('.', 1)
78 mod_name = cls_path + '/' + f_name
79 mod_name = mod_name.replace('/', '.')
80 try:
81 mod = importlib.import_module(mod_name)
82 target_name = getattr(mod, 'TARGET_CLASS_NAME')
83 clazz = getattr(mod, target_name)
84 classes.append(clazz)
85 except ImportError:
86 raise ToscaModImportError(mod_name=mod_name)
87 except AttributeError:
88 if target_name:
89 raise ToscaClassImportError(name=target_name,
90 mod_name=mod_name)
91 else:
92 # TARGET_CLASS_NAME is not defined in module.
93 # Re-raise the exception
94 raise
95
96 def _generate_type_map(log):
97 '''Generate TOSCA translation types map.
98
99 Load user defined classes from location path specified in conf file.
100 Base classes are located within the tosca directory.
101 '''
102
103 # Base types directory
104 BASE_PATH = 'rift/mano/tosca_translator/rwmano/tosca'
105
106 # Custom types directory defined in conf file
107 custom_path = translatorConfig.get_value('DEFAULT',
108 'custom_types_location')
109
110 # First need to load the parent module, for example 'contrib.mano',
111 # for all of the dynamically loaded classes.
112 classes = []
113 TranslateNodeTemplates._load_classes(log,
114 (BASE_PATH, custom_path),
115 classes)
116 try:
117 types_map = {clazz.toscatype: clazz for clazz in classes}
118 log.debug(_("Type maps loaded: {}").format(types_map.keys()))
119 except AttributeError as e:
120 raise ToscaClassAttributeError(message=e.message)
121
122 return types_map
123
124 def __init__(self, log, tosca, mano_template):
125 self.log = log
126 self.tosca = tosca
127 self.nodetemplates = self.tosca.nodetemplates
128 self.mano_template = mano_template
129 # list of all MANO resources generated
130 self.mano_resources = []
131 self.mano_policies = []
132 self.mano_groups = []
133 # mapping between TOSCA nodetemplate and MANO resource
134 log.debug(_('Mapping between TOSCA nodetemplate and MANO resource.'))
135 self.mano_lookup = {}
136 self.policies = self.tosca.topology_template.policies
137 self.groups = self.tosca.topology_template.groups
138 self.metadata = {}
139
140 def translate(self):
141 if TranslateNodeTemplates.TOSCA_TO_MANO_TYPE is None:
142 TranslateNodeTemplates.TOSCA_TO_MANO_TYPE = \
143 TranslateNodeTemplates._generate_type_map(self.log)
144 # Translate metadata
145 self.translate_metadata()
146 return self._translate_nodetemplates()
147
148 def translate_metadata(self):
149 """Translate and store the metadata in instance"""
150 FIELDS_MAP = {
151 'ID': 'name',
152 'vendor': 'vendor',
153 'version': 'version',
154 }
155 metadata = {}
156 # Initialize to default values
157 metadata['name'] = 'tosca_to_mano'
158 metadata['vendor'] = 'RIFT.io'
159 metadata['version'] = '1.0'
160 if 'metadata' in self.tosca.tpl:
161 tosca_meta = self.tosca.tpl['metadata']
162 for key in FIELDS_MAP:
163 if key in tosca_meta.keys():
164 metadata[FIELDS_MAP[key]] = str(tosca_meta[key])
165 if 'logo' in tosca_meta:
166 metadata['logo'] = os.path.basename(tosca_meta['logo'])
167 self.log.debug(_("Metadata {0}").format(metadata))
168 self.metadata = metadata
169
170 def _recursive_handle_properties(self, resource):
171 '''Recursively handle the properties of the depends_on_nodes nodes.'''
172 # Use of hashtable (dict) here should be faster?
173 if resource in self.processed_resources:
174 return
175 self.processed_resources.append(resource)
176 for depend_on in resource.depends_on_nodes:
177 self._recursive_handle_properties(depend_on)
178
179 if resource.type == "OS::Nova::ServerGroup":
180 resource.handle_properties(self.mano_resources)
181 else:
182 resource.handle_properties()
183
184 def _get_policy_type(self, policy):
185 if isinstance(policy, dict):
186 for key, details in policy.items():
187 if 'type' in details:
188 return details['type']
189
190 def _translate_nodetemplates(self):
191
192 self.log.debug(_('Translating the node templates.'))
193 # Copy the TOSCA graph: nodetemplate
194 all_node_templates = []
195 node_to_artifact_map = {}
196 vnf_type_to_vnf_node = {}
197 vnf_type_to_vdus_map = {}
198 vnf_type_substitution_mapping = {}
199 vnf_type_to_capability_substitution_mapping = {}
200 tpl = self.tosca.tpl['topology_template']['node_templates']
201 associated_vnfd_flag = False
202
203 for node in self.nodetemplates:
204 all_node_templates.append(node)
205 if node.parent_type.type == 'tosca.nodes.nfv.riftio.VNF1':
206 vnf_type_to_vnf_node[node.type] = node.name
207 for node_key in tpl:
208 if 'artifacts' in tpl[node_key]:
209 node_to_artifact_map[node_key] = tpl[node_key]['artifacts']
210 for template in self.tosca.nested_tosca_templates_with_topology:
211 tpl_node = template.tpl['node_templates']
212 vnf_type = template.substitution_mappings.node_type
213
214 vnf_type_to_vdus_map[vnf_type] = []
215 vnf_type_substitution_mapping[vnf_type] = []
216 vnf_type_to_capability_substitution_mapping[vnf_type] = []
217 vnf_type_to_capability_substitution_mapping[vnf_type] = []
218 policies = []
219
220 for node in template.nodetemplates:
221 all_node_templates.append(node)
222 for node_key in tpl_node:
223 if 'artifacts' in tpl_node[node_key]:
224 node_to_artifact_map[node_key] = tpl_node[node_key]['artifacts']
225 for node in template.nodetemplates:
226 if 'VDU' in node.type:
227 vnf_type_to_vdus_map[vnf_type].append(node.name)
228 for policy in template.policies:
229 policies.append(policy.name)
230 for req in template.substitution_mappings.requirements:
231 vnf_type_substitution_mapping[template.substitution_mappings.node_type].append(req)
232 if template.substitution_mappings.capabilities:
233 for capability in template.substitution_mappings.capabilities:
234 sub_list = template.substitution_mappings.capabilities[capability]
235 if len(sub_list) > 0:
236 vnf_type_to_capability_substitution_mapping[vnf_type].append({capability: sub_list[0]})
237
238 for node in all_node_templates:
239 base_type = ManoResource.get_base_type(node.type_definition)
240 self.log.debug(_("Translate node %(name)s of type %(type)s with "
241 "base %(base)s") %
242 {'name': node.name,
243 'type': node.type,
244 'base': base_type.type})
245 mano_node = TranslateNodeTemplates. \
246 TOSCA_TO_MANO_TYPE[base_type.type](
247 self.log,
248 node,
249 metadata=self.metadata)
250 # Currently tosca-parser does not add the artifacts
251 # to the node
252 if mano_node.type == 'vnfd':
253 associated_vnfd_flag = True
254 if mano_node.name in node_to_artifact_map:
255 mano_node.artifacts = node_to_artifact_map[mano_node.name]
256 self.mano_resources.append(mano_node)
257 self.mano_lookup[node] = mano_node
258
259 if not associated_vnfd_flag:
260 dummy_file = "{0}{1}".format(os.getenv('RIFT_INSTALL'), "/usr/rift/mano/common/dummy_vnf_node.yaml")
261 tosca_vnf = ToscaTemplate(dummy_file, {}, True)
262 vnf_type = self.tosca.topology_template.substitution_mappings.node_type
263 vnf_type_to_vdus_map[vnf_type] = []
264
265 for node in tosca_vnf.nodetemplates:
266 all_node_templates.append(node)
267 base_type = ManoResource.get_base_type(node.type_definition)
268 vnf_type_to_vnf_node[vnf_type] = node.name
269 mano_node = TranslateNodeTemplates. \
270 TOSCA_TO_MANO_TYPE[base_type.type](
271 self.log,
272 node,
273 metadata=self.metadata)
274 mano_node.vnf_type = vnf_type
275 self.mano_resources.append(mano_node)
276 print("Adding a new node")
277
278 for node in self.tosca.nodetemplates:
279 if 'VDU' in node.type:
280 vnf_type_to_vdus_map[vnf_type].append(node.name)
281
282 # The parser currently do not generate the objects for groups
283 for group in self.tosca.topology_template.groups:
284 group_type = group.type
285 if group_type:
286 group_node = TranslateNodeTemplates. \
287 TOSCA_TO_MANO_TYPE[group_type](
288 self.log,
289 group,
290 metadata=self.metadata)
291 self.mano_groups.append(group_node)
292
293 # The parser currently do not generate the objects for policies
294
295 for policy in self.tosca.topology_template.policies:
296 policy_type = policy.type
297 if policy_type:
298 policy_node = TranslateNodeTemplates. \
299 TOSCA_TO_MANO_TYPE[policy_type](
300 self.log,
301 policy,
302 metadata=self.metadata)
303 self.mano_policies.append(policy_node)
304 for template in self.tosca.nested_tosca_templates_with_topology:
305 vnf_type = template.substitution_mappings.node_type
306 if vnf_type in vnf_type_to_vnf_node:
307 vnf_node = vnf_type_to_vnf_node[vnf_type]
308
309 for policy in template.policies:
310 policy_type = policy.type
311 if policy_type:
312 policy_node = TranslateNodeTemplates. \
313 TOSCA_TO_MANO_TYPE[policy_type](
314 self.log,
315 policy,
316 metadata=self.metadata,
317 vnf_name=vnf_node)
318 self.mano_policies.append(policy_node)
319
320 for node in self.mano_resources:
321 self.log.debug(_("Handle properties for {0} of type {1}").
322 format(node.name, node.type_))
323 node.handle_properties()
324
325 self.log.debug(_("Handle capabilites for {0} of type {1}").
326 format(node.name, node.type_))
327 node.handle_capabilities()
328
329 self.log.debug(_("Handle aritfacts for {0} of type {1}").
330 format(node.name, node.type_))
331 node.handle_artifacts()
332
333 self.log.debug(_("Handle interfaces for {0} of type {1}").
334 format(node.name, node.type_))
335 node.handle_interfaces()
336
337 self.log.debug(_("Update image checksum for {0} of type {1}").
338 format(node.name, node.type_))
339 node.update_image_checksum(self.tosca.path)
340
341 for node in self.mano_resources:
342 # Handle vnf and vdu dependencies first
343 if node.type == "vnfd":
344 try:
345 self.log.debug(_("Handle requirements for {0} of "
346 "type {1}").
347 format(node.name, node.type_))
348 node.handle_requirements(self.mano_resources, self.mano_policies, vnf_type_to_vdus_map)
349
350 except Exception as e:
351 self.log.error(_("Exception for {0} in requirements {1}").
352 format(node.name, node.type_))
353 self.log.exception(e)
354
355 for node in self.mano_resources:
356 # Now handle other dependencies
357 if node.type != "vnfd":
358 try:
359 self.log.debug(_("Handle requirements for {0} of type {1}").
360 format(node.name, node.type_))
361 node.handle_requirements(self.mano_resources)
362 except Exception as e:
363 self.log.error(_("Exception for {0} in requirements {1}").
364 format(node.name, node.type_))
365 self.log.exception(e)
366
367 for node in self.mano_resources:
368 if node.type == "vld":
369 node.handle_vld_properties(self.mano_resources, vnf_type_substitution_mapping)
370 elif node.type == 'forwarding_path':
371 node.handle_forwarding_path_dependencies(self.mano_resources, vnf_type_to_capability_substitution_mapping)
372
373 return self.mano_resources
374
375 def translate_groups(self):
376 for group in self.mano_groups:
377 group.handle_properties(self.mano_resources, self.mano_groups)
378 return self.mano_groups
379
380 def translate_policies(self):
381 for policy in self.mano_policies:
382 policy.handle_properties(self.mano_resources, self.mano_groups)
383 return self.mano_policies
384
385 def find_mano_resource(self, name):
386 for resource in self.mano_resources:
387 if resource.name == name:
388 return resource
389
390 def _find_tosca_node(self, tosca_name):
391 for node in self.nodetemplates:
392 if node.name == tosca_name:
393 return node
394
395 def _find_mano_resource_for_tosca(self, tosca_name,
396 current_mano_resource=None):
397 if tosca_name == 'SELF':
398 return current_mano_resource
399 if tosca_name == 'HOST' and current_mano_resource is not None:
400 for req in current_mano_resource.nodetemplate.requirements:
401 if 'host' in req:
402 return self._find_mano_resource_for_tosca(req['host'])
403
404 for node in self.nodetemplates:
405 if node.name == tosca_name:
406 return self.mano_lookup[node]
407
408 return None