dbfaa62a1878de567e54016e25b9d4173afad657
[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 self.log.debug(_("Metadata {0}").format(metadata))
165 self.metadata = metadata
166
167 def _recursive_handle_properties(self, resource):
168 '''Recursively handle the properties of the depends_on_nodes nodes.'''
169 # Use of hashtable (dict) here should be faster?
170 if resource in self.processed_resources:
171 return
172 self.processed_resources.append(resource)
173 for depend_on in resource.depends_on_nodes:
174 self._recursive_handle_properties(depend_on)
175
176 if resource.type == "OS::Nova::ServerGroup":
177 resource.handle_properties(self.mano_resources)
178 else:
179 resource.handle_properties()
180
181 def _get_policy_type(self, policy):
182 if isinstance(policy, dict):
183 for key, details in policy.items():
184 if 'type' in details:
185 return details['type']
186
187 def _translate_nodetemplates(self):
188
189 self.log.debug(_('Translating the node templates.'))
190 # Copy the TOSCA graph: nodetemplate
191 tpl = self.tosca.tpl['topology_template']['node_templates']
192 for node in self.nodetemplates:
193 base_type = ManoResource.get_base_type(node.type_definition)
194 self.log.debug(_("Translate node %(name)s of type %(type)s with "
195 "base %(base)s") %
196 {'name': node.name,
197 'type': node.type,
198 'base': base_type.type})
199 mano_node = TranslateNodeTemplates. \
200 TOSCA_TO_MANO_TYPE[base_type.type](
201 self.log,
202 node,
203 metadata=self.metadata)
204 # Currently tosca-parser does not add the artifacts
205 # to the node
206 if mano_node.name in tpl:
207 tpl_node = tpl[mano_node.name]
208 self.log.debug("Check artifacts for {}".format(tpl_node))
209 if 'artifacts' in tpl_node:
210 mano_node.artifacts = tpl_node['artifacts']
211 self.mano_resources.append(mano_node)
212 self.mano_lookup[node] = mano_node
213
214 # The parser currently do not generate the objects for groups
215 if 'groups' in self.tosca.tpl['topology_template']:
216 tpl = self.tosca.tpl['topology_template']['groups']
217 self.log.debug("Groups: {}".format(tpl))
218 for group, details in tpl.items():
219 self.log.debug(_("Translate group {}: {}").
220 format(group, details))
221 group_type = details['type']
222 if group_type:
223 group_node = TranslateNodeTemplates. \
224 TOSCA_TO_MANO_TYPE[group_type](
225 self.log,
226 group,
227 details,
228 metadata=self.metadata)
229 self.mano_groups.append(group_node)
230
231 # The parser currently do not generate the objects for policies
232 if 'policies' in self.tosca.tpl['topology_template']:
233 tpl = self.tosca.tpl['topology_template']['policies']
234 # for policy in self.policies:
235 for policy in tpl:
236 self.log.debug(_("Translate policy {}").
237 format(policy))
238 policy_type = self._get_policy_type(policy)
239 if policy_type:
240 policy_node = TranslateNodeTemplates. \
241 TOSCA_TO_MANO_TYPE[policy_type](
242 self.log,
243 policy,
244 metadata=self.metadata)
245 self.mano_policies.append(policy_node)
246
247 for node in self.mano_resources:
248 self.log.debug(_("Handle properties for {0} of type {1}").
249 format(node.name, node.type_))
250 node.handle_properties()
251
252 self.log.debug(_("Handle capabilites for {0} of type {1}").
253 format(node.name, node.type_))
254 node.handle_capabilities()
255
256 self.log.debug(_("Handle aritfacts for {0} of type {1}").
257 format(node.name, node.type_))
258 node.handle_artifacts()
259
260 self.log.debug(_("Handle interfaces for {0} of type {1}").
261 format(node.name, node.type_))
262 node.handle_interfaces()
263
264 self.log.debug(_("Update image checksum for {0} of type {1}").
265 format(node.name, node.type_))
266 node.update_image_checksum(self.tosca.path)
267
268 for node in self.mano_resources:
269 # Handle vnf and vdu dependencies first
270 if node.type == "vnfd":
271 try:
272 self.log.debug(_("Handle requirements for {0} of "
273 "type {1}").
274 format(node.name, node.type_))
275 node.handle_requirements(self.mano_resources)
276 except Exception as e:
277 self.log.error(_("Exception for {0} in requirements {1}").
278 format(node.name, node.type_))
279 self.log.exception(e)
280
281 for node in self.mano_resources:
282 # Now handle other dependencies
283 if node.type != "vnfd":
284 try:
285 self.log.debug(_("Handle requirements for {0} of type {1}").
286 format(node.name, node.type_))
287 node.handle_requirements(self.mano_resources)
288 except Exception as e:
289 self.log.error(_("Exception for {0} in requirements {1}").
290 format(node.name, node.type_))
291 self.log.exception(e)
292
293 return self.mano_resources
294
295 def translate_groups(self):
296 for group in self.mano_groups:
297 group.handle_properties(self.mano_resources)
298 return self.mano_groups
299
300 def translate_policies(self):
301 for policy in self.mano_policies:
302 policy.handle_properties(self.mano_resources, self.mano_groups)
303 return self.mano_policies
304
305 def find_mano_resource(self, name):
306 for resource in self.mano_resources:
307 if resource.name == name:
308 return resource
309
310 def _find_tosca_node(self, tosca_name):
311 for node in self.nodetemplates:
312 if node.name == tosca_name:
313 return node
314
315 def _find_mano_resource_for_tosca(self, tosca_name,
316 current_mano_resource=None):
317 if tosca_name == 'SELF':
318 return current_mano_resource
319 if tosca_name == 'HOST' and current_mano_resource is not None:
320 for req in current_mano_resource.nodetemplate.requirements:
321 if 'host' in req:
322 return self._find_mano_resource_for_tosca(req['host'])
323
324 for node in self.nodetemplates:
325 if node.name == tosca_name:
326 return self.mano_lookup[node]
327
328 return None