update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b second try
[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
171 def _recursive_handle_properties(self, resource):
172 '''Recursively handle the properties of the depends_on_nodes nodes.'''
173 # Use of hashtable (dict) here should be faster?
174 if resource in self.processed_resources:
175 return
176 self.processed_resources.append(resource)
177 for depend_on in resource.depends_on_nodes:
178 self._recursive_handle_properties(depend_on)
179
180 if resource.type == "OS::Nova::ServerGroup":
181 resource.handle_properties(self.mano_resources)
182 else:
183 resource.handle_properties()
184
185 def _get_policy_type(self, policy):
186 if isinstance(policy, dict):
187 for key, details in policy.items():
188 if 'type' in details:
189 return details['type']
190
191 def _translate_nodetemplates(self):
192
193 self.log.debug(_('Translating the node templates.'))
194 # Copy the TOSCA graph: nodetemplate
195 all_node_templates = []
196 node_to_artifact_map = {}
197 vnf_type_to_vnf_node = {}
198 vnf_type_to_vdus_map = {}
199 vnf_type_substitution_mapping = {}
200 vnf_type_to_capability_substitution_mapping = {}
201 tpl = self.tosca.tpl['topology_template']['node_templates']
202 associated_vnfd_flag = False
203
204 for node in self.nodetemplates:
205 all_node_templates.append(node)
206 if node.parent_type.type == 'tosca.nodes.nfv.riftio.VNF1':
207 vnf_type_to_vnf_node[node.type] = node.name
208 for node_key in tpl:
209 if 'artifacts' in tpl[node_key]:
210 node_to_artifact_map[node_key] = tpl[node_key]['artifacts']
211 for template in self.tosca.nested_tosca_templates_with_topology:
212 tpl_node = template.tpl['node_templates']
213 vnf_type = template.substitution_mappings.node_type
214
215 vnf_type_to_vdus_map[vnf_type] = []
216 vnf_type_substitution_mapping[vnf_type] = []
217 vnf_type_to_capability_substitution_mapping[vnf_type] = []
218 vnf_type_to_capability_substitution_mapping[vnf_type] = []
219 policies = []
220
221 for node in template.nodetemplates:
222 all_node_templates.append(node)
223 for node_key in tpl_node:
224 if 'artifacts' in tpl_node[node_key]:
225 node_to_artifact_map[node_key] = tpl_node[node_key]['artifacts']
226 for node in template.nodetemplates:
227 if 'VDU' in node.type:
228 vnf_type_to_vdus_map[vnf_type].append(node.name)
229 for policy in template.policies:
230 policies.append(policy.name)
231 if template.substitution_mappings.requirements:
232 for req in template.substitution_mappings.requirements:
233 vnf_type_substitution_mapping[template.substitution_mappings.node_type].append(req)
234 if template.substitution_mappings.capabilities:
235 for capability in template.substitution_mappings.capabilities:
236 sub_list = template.substitution_mappings.capabilities[capability]
237 if len(sub_list) > 0:
238 vnf_type_to_capability_substitution_mapping[vnf_type].append({capability: sub_list[0]})
239
240 for node in all_node_templates:
241 base_type = ManoResource.get_base_type(node.type_definition)
242 self.log.debug(_("Translate node %(name)s of type %(type)s with "
243 "base %(base)s") %
244 {'name': node.name,
245 'type': node.type,
246 'base': base_type.type})
247 mano_node = TranslateNodeTemplates. \
248 TOSCA_TO_MANO_TYPE[base_type.type](
249 self.log,
250 node,
251 metadata=self.metadata)
252 # Currently tosca-parser does not add the artifacts
253 # to the node
254 if mano_node.type == 'vnfd':
255 associated_vnfd_flag = True
256 if mano_node.name in node_to_artifact_map:
257 mano_node.artifacts = node_to_artifact_map[mano_node.name]
258 self.mano_resources.append(mano_node)
259 self.mano_lookup[node] = mano_node
260
261 if not associated_vnfd_flag:
262 dummy_file = "{0}{1}".format(os.getenv('RIFT_INSTALL'), "/usr/rift/mano/common/dummy_vnf_node.yaml")
263 tosca_vnf = ToscaTemplate(dummy_file, {}, True)
264 vnf_type = self.tosca.topology_template.substitution_mappings.node_type
265 vnf_type_to_vdus_map[vnf_type] = []
266
267 for node in tosca_vnf.nodetemplates:
268 all_node_templates.append(node)
269 base_type = ManoResource.get_base_type(node.type_definition)
270 vnf_type_to_vnf_node[vnf_type] = node.name
271 mano_node = TranslateNodeTemplates. \
272 TOSCA_TO_MANO_TYPE[base_type.type](
273 self.log,
274 node,
275 metadata=self.metadata)
276 mano_node.vnf_type = vnf_type
277 self.mano_resources.append(mano_node)
278
279 for node in self.tosca.nodetemplates:
280 if 'VDU' in node.type:
281 vnf_type_to_vdus_map[vnf_type].append(node.name)
282
283 # The parser currently do not generate the objects for groups
284 for group in self.tosca.topology_template.groups:
285 group_type = group.type
286 if group_type:
287 group_node = TranslateNodeTemplates. \
288 TOSCA_TO_MANO_TYPE[group_type](
289 self.log,
290 group,
291 metadata=self.metadata)
292 self.mano_groups.append(group_node)
293
294 # The parser currently do not generate the objects for policies
295
296 for policy in self.tosca.topology_template.policies:
297 policy_type = policy.type
298 if policy_type:
299 policy_node = TranslateNodeTemplates. \
300 TOSCA_TO_MANO_TYPE[policy_type](
301 self.log,
302 policy,
303 metadata=self.metadata)
304 self.mano_policies.append(policy_node)
305 for template in self.tosca.nested_tosca_templates_with_topology:
306 vnf_type = template.substitution_mappings.node_type
307 if vnf_type in vnf_type_to_vnf_node:
308 vnf_node = vnf_type_to_vnf_node[vnf_type]
309
310 for policy in template.policies:
311 policy_type = policy.type
312 if policy_type:
313 policy_node = TranslateNodeTemplates. \
314 TOSCA_TO_MANO_TYPE[policy_type](
315 self.log,
316 policy,
317 metadata=self.metadata,
318 vnf_name=vnf_node)
319 self.mano_policies.append(policy_node)
320
321 vnfd_resources = []
322 for node in self.mano_resources:
323 self.log.debug(_("Handle properties for {0} of type {1}").
324 format(node.name, node.type_))
325 node.handle_properties()
326
327 self.log.debug(_("Handle capabilites for {0} of type {1}").
328 format(node.name, node.type_))
329 node.handle_capabilities()
330
331 self.log.debug(_("Handle aritfacts for {0} of type {1}").
332 format(node.name, node.type_))
333 node.handle_artifacts()
334
335 self.log.debug(_("Handle interfaces for {0} of type {1}").
336 format(node.name, node.type_))
337 node.handle_interfaces()
338
339 self.log.debug(_("Update image checksum for {0} of type {1}").
340 format(node.name, node.type_))
341 node.update_image_checksum(self.tosca.path)
342
343 for node in list(self.mano_resources):
344 if node.type == "vnfd":
345 vnfd_resources.append(node)
346 self.mano_resources.remove(node)
347
348 vnfd_resources.sort(key=lambda x: x.member_vnf_id, reverse=True)
349 vnf_type_duplicate_map = {}
350 for node in reversed(vnfd_resources):
351 if node.vnf_type in vnf_type_duplicate_map:
352 for policy in self.mano_policies:
353 if hasattr(policy, '_vnf_name') and policy._vnf_name == node.name:
354 policy._vnf_name = vnf_type_duplicate_map[node.vnf_type]
355 continue
356 vnf_type_duplicate_map[node.vnf_type] = node.name
357
358 self.mano_resources.extend(vnfd_resources)
359 for node in self.mano_resources:
360 # Handle vnf and vdu dependencies first
361 if node.type == "vnfd":
362 try:
363 self.log.debug(_("Handle requirements for {0} of "
364 "type {1}").
365 format(node.name, node.type_))
366 node.handle_requirements(self.mano_resources, self.mano_policies, vnf_type_to_vdus_map)
367
368 except Exception as e:
369 self.log.error(_("Exception for {0} in requirements {1}").
370 format(node.name, node.type_))
371 self.log.exception(e)
372
373 for node in self.mano_resources:
374 # Now handle other dependencies
375 if node.type != "vnfd":
376 try:
377 self.log.debug(_("Handle requirements for {0} of type {1}").
378 format(node.name, node.type_))
379 node.handle_requirements(self.mano_resources)
380 except Exception as e:
381 self.log.error(_("Exception for {0} in requirements {1}").
382 format(node.name, node.type_))
383 self.log.exception(e)
384
385 for node in self.mano_resources:
386 if node.type == "vld":
387 node.handle_vld_properties(self.mano_resources, vnf_type_substitution_mapping)
388 elif node.type == 'forwarding_path':
389 node.handle_forwarding_path_dependencies(self.mano_resources, vnf_type_to_capability_substitution_mapping)
390
391 return self.mano_resources
392
393 def translate_groups(self):
394 for group in self.mano_groups:
395 group.handle_properties(self.mano_resources, self.mano_groups)
396 return self.mano_groups
397
398 def translate_policies(self):
399 for policy in self.mano_policies:
400 policy.handle_properties(self.mano_resources, self.mano_groups)
401 return self.mano_policies
402
403 def find_mano_resource(self, name):
404 for resource in self.mano_resources:
405 if resource.name == name:
406 return resource
407
408 def _find_tosca_node(self, tosca_name):
409 for node in self.nodetemplates:
410 if node.name == tosca_name:
411 return node
412
413 def _find_mano_resource_for_tosca(self, tosca_name,
414 current_mano_resource=None):
415 if tosca_name == 'SELF':
416 return current_mano_resource
417 if tosca_name == 'HOST' and current_mano_resource is not None:
418 for req in current_mano_resource.nodetemplate.requirements:
419 if 'host' in req:
420 return self._find_mano_resource_for_tosca(req['host'])
421
422 for node in self.nodetemplates:
423 if node.name == tosca_name:
424 return self.mano_lookup[node]
425
426 return None