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