blob: d98cdf6a01b45b061e681e2969fc6495359d8903 [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 Scandolo21dde412017-07-11 18:54:12 -07005from grpc._channel import _Rendezvous
6import json
Matteo Scandolo9ce18252017-06-22 10:48:25 -07007
8class TOSCA_Parser:
9
Matteo Scandolo485b7132017-06-30 11:46:47 -070010 def compute_dependencies(self, template, models_by_name):
11 """
12 NOTE this method is augmenting self.template, isn't there a more explicit way to achieve it?
13 """
14 for nodetemplate in template.nodetemplates:
Matteo Scandolo9ce18252017-06-22 10:48:25 -070015 nodetemplate.dependencies = []
16 nodetemplate.dependencies_names = []
17 for reqs in nodetemplate.requirements:
18 for (k,v) in reqs.items():
19 name = v["node"]
Matteo Scandolo485b7132017-06-30 11:46:47 -070020 if (name in models_by_name):
21 nodetemplate.dependencies.append(models_by_name[name])
Matteo Scandolo9ce18252017-06-22 10:48:25 -070022 nodetemplate.dependencies_names.append(name)
23
24 # go another level deep, as our requirements can have requirements...
Matteo Scandolodf2600b2017-07-05 17:01:29 -070025 # NOTE do we still need to go deep?
Matteo Scandolo9ce18252017-06-22 10:48:25 -070026 for sd_req in v.get("requirements",[]):
27 for (sd_req_k, sd_req_v) in sd_req.items():
28 name = sd_req_v["node"]
Matteo Scandolo485b7132017-06-30 11:46:47 -070029 if (name in models_by_name):
30 nodetemplate.dependencies.append(models_by_name[name])
Matteo Scandolo9ce18252017-06-22 10:48:25 -070031 nodetemplate.dependencies_names.append(name)
32
Matteo Scandolo485b7132017-06-30 11:46:47 -070033 @staticmethod
34 def topsort_dependencies(g):
Matteo Scandolo9ce18252017-06-22 10:48:25 -070035
36 # Get set of all nodes, including those without outgoing edges
37 keys = set(g.keys())
38 values = set({})
39 for v in g.values():
40 values = values | set(v.dependencies_names)
41
42 all_nodes = list(keys | values)
43 steps = all_nodes
44
45
46 # Final order
47 order = []
48
49 # DFS stack, not using recursion
50 stack = []
51
52 # Unmarked set
53 unmarked = all_nodes
54
55 # visiting = [] - skip, don't expect 1000s of nodes, |E|/|V| is small
56
57 while unmarked:
58 stack.insert(0, unmarked[0]) # push first unmarked
59
60 while (stack):
61 n = stack[0]
62 add = True
63 try:
64 for m in g[n].dependencies_names:
65 if (m in unmarked):
66 add = False
67 stack.insert(0, m)
68 except KeyError:
69 pass
70 if (add):
71 if (n in steps and n not in order):
72 order.append(n)
73 item = stack.pop(0)
74 try:
75 unmarked.remove(item)
76 except ValueError:
77 pass
78
79 noorder = list(set(steps) - set(order))
80 return order + noorder
81
Matteo Scandolo9ce18252017-06-22 10:48:25 -070082 @staticmethod
83 def populate_model(model, data):
84 for k,v in data.iteritems():
85 setattr(model, k, v)
86 return model
87
88 @staticmethod
Matteo Scandolo9ce18252017-06-22 10:48:25 -070089 def _translate_exception(msg):
90 readable = []
91 for line in msg.splitlines():
Matteo Scandolo9ce18252017-06-22 10:48:25 -070092 if line.strip().startswith('MissingRequiredFieldError'):
93 readable.append(line)
Matteo Scandolod12be212017-07-07 10:44:34 -070094 if line.strip().startswith('UnknownFieldError'):
95 readable.append(line)
Matteo Scandolo9ce18252017-06-22 10:48:25 -070096
97 if len(readable) > 0:
98 return '/n'.join(readable)
99 else:
100 return msg
101
Matteo Scandolo485b7132017-06-30 11:46:47 -0700102 def save_recipe_to_tmp_file(self, recipe):
103 tmp_file = open(self.recipe_file, 'w')
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700104 tmp_file.write(recipe)
105 tmp_file.close()
106
Matteo Scandolo485b7132017-06-30 11:46:47 -0700107 @staticmethod
108 def get_tosca_models_by_name(template):
109 models_by_name = {}
110 for node in template.nodetemplates:
111 models_by_name[node.name] = node
112 return models_by_name
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700113
Matteo Scandolo485b7132017-06-30 11:46:47 -0700114 @staticmethod
115 def get_ordered_models_template(ordered_models_name, templates_by_model_name):
116 ordered_models_templates = []
117 for name in ordered_models_name:
118 if name in templates_by_model_name:
119 ordered_models_templates.append(templates_by_model_name[name])
120 return ordered_models_templates
121
Matteo Scandolo8bbb03a2017-07-05 14:03:33 -0700122 @staticmethod
123 def populate_dependencies(model, requirements, saved_models):
124 for dep in requirements:
125 class_name = dep.keys()[0]
126 related_model = saved_models[dep[class_name]['node']]
127 setattr(model, "%s_id" % class_name, related_model.id)
128 return model
Matteo Scandolo485b7132017-06-30 11:46:47 -0700129
Matteo Scandolo78ca3eb2017-07-13 16:58:22 -0700130 def __init__(self, recipe, username, password, **kwargs):
131
132 self.delete = False
133 if 'delete' in kwargs:
134 self.delete = True
Matteo Scandolo21dde412017-07-11 18:54:12 -0700135
136 # store username/password combination to read resources
137 self.username = username
138 self.password = password
Matteo Scandolo485b7132017-06-30 11:46:47 -0700139
140 # the template returned by TOSCA-Parser
141 self.template = None
142 # dictionary containing the models in the recipe and their template
143 self.templates_by_model_name = None
144 # list of models ordered by requirements
Matteo Scandolodf2600b2017-07-05 17:01:29 -0700145 self.ordered_models_name = []
Matteo Scandolo8bbb03a2017-07-05 14:03:33 -0700146 # dictionary containing the saved model
147 self.saved_model_by_name = {}
Matteo Scandolo485b7132017-06-30 11:46:47 -0700148
149 self.ordered_models_template = []
150 self.recipe_file = TOSCA_RECIPES_DIR + '/tmp.yaml'
151
Matteo Scandolodf2600b2017-07-05 17:01:29 -0700152 self.recipe = recipe
153
154 def execute(self):
Matteo Scandolo78ca3eb2017-07-13 16:58:22 -0700155
Matteo Scandolo485b7132017-06-30 11:46:47 -0700156 try:
157 # [] save the recipe to a tmp file
Matteo Scandolodf2600b2017-07-05 17:01:29 -0700158 self.save_recipe_to_tmp_file(self.recipe)
Matteo Scandolo485b7132017-06-30 11:46:47 -0700159 # [] parse the recipe with TOSCA Parse
160 self.template = ToscaTemplate(self.recipe_file)
161 # [] get all models in the recipe
162 self.templates_by_model_name = self.get_tosca_models_by_name(self.template)
163 # [] compute requirements
164 self.compute_dependencies(self.template, self.templates_by_model_name)
165 # [] topsort requirements
166 self.ordered_models_name = self.topsort_dependencies(self.templates_by_model_name)
167 # [] topsort templates
168 self.ordered_models_template = self.get_ordered_models_template(self.ordered_models_name, self.templates_by_model_name)
169
170 for recipe in self.ordered_models_template:
171 # get properties from tosca
172 data = recipe.templates[recipe.name]['properties']
173 # [] get model by class name
174 class_name = recipe.type.replace("tosca.nodes.", "")
Matteo Scandolo21dde412017-07-11 18:54:12 -0700175 model = GRPCModelsAccessor.get_model_from_classname(class_name, data, self.username, self.password)
Matteo Scandolod12be212017-07-07 10:44:34 -0700176 # [] populate model with data
Matteo Scandolo485b7132017-06-30 11:46:47 -0700177 model = self.populate_model(model, data)
178 # [] check if the model has requirements
179 # [] if it has populate them
Matteo Scandolo8bbb03a2017-07-05 14:03:33 -0700180 model = self.populate_dependencies(model, recipe.requirements, self.saved_model_by_name)
Matteo Scandolo78ca3eb2017-07-13 16:58:22 -0700181 # [] save, update or delete
182 if self.delete and not model.is_new:
183 model.delete()
184 elif not self.delete:
185 model.save()
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700186
Matteo Scandolo8bbb03a2017-07-05 14:03:33 -0700187 self.saved_model_by_name[recipe.name] = model
188
Matteo Scandolod12be212017-07-07 10:44:34 -0700189 except ValidationError as e:
Matteo Scandolo485b7132017-06-30 11:46:47 -0700190 if e.message:
Matteo Scandolod12be212017-07-07 10:44:34 -0700191 exception_msg = TOSCA_Parser._translate_exception(e.message)
Matteo Scandolo485b7132017-06-30 11:46:47 -0700192 else:
Matteo Scandolod12be212017-07-07 10:44:34 -0700193 exception_msg = TOSCA_Parser._translate_exception(str(e))
Matteo Scandolo485b7132017-06-30 11:46:47 -0700194 raise Exception(exception_msg)
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700195
Matteo Scandolo21dde412017-07-11 18:54:12 -0700196 except _Rendezvous, e:
197 try:
198 exception_msg = json.loads(e._state.details)["error"]
199 except Exception:
200 exception_msg = e._state.details
201 raise Exception(exception_msg)
202 except Exception, e:
203 raise e
204
Matteo Scandolo9ce18252017-06-22 10:48:25 -0700205