blob: 7c4f429943d317ac42f340e44ef3540079d0f788 [file] [log] [blame]
Matteo Scandolod12be212017-07-07 10:44:34 -07001from toscaparser.tosca_template import ToscaTemplate, ValidationError
Matteo Scandolo9ce18252017-06-22 10:48:25 -07002from default import TOSCA_RECIPES_DIR
Matteo Scandolo485b7132017-06-30 11:46:47 -07003from grpc_client.resources import RESOURCES
4from grpc_client.models_accessor import GRPCModelsAccessor
Matteo Scandolo9ce18252017-06-22 10:48:25 -07005
6class TOSCA_Parser:
7
Matteo Scandolo485b7132017-06-30 11:46:47 -07008 def compute_dependencies(self, template, models_by_name):
9 """
10 NOTE this method is augmenting self.template, isn't there a more explicit way to achieve it?
11 """
12 for nodetemplate in template.nodetemplates:
Matteo Scandolo9ce18252017-06-22 10:48:25 -070013 nodetemplate.dependencies = []
14 nodetemplate.dependencies_names = []
15 for reqs in nodetemplate.requirements:
16 for (k,v) in reqs.items():
17 name = v["node"]
Matteo Scandolo485b7132017-06-30 11:46:47 -070018 if (name in models_by_name):
19 nodetemplate.dependencies.append(models_by_name[name])
Matteo Scandolo9ce18252017-06-22 10:48:25 -070020 nodetemplate.dependencies_names.append(name)
21
22 # go another level deep, as our requirements can have requirements...
Matteo Scandolodf2600b2017-07-05 17:01:29 -070023 # NOTE do we still need to go deep?
Matteo Scandolo9ce18252017-06-22 10:48:25 -070024 for sd_req in v.get("requirements",[]):
25 for (sd_req_k, sd_req_v) in sd_req.items():
26 name = sd_req_v["node"]
Matteo Scandolo485b7132017-06-30 11:46:47 -070027 if (name in models_by_name):
28 nodetemplate.dependencies.append(models_by_name[name])
Matteo Scandolo9ce18252017-06-22 10:48:25 -070029 nodetemplate.dependencies_names.append(name)
30
Matteo Scandolo485b7132017-06-30 11:46:47 -070031 @staticmethod
32 def topsort_dependencies(g):
Matteo Scandolo9ce18252017-06-22 10:48:25 -070033
34 # Get set of all nodes, including those without outgoing edges
35 keys = set(g.keys())
36 values = set({})
37 for v in g.values():
38 values = values | set(v.dependencies_names)
39
40 all_nodes = list(keys | values)
41 steps = all_nodes
42
43
44 # Final order
45 order = []
46
47 # DFS stack, not using recursion
48 stack = []
49
50 # Unmarked set
51 unmarked = all_nodes
52
53 # visiting = [] - skip, don't expect 1000s of nodes, |E|/|V| is small
54
55 while unmarked:
56 stack.insert(0, unmarked[0]) # push first unmarked
57
58 while (stack):
59 n = stack[0]
60 add = True
61 try:
62 for m in g[n].dependencies_names:
63 if (m in unmarked):
64 add = False
65 stack.insert(0, m)
66 except KeyError:
67 pass
68 if (add):
69 if (n in steps and n not in order):
70 order.append(n)
71 item = stack.pop(0)
72 try:
73 unmarked.remove(item)
74 except ValueError:
75 pass
76
77 noorder = list(set(steps) - set(order))
78 return order + noorder
79
Matteo Scandolo9ce18252017-06-22 10:48:25 -070080 @staticmethod
81 def populate_model(model, data):
82 for k,v in data.iteritems():
83 setattr(model, k, v)
84 return model
85
86 @staticmethod
Matteo Scandolo9ce18252017-06-22 10:48:25 -070087 def _translate_exception(msg):
88 readable = []
89 for line in msg.splitlines():
Matteo Scandolo9ce18252017-06-22 10:48:25 -070090 if line.strip().startswith('MissingRequiredFieldError'):
91 readable.append(line)
Matteo Scandolod12be212017-07-07 10:44:34 -070092 if line.strip().startswith('UnknownFieldError'):
93 readable.append(line)
Matteo Scandolo9ce18252017-06-22 10:48:25 -070094
95 if len(readable) > 0:
96 return '/n'.join(readable)
97 else:
98 return msg
99
Matteo Scandolo485b7132017-06-30 11:46:47 -0700100 def save_recipe_to_tmp_file(self, recipe):
101 tmp_file = open(self.recipe_file, 'w')
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700102 tmp_file.write(recipe)
103 tmp_file.close()
104
Matteo Scandolo485b7132017-06-30 11:46:47 -0700105 @staticmethod
106 def get_tosca_models_by_name(template):
107 models_by_name = {}
108 for node in template.nodetemplates:
109 models_by_name[node.name] = node
110 return models_by_name
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700111
Matteo Scandolo485b7132017-06-30 11:46:47 -0700112 @staticmethod
113 def get_ordered_models_template(ordered_models_name, templates_by_model_name):
114 ordered_models_templates = []
115 for name in ordered_models_name:
116 if name in templates_by_model_name:
117 ordered_models_templates.append(templates_by_model_name[name])
118 return ordered_models_templates
119
Matteo Scandolo8bbb03a2017-07-05 14:03:33 -0700120 @staticmethod
121 def populate_dependencies(model, requirements, saved_models):
122 for dep in requirements:
123 class_name = dep.keys()[0]
124 related_model = saved_models[dep[class_name]['node']]
125 setattr(model, "%s_id" % class_name, related_model.id)
126 return model
Matteo Scandolo485b7132017-06-30 11:46:47 -0700127
128 def __init__(self, recipe):
129
130 # the template returned by TOSCA-Parser
131 self.template = None
132 # dictionary containing the models in the recipe and their template
133 self.templates_by_model_name = None
134 # list of models ordered by requirements
Matteo Scandolodf2600b2017-07-05 17:01:29 -0700135 self.ordered_models_name = []
Matteo Scandolo8bbb03a2017-07-05 14:03:33 -0700136 # dictionary containing the saved model
137 self.saved_model_by_name = {}
Matteo Scandolo485b7132017-06-30 11:46:47 -0700138
139 self.ordered_models_template = []
140 self.recipe_file = TOSCA_RECIPES_DIR + '/tmp.yaml'
141
Matteo Scandolodf2600b2017-07-05 17:01:29 -0700142 self.recipe = recipe
143
144 def execute(self):
Matteo Scandolo485b7132017-06-30 11:46:47 -0700145 try:
146 # [] save the recipe to a tmp file
Matteo Scandolodf2600b2017-07-05 17:01:29 -0700147 self.save_recipe_to_tmp_file(self.recipe)
Matteo Scandolo485b7132017-06-30 11:46:47 -0700148 # [] parse the recipe with TOSCA Parse
149 self.template = ToscaTemplate(self.recipe_file)
150 # [] get all models in the recipe
151 self.templates_by_model_name = self.get_tosca_models_by_name(self.template)
152 # [] compute requirements
153 self.compute_dependencies(self.template, self.templates_by_model_name)
154 # [] topsort requirements
155 self.ordered_models_name = self.topsort_dependencies(self.templates_by_model_name)
156 # [] topsort templates
157 self.ordered_models_template = self.get_ordered_models_template(self.ordered_models_name, self.templates_by_model_name)
158
159 for recipe in self.ordered_models_template:
160 # get properties from tosca
161 data = recipe.templates[recipe.name]['properties']
162 # [] get model by class name
163 class_name = recipe.type.replace("tosca.nodes.", "")
164 if class_name not in RESOURCES:
165 raise Exception("Nodetemplate %s's type %s is not a known resource" % (recipe.name, class_name))
166 model = GRPCModelsAccessor.get_model_from_classname(class_name, data)
Matteo Scandolod12be212017-07-07 10:44:34 -0700167 # [] populate model with data
Matteo Scandolo485b7132017-06-30 11:46:47 -0700168 model = self.populate_model(model, data)
169 # [] check if the model has requirements
170 # [] if it has populate them
Matteo Scandolo8bbb03a2017-07-05 14:03:33 -0700171 model = self.populate_dependencies(model, recipe.requirements, self.saved_model_by_name)
Matteo Scandolo485b7132017-06-30 11:46:47 -0700172 # [] save or update
173 model.save()
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700174
Matteo Scandolo8bbb03a2017-07-05 14:03:33 -0700175 self.saved_model_by_name[recipe.name] = model
176
Matteo Scandolod12be212017-07-07 10:44:34 -0700177 except ValidationError as e:
Matteo Scandolo485b7132017-06-30 11:46:47 -0700178 if e.message:
Matteo Scandolod12be212017-07-07 10:44:34 -0700179 exception_msg = TOSCA_Parser._translate_exception(e.message)
Matteo Scandolo485b7132017-06-30 11:46:47 -0700180 else:
Matteo Scandolod12be212017-07-07 10:44:34 -0700181 exception_msg = TOSCA_Parser._translate_exception(str(e))
Matteo Scandolo485b7132017-06-30 11:46:47 -0700182 raise Exception(exception_msg)
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700183
184