| # Copyright 2019 AT&T Intellectual Property. All rights reserved. |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| from robot.api.deco import keyword |
| |
| import json |
| import yaml |
| import requests |
| |
| stack_url = "{}/stacks/{}" |
| stack_resources_url = "{}/stacks/{}/resources" |
| |
| |
| # use this to import and run validation from robot |
| class HeatVNFValidation: |
| def __init__(self): |
| pass |
| |
| @keyword |
| def validate(self, orchestration_url, token, manifest, vnf_name): |
| validator = StackValidation(orchestration_url, token, manifest, vnf_name) |
| validator.create_summary() |
| validator.validate_summary() |
| |
| return validator.report |
| |
| |
| class StackValidation: |
| def __init__(self, orchestration_url, token, manifest, vnf_name): |
| """retrieves stack and template details, and creates |
| a report for submission to OVP portal. |
| |
| :orchestration_url heat service endpoint in openstack |
| :token keystone auth token |
| :manifest json that contains list of heat templates, env, |
| preloads, and stack names for each module in |
| a VNF |
| """ |
| self.modules = [] |
| self.url = orchestration_url |
| self.token = token |
| self.manifest = manifest |
| self.vnf_name = vnf_name |
| self.report = {} |
| |
| self.load_manifest() |
| |
| def load_manifest(self): |
| for entry in self.manifest: |
| template = entry.get("template_name") |
| env_file = template.replace(".yaml", ".env").replace(".yml", ".env") |
| preload = entry.get("preload_name") |
| stack = entry.get("stack_name") |
| module = HeatModule( |
| template, |
| env_file, |
| stack, |
| preload |
| ) |
| module.get_data(self.url, self.token) |
| self.modules.append(module) |
| |
| def create_summary(self): |
| """creates a report dictionary to compare stack |
| resources, parameters, outputs w/ template""" |
| self.report["modules"] = [] |
| self.report["VNF Name"] = self.vnf_name |
| for module in self.modules: |
| stack = module.stack |
| preload = module.preload |
| template = module.template |
| |
| module_report = {} |
| module_report["stack_details"] = stack.stack_details |
| |
| module_report["resources"] = {} |
| module_report["resources"]["summary"] = "" |
| |
| module_report["parameters"] = {} |
| module_report["parameters"]["summary"] = "" |
| |
| module_report["outputs"] = {} |
| module_report["outputs"]["summary"] = "" |
| |
| module_report["resources"]["stack_resources"] = stack.resources |
| module_report["resources"]["template_resources"] = template.resources |
| |
| module_report["parameters"]["stack_parameters"] = stack.parameters |
| module_report["parameters"]["template_parameters"] = dict(template.parameters, **preload.parameters) |
| |
| module_report["outputs"]["stack_outputs"] = stack.outputs |
| module_report["outputs"]["template_outputs"] = template.outputs |
| |
| self.report["modules"].append(module_report) |
| |
| def validate_summary(self): |
| # validates resources, parameters, and outputs |
| self.validate_resources() |
| self.validate_parameters() |
| self.validate_outputs() |
| |
| self.report["summary"] = "SUCCESS" |
| for module in self.report["modules"]: |
| if module["resources"]["summary"] != "SUCCESS": |
| self.report["summary"] = "FAILED" |
| break |
| if module["parameters"]["summary"] != "SUCCESS": |
| self.report["summary"] = "FAILED" |
| break |
| if module["outputs"]["summary"] != "SUCCESS": |
| self.report["summary"] = "FAILED" |
| break |
| |
| def validate_resources(self): |
| """validates that all resources from a heat template |
| are present in instantiated heat stack""" |
| report = self.report |
| for module in report["modules"]: |
| module["resources"]["summary"] = "SUCCESS" |
| resources = module.get("resources", {}) |
| template_resources = resources.get("template_resources", []) |
| stack_resources = resources.get("stack_resources", []) |
| |
| if len(stack_resources) != len(template_resources): |
| module["resources"]["summary"] = "FAILED" |
| continue |
| |
| stack_rids = [] |
| for s_resource in stack_resources: |
| stack_rids.append(s_resource.get("resource_id")) |
| |
| template_rids = [] |
| for t_resource in template_resources: |
| template_rids.append(t_resource.get("resource_id")) |
| |
| if stack_rids.sort() != template_rids.sort(): |
| module["resources"]["summary"] = "FAILED" |
| continue |
| |
| def validate_parameters(self): |
| """validates that parameter name/value from template |
| == values from instantiated heat stack""" |
| report = self.report |
| for module in report["modules"]: |
| module["parameters"]["summary"] = "SUCCESS" |
| parameters = module.get("parameters", {}) |
| template_parameters = parameters.get("template_parameters", {}) |
| stack_parameters = parameters.get("stack_parameters", {}) |
| |
| for parameter, parameter_value in template_parameters.items(): |
| stack_parameter = stack_parameters.get(parameter) |
| if not stack_parameter: |
| module["parameters"]["summary"] = "FAILED" |
| break |
| |
| if stack_parameter != parameter_value: |
| module["parameters"]["summary"] = "FAILED" |
| break |
| |
| def validate_outputs(self): |
| """validates that all outputs from a heat template |
| are present in instantiated heat stack""" |
| report = self.report |
| for module in report["modules"]: |
| module["outputs"]["summary"] = "SUCCESS" |
| outputs = module.get("outputs", {}) |
| template_outputs = outputs.get("template_outputs", {}) |
| stack_outputs = outputs.get("stack_outputs", []) |
| |
| for output in stack_outputs: |
| output_key = output.get("output_key") |
| if output_key not in template_outputs: |
| module["outputs"]["summary"] = "FAILED" |
| break |
| |
| |
| class HeatModule: |
| def __init__(self, heat_template, environment_file, stack_name, preload): |
| """ |
| creates module object that has stack, preload, and template objects |
| |
| :heat_template /path/to/heat/template.yaml |
| :environment_file /path/to/heat/env.env |
| :preload /path/to/preloads/file.json |
| :stack_name name of heat stack in openstack |
| """ |
| self.stack = HeatStack(stack_name) |
| self.template = HeatTemplate(heat_template, environment_file) |
| self.preload = HeatPreload(preload) |
| |
| def get_data(self, url, token): |
| self.stack.get_data(url, token) |
| self.template.get_data() |
| self.preload.get_data() |
| |
| |
| class HeatTemplate: |
| def __init__(self, heat_template, environment_file): |
| """ |
| creates template object that holds template resources, |
| parameters, and outputs of a heat template/env pair. |
| |
| :heat_template /path/to/heat/template.yaml |
| :environment_file /path/to/heat/env.env |
| """ |
| self.template = heat_template |
| self.env = environment_file |
| self.resources = [] |
| self.parameters = {} |
| self.outputs = [] |
| |
| def get_data(self): |
| with open(self.template, "r") as f: |
| ydata = yaml.safe_load(f) |
| |
| resources = ydata.get("resources", {}) |
| |
| for rid, resource in resources.items(): |
| self.resources.append( |
| {"resource_id": rid, "resource_type": resource.get("type", "")} |
| ) |
| |
| outputs = ydata.get("outputs", {}) |
| |
| for output, output_value in outputs.items(): |
| self.outputs.append(output) |
| |
| with open(self.env, "r") as f: |
| ydata = yaml.safe_load(f) |
| |
| self.parameters = ydata.get("parameters", {}) |
| |
| |
| class HeatPreload: |
| def __init__(self, preload): |
| """ |
| creates preload object that holds parameter name/values |
| |
| :preload /path/to/preloads/file.json |
| """ |
| self.preload = preload |
| self.parameters = {} |
| |
| def get_data(self): |
| with open(self.preload, "r") as f: |
| jdata = json.loads(f.read()) |
| |
| # get parameters regardless of API version |
| |
| vnf_api_parameters = ( |
| jdata.get("input", {}) |
| .get("vnf-topology-information", {}) |
| .get("vnf-parameters", []) |
| ) |
| |
| for parameter in vnf_api_parameters: |
| p_name = parameter.get("vnf-parameter-name") |
| p_value = parameter.get("vnf-parameter-value") |
| self.parameters[p_name] = p_value |
| |
| gr_api_parameters = ( |
| jdata.get("input", {}) |
| .get("preload-vf-module-topology-information", {}) |
| .get("vf-module-topology", {}) |
| .get("vf-module-parameters", {}) |
| .get("param", []) |
| ) |
| |
| for parameter in gr_api_parameters: |
| p_name = parameter.get("name") |
| p_value = parameter.get("value") |
| self.parameters[p_name] = p_value |
| |
| |
| class HeatStack: |
| def __init__(self, stack_name): |
| """ |
| creates stack object that hold stack resources, |
| parameters, and outputs |
| |
| :stack_name name of heat stack in openstack |
| """ |
| self.stack_name = stack_name |
| self.resources = [] |
| self.parameters = {} |
| self.outputs = [] |
| self.status = "" |
| self.stack_details = {} |
| |
| def get_data(self, orchestration_url, token): |
| url = stack_url.format(orchestration_url, self.stack_name) |
| r = requests.get(headers={"X-Auth-Token": token}, url=url) |
| |
| if r.status_code == 200: |
| response = r.json() |
| self.parameters = response.get("stack", {}).get("parameters", {}) |
| self.outputs = response.get("stack", {}).get("outputs", {}) |
| self.status = response.get("stack", {}).get("stack_status", "") |
| self.stack_details = response.get("stack", {}) |
| |
| url = stack_resources_url.format(orchestration_url, self.stack_name) |
| r = requests.get(headers={"X-Auth-Token": token}, url=url) |
| if r.status_code == 200: |
| response = r.json() |
| resources = response.get("resources", []) |
| for resource in resources: |
| self.resources.append( |
| { |
| "resource_id": resource.get("resource_name"), |
| "resource_type": resource.get("resource_type"), |
| "resource_status": resource.get("resource_status"), |
| } |
| ) |