summaryrefslogtreecommitdiffstats
path: root/app/toscalib/templates/topology.py
blob: 9426ef09d443b090d0c26e92639bf0d8ca98920e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
from toscalib.templates.constant import *
from toscalib.templates.heat_constants import *
from toscalib.templates.substitution_rule import SubstitutionRule
from toscalib.types.property import PropertyDefinition
from toscalib.templates.property_item import PropertyItem
from toscalib.templates.heat_constants import HOT_VERSION_NUM
import copy, logging

class ToscaTopology(object):
    def __init__(self, name, metadata_section=None, content_section=None):
        self.name = name
        self.metadata = metadata_section
        self.raw_content = content_section
        self.db  = None

        self.node_dict = {}
        self.inputs = {}
        self.aux_inputs = {}
        self.outputs = {}
        self.sub_rules = []

        self.node_index = 0
        self.temp_index = 0
        
        self.extra_imports = []
        
    def _parse_content(self, db):
        if self.db is not None:
            return
        
        self.db = db
        
        if self.raw_content is None:
            return
        
#        if self.raw_content.has_key(TOPO_INPUTS):
        if TOPO_INPUTS in self.raw_content:
            self._parse_input(db, self.raw_content[TOPO_INPUTS])
        
#        if self.raw_content.has_key(TOPO_NODE_TEMPLATES):
        if TOPO_NODE_TEMPLATES in self.raw_content:
            self._parse_node_template(db, self.raw_content[TOPO_NODE_TEMPLATES])
        else:
            logging.warning( 'Topology template: ' + self.name+ ' has NO node templates!')
        
#        if self.raw_content.has_key(TOPO_OUTPUTS):
        if TOPO_OUTPUTS in self.raw_content:
            self._parse_output(db, self.raw_content[TOPO_OUTPUTS])
            
#        if self.raw_content.has_key(TOPO_SUBSTITUION_MAPPINGS):
        if TOPO_SUBSTITUION_MAPPINGS in self.raw_content:
            self._parse_substitution(db, self.raw_content[TOPO_SUBSTITUION_MAPPINGS])
        else:
            self.sub_type = None        
        self._verify_substitution() 
        self._update_function_pointer()   
        
    def _parse_substitution(self, db, sub_sec):
#        if sub_sec.has_key(SUB_NODE_TYPE):
        if SUB_NODE_TYPE in sub_sec:
            self.sub_type = sub_sec[SUB_NODE_TYPE]
#            if db.NODE_TYPES.has_key(self.sub_type):
            if self.sub_type in db.NODE_TYPES:
                db.NODE_TYPES[self.sub_type].mapping_template = self
        else:
            logging.warning( 'substitution mapping section does not have node_type defined')
            return  
                 
#        if sub_sec.has_key(SUB_PROPERTY):
#            sub_prop = sub_sec[SUB_PROPERTY]
#            for sub_prop_name in sub_prop.keys():
#                self.sub_rules.append(SubstitutionRule(SUB_PROPERTY, None, sub_prop_name, sub_prop[sub_prop_name]))
 
        for sub_prop in db.NODE_TYPES[self.sub_type].properties.keys():
#            if self.inputs.has_key(sub_prop):
            if sub_prop in self.inputs:
                self.sub_rules.append(SubstitutionRule(SUB_PROPERTY, None, sub_prop, [SUB_INPUT, sub_prop]))

        for sub_attr in db.NODE_TYPES[self.sub_type].attributes.keys():
#            if self.outputs.has_key(sub_attr):
            if sub_attr in self.outputs:
                self.sub_rules.append(SubstitutionRule(SUB_ATTRIBUTE, None, sub_attr, [SUB_OUTPUT, sub_attr]))
                   
#        if sub_sec.has_key(SUB_CAPABILITY):
        if SUB_CAPABILITY in sub_sec:
            sub_cap = sub_sec[SUB_CAPABILITY]
            for sub_cap_name in sub_cap.keys():
                sub_cap_item = sub_cap[sub_cap_name]
                #standard capability mapping rule
                if type(sub_cap_item) is not dict: 
                    self.sub_rules.append(SubstitutionRule(SUB_CAPABILITY, sub_cap_name, None, sub_cap_item))
                #self-proposed capability mapping rules 
                else: 
