Update INFO.yaml with new PTL
[testsuite/python-testing-utils.git] / robotframework-onap / ONAPLibrary / HeatVNFValidation.py
1 # Copyright 2019 AT&T Intellectual Property. All rights reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14 from robot.api.deco import keyword
15
16 import json
17 import yaml
18 import requests
19
20 stack_url = "{}/stacks/{}"
21 stack_resources_url = "{}/stacks/{}/resources"
22
23
24 # use this to import and run validation from robot
25 class HeatVNFValidation:
26     def __init__(self):
27         pass
28
29     @keyword
30     def validate(self, orchestration_url, token, manifest, vnf_name):
31         validator = StackValidation(orchestration_url, token, manifest, vnf_name)
32         validator.create_summary()
33         validator.validate_summary()
34
35         return validator.report
36
37
38 class StackValidation:
39     def __init__(self, orchestration_url, token, manifest, vnf_name):
40         """retrieves stack and template details, and creates
41         a report for submission to OVP portal.
42
43         :orchestration_url          heat service endpoint in openstack
44         :token                      keystone auth token
45         :manifest                   json that contains list of heat templates, env,
46                                     preloads, and stack names for each module in
47                                     a VNF
48         """
49         self.modules = []
50         self.url = orchestration_url
51         self.token = token
52         self.manifest = manifest
53         self.vnf_name = vnf_name
54         self.report = {}
55
56         self.load_manifest()
57
58     def load_manifest(self):
59         for entry in self.manifest:
60             template = entry.get("template_name")
61             env_file = template.replace(".yaml", ".env").replace(".yml", ".env")
62             preload = entry.get("preload_name")
63             stack = entry.get("stack_name")
64             module = HeatModule(
65                 template,
66                 env_file,
67                 stack,
68                 preload
69             )
70             module.get_data(self.url, self.token)
71             self.modules.append(module)
72
73     def create_summary(self):
74         """creates a report dictionary to compare stack
75         resources, parameters, outputs w/ template"""
76         self.report["modules"] = []
77         self.report["VNF Name"] = self.vnf_name
78         for module in self.modules:
79             stack = module.stack
80             preload = module.preload
81             template = module.template
82
83             module_report = {}
84             module_report["stack_details"] = stack.stack_details
85
86             module_report["resources"] = {}
87             module_report["resources"]["summary"] = ""
88
89             module_report["parameters"] = {}
90             module_report["parameters"]["summary"] = ""
91
92             module_report["outputs"] = {}
93             module_report["outputs"]["summary"] = ""
94
95             module_report["resources"]["stack_resources"] = stack.resources
96             module_report["resources"]["template_resources"] = template.resources
97
98             module_report["parameters"]["stack_parameters"] = stack.parameters
99             module_report["parameters"]["template_parameters"] = dict(template.parameters, **preload.parameters)
100
101             module_report["outputs"]["stack_outputs"] = stack.outputs
102             module_report["outputs"]["template_outputs"] = template.outputs
103
104             self.report["modules"].append(module_report)
105
106     def validate_summary(self):
107         # validates resources, parameters, and outputs
108         self.validate_resources()
109         self.validate_parameters()
110         self.validate_outputs()
111
112         self.report["summary"] = "SUCCESS"
113         for module in self.report["modules"]:
114             if module["resources"]["summary"] != "SUCCESS":
115                 self.report["summary"] = "FAILED"
116                 break
117             if module["parameters"]["summary"] != "SUCCESS":
118                 self.report["summary"] = "FAILED"
119                 break
120             if module["outputs"]["summary"] != "SUCCESS":
121                 self.report["summary"] = "FAILED"
122                 break
123
124     def validate_resources(self):
125         """validates that all resources from a heat template
126         are present in instantiated heat stack"""
127         report = self.report
128         for module in report["modules"]:
129             module["resources"]["summary"] = "SUCCESS"
130             resources = module.get("resources", {})
131             template_resources = resources.get("template_resources", [])
132             stack_resources = resources.get("stack_resources", [])
133
134             if len(stack_resources) != len(template_resources):
135                 module["resources"]["summary"] = "FAILED"
136                 continue
137
138             stack_rids = []
139             for s_resource in stack_resources:
140                 stack_rids.append(s_resource.get("resource_id"))
141
142             template_rids = []
143             for t_resource in template_resources:
144                 template_rids.append(t_resource.get("resource_id"))
145
146             if stack_rids.sort() != template_rids.sort():
147                 module["resources"]["summary"] = "FAILED"
148                 continue
149
150     def validate_parameters(self):
151         """validates that parameter name/value from template
152         == values from instantiated heat stack"""
153         report = self.report
154         for module in report["modules"]:
155             module["parameters"]["summary"] = "SUCCESS"
156             parameters = module.get("parameters", {})
157             template_parameters = parameters.get("template_parameters", {})
158             stack_parameters = parameters.get("stack_parameters", {})
159
160             for parameter, parameter_value in template_parameters.items():
161                 stack_parameter = stack_parameters.get(parameter)
162                 if not stack_parameter:
163                     module["parameters"]["summary"] = "FAILED"
164                     break
165
166                 if stack_parameter != parameter_value:
167                     module["parameters"]["summary"] = "FAILED"
168                     break
169
170     def validate_outputs(self):
171         """validates that all outputs from a heat template
172         are present in instantiated heat stack"""
173         report = self.report
174         for module in report["modules"]:
175             module["outputs"]["summary"] = "SUCCESS"
176             outputs = module.get("outputs", {})
177             template_outputs = outputs.get("template_outputs", {})
178             stack_outputs = outputs.get("stack_outputs", [])
179
180             for output in stack_outputs:
181                 output_key = output.get("output_key")
182                 if output_key not in template_outputs:
183                     module["outputs"]["summary"] = "FAILED"
184                     break
185
186
187 class HeatModule:
188     def __init__(self, heat_template, environment_file, stack_name, preload):
189         """
190         creates module object that has stack, preload, and template objects
191
192         :heat_template             /path/to/heat/template.yaml
193         :environment_file          /path/to/heat/env.env
194         :preload                   /path/to/preloads/file.json
195         :stack_name                name of heat stack in openstack
196         """
197         self.stack = HeatStack(stack_name)
198         self.template = HeatTemplate(heat_template, environment_file)
199         self.preload = HeatPreload(preload)
200
201     def get_data(self, url, token):
202         self.stack.get_data(url, token)
203         self.template.get_data()
204         self.preload.get_data()
205
206
207 class HeatTemplate:
208     def __init__(self, heat_template, environment_file):
209         """
210         creates template object that holds template resources,
211         parameters, and outputs of a heat template/env pair.
212
213         :heat_template             /path/to/heat/template.yaml
214         :environment_file          /path/to/heat/env.env
215         """
216         self.template = heat_template
217         self.env = environment_file
218         self.resources = []
219         self.parameters = {}
220         self.outputs = []
221
222     def get_data(self):
223         with open(self.template, "r") as f:
224             ydata = yaml.safe_load(f)
225
226         resources = ydata.get("resources", {})
227
228         for rid, resource in resources.items():
229             self.resources.append(
230                 {"resource_id": rid, "resource_type": resource.get("type", "")}
231             )
232
233         outputs = ydata.get("outputs", {})
234
235         for output, output_value in outputs.items():
236             self.outputs.append(output)
237
238         with open(self.env, "r") as f:
239             ydata = yaml.safe_load(f)
240
241         self.parameters = ydata.get("parameters", {})
242
243
244 class HeatPreload:
245     def __init__(self, preload):
246         """
247         creates preload object that holds parameter name/values
248
249         :preload             /path/to/preloads/file.json
250         """
251         self.preload = preload
252         self.parameters = {}
253
254     def get_data(self):
255         with open(self.preload, "r") as f:
256             jdata = json.loads(f.read())
257
258         # get parameters regardless of API version
259
260         vnf_api_parameters = (
261             jdata.get("input", {})
262             .get("vnf-topology-information", {})
263             .get("vnf-parameters", [])
264         )
265
266         for parameter in vnf_api_parameters:
267             p_name = parameter.get("vnf-parameter-name")
268             p_value = parameter.get("vnf-parameter-value")
269             self.parameters[p_name] = p_value
270
271         gr_api_parameters = (
272             jdata.get("input", {})
273             .get("preload-vf-module-topology-information", {})
274             .get("vf-module-topology", {})
275             .get("vf-module-parameters", {})
276             .get("param", [])
277         )
278
279         for parameter in gr_api_parameters:
280             p_name = parameter.get("name")
281             p_value = parameter.get("value")
282             self.parameters[p_name] = p_value
283
284
285 class HeatStack:
286     def __init__(self, stack_name):
287         """
288         creates stack object that hold stack resources,
289         parameters, and outputs
290
291         :stack_name             name of heat stack in openstack
292         """
293         self.stack_name = stack_name
294         self.resources = []
295         self.parameters = {}
296         self.outputs = []
297         self.status = ""
298         self.stack_details = {}
299
300     def get_data(self, orchestration_url, token):
301         url = stack_url.format(orchestration_url, self.stack_name)
302         r = requests.get(headers={"X-Auth-Token": token}, url=url)
303
304         if r.status_code == 200:
305             response = r.json()
306             self.parameters = response.get("stack", {}).get("parameters", {})
307             self.outputs = response.get("stack", {}).get("outputs", {})
308             self.status = response.get("stack", {}).get("stack_status", "")
309             self.stack_details = response.get("stack", {})
310
311         url = stack_resources_url.format(orchestration_url, self.stack_name)
312         r = requests.get(headers={"X-Auth-Token": token}, url=url)
313         if r.status_code == 200:
314             response = r.json()
315             resources = response.get("resources", [])
316             for resource in resources:
317                 self.resources.append(
318                     {
319                         "resource_id": resource.get("resource_name"),
320                         "resource_type": resource.get("resource_type"),
321                         "resource_status": resource.get("resource_status"),
322                     }
323                 )