1 # Copyright 2019 AT&T Intellectual Property. All rights reserved.
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
7 # http://www.apache.org/licenses/LICENSE-2.0
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
20 stack_url = "{}/stacks/{}"
21 stack_resources_url = "{}/stacks/{}/resources"
24 # use this to import and run validation from robot
25 class HeatVNFValidation:
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()
35 return validator.report
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.
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
50 self.url = orchestration_url
52 self.manifest = manifest
53 self.vnf_name = vnf_name
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")
70 module.get_data(self.url, self.token)
71 self.modules.append(module)
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:
80 preload = module.preload
81 template = module.template
84 module_report["stack_details"] = stack.stack_details
86 module_report["resources"] = {}
87 module_report["resources"]["summary"] = ""
89 module_report["parameters"] = {}
90 module_report["parameters"]["summary"] = ""
92 module_report["outputs"] = {}
93 module_report["outputs"]["summary"] = ""
95 module_report["resources"]["stack_resources"] = stack.resources
96 module_report["resources"]["template_resources"] = template.resources
98 module_report["parameters"]["stack_parameters"] = stack.parameters
99 module_report["parameters"]["template_parameters"] = dict(template.parameters, **preload.parameters)
101 module_report["outputs"]["stack_outputs"] = stack.outputs
102 module_report["outputs"]["template_outputs"] = template.outputs
104 self.report["modules"].append(module_report)
106 def validate_summary(self):
107 # validates resources, parameters, and outputs
108 self.validate_resources()
109 self.validate_parameters()
110 self.validate_outputs()
112 self.report["summary"] = "SUCCESS"
113 for module in self.report["modules"]:
114 if module["resources"]["summary"] != "SUCCESS":
115 self.report["summary"] = "FAILED"
117 if module["parameters"]["summary"] != "SUCCESS":
118 self.report["summary"] = "FAILED"
120 if module["outputs"]["summary"] != "SUCCESS":
121 self.report["summary"] = "FAILED"
124 def validate_resources(self):
125 """validates that all resources from a heat template
126 are present in instantiated heat stack"""
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", [])
134 if len(stack_resources) != len(template_resources):
135 module["resources"]["summary"] = "FAILED"
139 for s_resource in stack_resources:
140 stack_rids.append(s_resource.get("resource_id"))
143 for t_resource in template_resources:
144 template_rids.append(t_resource.get("resource_id"))
146 if stack_rids.sort() != template_rids.sort():
147 module["resources"]["summary"] = "FAILED"
150 def validate_parameters(self):
151 """validates that parameter name/value from template
152 == values from instantiated heat stack"""
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", {})
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"
166 if stack_parameter != parameter_value:
167 module["parameters"]["summary"] = "FAILED"
170 def validate_outputs(self):
171 """validates that all outputs from a heat template
172 are present in instantiated heat stack"""
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", [])
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"
188 def __init__(self, heat_template, environment_file, stack_name, preload):
190 creates module object that has stack, preload, and template objects
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
197 self.stack = HeatStack(stack_name)
198 self.template = HeatTemplate(heat_template, environment_file)
199 self.preload = HeatPreload(preload)
201 def get_data(self, url, token):
202 self.stack.get_data(url, token)
203 self.template.get_data()
204 self.preload.get_data()
208 def __init__(self, heat_template, environment_file):
210 creates template object that holds template resources,
211 parameters, and outputs of a heat template/env pair.
213 :heat_template /path/to/heat/template.yaml
214 :environment_file /path/to/heat/env.env
216 self.template = heat_template
217 self.env = environment_file
223 with open(self.template, "r") as f:
224 ydata = yaml.safe_load(f)
226 resources = ydata.get("resources", {})
228 for rid, resource in resources.items():
229 self.resources.append(
230 {"resource_id": rid, "resource_type": resource.get("type", "")}
233 outputs = ydata.get("outputs", {})
235 for output, output_value in outputs.items():
236 self.outputs.append(output)
238 with open(self.env, "r") as f:
239 ydata = yaml.safe_load(f)
241 self.parameters = ydata.get("parameters", {})
245 def __init__(self, preload):
247 creates preload object that holds parameter name/values
249 :preload /path/to/preloads/file.json
251 self.preload = preload
255 with open(self.preload, "r") as f:
256 jdata = json.loads(f.read())
258 # get parameters regardless of API version
260 vnf_api_parameters = (
261 jdata.get("input", {})
262 .get("vnf-topology-information", {})
263 .get("vnf-parameters", [])
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
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", {})
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
286 def __init__(self, stack_name):
288 creates stack object that hold stack resources,
289 parameters, and outputs
291 :stack_name name of heat stack in openstack
293 self.stack_name = stack_name
298 self.stack_details = {}
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)
304 if r.status_code == 200:
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", {})
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:
315 resources = response.get("resources", [])
316 for resource in resources:
317 self.resources.append(
319 "resource_id": resource.get("resource_name"),
320 "resource_type": resource.get("resource_type"),
321 "resource_status": resource.get("resource_status"),