#                    if sub_cap_item.has_key(SUB_CAP_ID):
                    if SUB_CAP_ID in sub_cap_item:
                        self.sub_rules.append(SubstitutionRule(SUB_CAPABILITY, sub_cap_name, SUB_CAP_ID, sub_cap_item[SUB_CAP_ID]))
#                    if sub_cap_item.has_key(SUB_CAP_PROPERTY):
                    if SUB_CAP_PROPERTY in sub_cap_item:
                        sub_cap_item_prop = sub_cap_item[SUB_CAP_PROPERTY] 
                        for sub_cap_item_prop_name in sub_cap_item_prop.keys():
                            self.sub_rules.append(SubstitutionRule(SUB_CAPABILITY, sub_cap_name, sub_cap_item_prop_name, sub_cap_item_prop[sub_cap_item_prop_name]))
        
#        if sub_sec.has_key(SUB_REQUIREMENT):
        if SUB_REQUIREMENT in sub_sec:
            sub_req = sub_sec[SUB_REQUIREMENT]
            for sub_req_name in sub_req.keys():
                sub_req_item = sub_req[sub_req_name]
            #standard requirement mapping rule
                if type(sub_req_item) is not dict: 
                    self.sub_rules.append(SubstitutionRule(SUB_REQUIREMENT, sub_req_name, None, sub_req_item))
            #self-proposed requirement mapping rules 
                else: 
#                    if sub_req_item.has_key(SUB_REQ_ID):   
                    if SUB_REQ_ID in sub_req_item:   
                        self.sub_rules.append(SubstitutionRule(SUB_REQUIREMENT, sub_req_name, SUB_REQ_ID, sub_req_item[SUB_REQ_ID]))
                    else:
                        logging.warning( 'Incorrect substitution mapping rules')
    
    def _verify_substitution(self, target_node=None):
        for rule in self.sub_rules:
            rule._update_pointer(target_node, self)       
    
    def _parse_input(self, db, input_sec):
        for input_name in input_sec.keys():
            input_def = PropertyDefinition(input_name, input_sec[input_name])
            input_def._parse_content(db)
            self.inputs[input_name] = PropertyItem(input_def)

    def _parse_output(self, db, output_sec):
        for output_name in output_sec.keys():
            output_def = PropertyDefinition(output_name)
#            output_def._parse_content(db)
            self.outputs[output_name] = PropertyItem(output_def)
#            if output_sec[output_name].has_key('value'):
            if 'value' in output_sec[output_name]:
                self.outputs[output_name]._assign(output_sec[output_name]['value'])
              
    def _parse_node_template(self, db, template_sec):
        self.node_dict = {}
        for name in template_sec.keys():
#            if template_sec[name].has_key(NOD_TYPE):
            if NOD_TYPE in template_sec[name]:
                node_type_name = template_sec[name][NOD_TYPE]
            else:
                logging.warning( 'Invalid template: node section has no type')
                continue
            
#            if db.NODE_TYPES.has_key(node_type_name) is False:
            if node_type_name not in db.NODE_TYPES:
                logging.warning( 'Invalid template: node type: '+ str(node_type_name)+ ' not defined or imported')
                continue
                
            from toscalib.templates.node import Node
            new_node = Node(self, name, db.NODE_TYPES[node_type_name])
            new_node._parse_pre_defined_content(template_sec[name])

            self._add_node(new_node)
        
        for node in iter(self.node_dict.values()):
            node._verify_requirements(self.node_dict)
            node._verify_functions()

            
        self.edge_list = self._create_edges()
        
    def _create_edges(self):
        edges = []
        for node in iter(self.node_dict.values()):
            for req in node.requirements:
                if req.filled is True:
                    new_edge = (node, self.node_dict[req.str_value])
                    logging.debug( 'edge created: '+ new_edge[0].name+ ' --> '+ new_edge[1].name)
                    edges.append(new_edge)
        return edges

    def _update_function_pointer(self, parent_temp=None):   
        for node in iter(self.node_dict.values()):
            #node._verify_requirements(self.node_dict)
            node._verify_functions(parent_temp)
        for output in iter(self.outputs.values()):
            if output.value is not None:
                if parent_temp is None:
                    output.value._update_function_reference(self)
                else:
                    output.value._update_function_reference(parent_temp)


    def _update_translation_function_pointer(self):
        for node in iter(self.node_dict.values()):
            if node.tran_template is not None:
                node.tran_template._update_function_pointer(node.template)
    
    def _update_get_node_name(self):
        for node in iter(self.node_dict.values()):
            node._update_get_node_name()
            
    def _update_template(self, template):
        for node in iter(self.node_dict.values()):
            node._update_template(template)
    
    def _update_prefix(self, prefix):
        exist_key_list = list(self.node_dict.keys())
        for node_key in exist_key_list:
            if node_key == 'NO_PREFIX':
                new_node_key = prefix[:len(prefix)-1]
            else:
                new_node_key = prefix + node_key
            node = self.node_dict.pop(node_key)
            node._update_prefix(prefix)
            self.node_dict[new_node_key] = node
            
        exist_key_list = list(self.inputs.keys())
        for item_key in exist_key_list:
            new_item_key = prefix + item_key
            item = self.inputs.pop(item_key)
            item._update_prefix(prefix)
            self.inputs[new_item_key] = item
            
        exist_key_list = list(self.outputs.keys())
        for item_key in exist_key_list:
            ###don't update output name prefix here
            ###temporary solution for cloudify generation
            ###but still need to update pointer for the value
            new_item_key = prefix + item_key
            #item = self.outputs.pop(item_key)
            #item._update_prefix(prefix)
            item = self.outputs[item_key]
            item.value._update_prefix(prefix)   
            item.value._update_function_reference(self)
            #self.outputs[new_item_key] = item
        
        #self._update_function_pointer()

            
    def _update_used_tag_for_translation(self):
        for item in iter(self.inputs.values()):
            item.used = False
        for node_item in iter(self.node_dict.values()):
            for prop_item in iter(node_item.properties.values()):
                prop_item.used = False
            
    def _add_node(self, new_node):
        if new_node is None:
            return
        self.node_dict[new_node.name] = new_node
        
    def _propagate_substitution_value(self):
        converge = False
        while converge is not True:
            converge = True
            for node_item in iter(self.node_dict.values()):
                converge = converge and node_item._propagate_substitution_value() 
            
     
    def _auto_generate_aux_inputs(self):
        for node_name in self.node_dict.keys():
            node = self.node_dict[node_name]
            for prop_name in  node.properties.keys():
                prop_item = node.properties[prop_name]
                if prop_item.value is None or prop_item.filled is False:
                    new_input_name = node_name + '_' + prop_name
#                    while self.inputs.has_key(new_input_name) or self.aux_inputs.has_key(new_input_name):
                    while new_input_name in self.inputs or new_input_name in self.aux_inputs:
                        new_input_name = new_input_name + '_'
                    def_item = copy.deepcopy(prop_item.definition)
                    def_item.name = new_input_name
                    self.aux_inputs[new_input_name] = PropertyItem(def_item)
                    fun_item = {}
                    fun_item['get_input'] = new_input_name
                    prop_item._assign(fun_item)
                    prop_item.value._update_function_reference(self)
                
    def _prepare_node_types(self):
        for node_type in iter(self.db.NODE_TYPES.values()):
            node_type.used = False
            
        for node in iter(self.node_dict.values()):
            node_type = node.type_obj
            while node_type is not None:
                self.db.NODE_TYPES[node_type.name].used = True
                node_type = node_type.parent
    
    def _prepare_node_types_output(self, tags=''):
        self._prepare_node_types()
        node_type = {}
        if 'noexpand' not in tags:
            for node in iter(self.node_dict.values()):
                if node.tran_template is not None: 
                    node_type.update(node.tran_template._prepare_node_types_output(tags))
        if len(node_type) < 1:
            for ntype in iter(self.db.NODE_TYPES.values()):
                if ntype.used is False:
                    continue
                type_content = copy.deepcopy(ntype.raw_content)
                if 'cloudify' in tags:
                    if ntype.name == 'cloudify.nodes.Root':
                        continue
                    
                    type_content.pop('capabilities', None)
                    type_content.pop('requirements', None)
                    type_content.pop('attributes', None)
                else: 
                    if ntype.name == 'tosca.nodes.Root':
                        continue

                node_type[ntype.name] = type_content
                
        return node_type
            
    def _prepare_extra_imports(self, tags):
        if 'cloudify' in tags:
            ret_val = []
            for item in self.extra_imports:
                ret_val += list(item.values())
            return ret_val
        else:
            return self.extra_imports
           
    def _prepare_output(self, tags=''):

        output ={} 
        import_sec = []
        
        if 'cloudify' in tags:
            output[YMO_VERSION]= 'cloudify_dsl_1_3'
            for item in self.extra_imports:
                import_sec += list(item.values()) 
            #import_sec.append('http://www.getcloudify.org/spec/cloudify/3.4/types.yaml')
        else:
            import_sec += self.extra_imports 
            output[YMO_VERSION]= 'tosca_simple_yaml_1_0_0'
        
        if 'import_schema' in tags: 
            output[YMO_IMPORT] = [{'schema': 'schema.yaml'}]
            
        if self.metadata is not None and 'java_sim' not in tags:
            output[YMO_METADATA] = self.metadata
        topo_sec = {}
        node_temp = {}
        for node in iter(self.node_dict.values()):
            node_temp.update(node._prepare_output(tags))
            import_sec += node._prepare_extra_imports(tags)
            
        if 'part' in tags: 
            return node_temp
        
        if len(node_temp.keys())> 0:
            topo_sec[YMO_TOPO_NODE_TEMPLATES] = node_temp
       
        input_sec = {}
        for name in self.inputs.keys():
            input_sec.update(self.inputs[name]._prepare_input_type_output(tags))
        for name in self.aux_inputs.keys():
            input_sec.update(self.aux_inputs[name]._prepare_input_type_output(tags))
        if (len(input_sec.keys())> 0) and 'java_sim' not in tags:
            topo_sec[YMO_TOPO_INPUTS] = input_sec
        output_sec = {}
        for name in self.outputs.keys():
            output_sec.update(self.outputs[name]._prepare_output_type_output())
        if (len(output_sec.keys())> 0) and 'java_sim' not in tags:
            topo_sec[YMO_TOPO_OUTPUTS] = output_sec
            
            
        if 'w_sub' in tags and self.sub_type is not None:
            sub_sec = {}
            sub_sec[YMO_SUB_NODE_TYPE] = self.sub_type
            sub_cap = {}
            sub_req = {}
            for sub_rule in self.sub_rules:
                if sub_rule.type is SUB_CAPABILITY:
                    sub_cap[sub_rule.item] = sub_rule.value
                if sub_rule.type is SUB_REQUIREMENT:
                    sub_req[sub_rule.item] = sub_rule.value
            sub_sec[YMO_SUB_CAPABILITY] = sub_cap
            sub_sec[YMO_SUB_REQUIREMENT] = sub_req
            
            topo_sec[YMO_TOPO_SUBSTITUION_MAPPINGS] = sub_sec 
    
        if 'cloudify' in tags:
            output.update(topo_sec)
        else:
            output[YMO_TOPOLOGY] = topo_sec
        
        if 'nodetype' in tags and 'java_sim' not in tags:
            output[YMO_NODE_TYPE] = self._prepare_node_types_output(tags)
        
        if len(import_sec) > 0:
            output[YMO_IMPORT] = import_sec

        
        return output
        
        
    def _prepare_heat_output(self, parameters_type = {}, parameters_val = {}, stripped = False):
        output = {}
        env_output = {}
        output[YMO_HOT_VERSION] = HOT_VERSION_NUM  

        for input_item in iter(self.inputs.values()):
            out1, out2 = input_item._prepare_heat_output()
            parameters_type.update(out1)
            parameters_val.update(out2)
        resources = {}
        for node in iter(self.node_dict.values()):
            resources.udpate(node._prepare_heat_output(parameters_type, parameters_val))
            
        output[YMO_HOT_PARAMETERS] = parameters_type
        output[YMO_HOT_RESOURCES]  = resources
        env_output[YMO_HOT_PARAMETERS] = parameters_val
        
        if stripped is True: 
            return resources
        else:
            return output, env_output
        
        
    def toJson(self):
        ret_json = {}
        tmp_json = {}
        for node in iter(self.node_dict.values()):
            tmp_json[node.name] = node.toJson()
        ret_json['nodes'] = tmp_json
        ret_json['relations'] = {}
        ret_json['inputs'] = {}
        ret_json['outputs'] = {}
        return ret_json