diff options
Diffstat (limited to 'test')
-rwxr-xr-x | test/vcpe/config_sdnc_so.py | 89 | ||||
-rwxr-xr-x | test/vcpe/csar_parser.py | 231 | ||||
-rwxr-xr-x | test/vcpe/get_info.py | 26 | ||||
-rwxr-xr-x | test/vcpe/healthcheck.py | 30 | ||||
-rwxr-xr-x | test/vcpe/loop.py | 37 | ||||
-rwxr-xr-x | test/vcpe/preload.py | 216 | ||||
-rwxr-xr-x | test/vcpe/soutils.py | 318 | ||||
-rwxr-xr-x | test/vcpe/vcpe.py | 207 | ||||
-rwxr-xr-x | test/vcpe/vcpe_custom_service.py | 80 | ||||
-rwxr-xr-x | test/vcpe/vcpecommon.py | 414 |
10 files changed, 1648 insertions, 0 deletions
diff --git a/test/vcpe/config_sdnc_so.py b/test/vcpe/config_sdnc_so.py new file mode 100755 index 000000000..660c70eb4 --- /dev/null +++ b/test/vcpe/config_sdnc_so.py @@ -0,0 +1,89 @@ +#! /usr/bin/python + +import logging +from vcpecommon import * +import csar_parser + + +def insert_customer_service_to_sdnc(vcpecommon): + """ + INSERT INTO SERVICE_MODEL (`service_uuid`, `model_yaml`,`invariant_uuid`,`version`,`name`,`description`,`type`,`category`,`ecomp_naming`,`service_instance_name_prefix`,`filename`,`naming_policy`) values ('7e319b6f-e710-440e-bbd2-63c1004949ef', null, 'a99ace8a-6e3b-447d-b2ff-4614e4234eea',null,'vCPEService', 'vCPEService', 'Service','Network L1-3', 'N', 'vCPEService', 'vCpeResCust110701/service-Vcperescust110701-template.yml',null); + INSERT INTO ALLOTTED_RESOURCE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`,`naming_policy`,`ecomp_generated_naming`,`depending_service`,`role`,`type`,`service_dependency`,`allotted_resource_type`) VALUES ( '7e40b664-d7bf-47ad-8f7c-615631d53cd7', NULL, 'f51b0aae-e24a-4cff-b190-fe3daf3d15ee', 'f3137496-1605-40e9-b6df-64aa0f8e91a0', '1.0', NULL,'Y',NULL,NULL,'TunnelXConnect',NULL, 'TunnelXConnect'); + INSERT INTO ALLOTTED_RESOURCE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`,`naming_policy`,`ecomp_generated_naming`,`depending_service`,`role`,`type`,`service_dependency`,`allotted_resource_type`) VALUES ( 'e46097e1-6a0c-4cf3-a7e5-c39ed407e65e', NULL, 'aa60f6ba-541b-48d6-a5ff-3b0e1f0ad9bf', '0e157d52-b945-422f-b3a8-ab685c2be079', '1.0', NULL,'Y',NULL,NULL,'BRG',NULL, 'TunnelXConnect'); + INSERT INTO VF_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`,`name`,`naming_policy`,`ecomp_generated_naming`,`avail_zone_max_count`,`nf_function`,`nf_code`,`nf_type`,`nf_role`,`vendor`,`vendor_version`) VALUES ( '3768afa5-cf9e-4071-bb25-3a2e2628dd87', NULL, '5f56893b-d026-4672-b785-7f5ffeb498c6', '7cf28b23-5719-485b-9ab4-dae1a2fa0e07', '1.0', 'vspvgw111601',NULL,'Y',1,NULL,NULL,NULL,NULL,'vCPE','1.0'); + INSERT INTO VF_MODULE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`,`vf_module_type`,`availability_zone_count`,`ecomp_generated_vm_assignments`) VALUES ( '17a9c7d1-6f8e-4930-aa83-0d323585184f', NULL, 'd772ddd1-7623-40b4-a2a5-ec287916cb51', '6e1a0652-f5e9-4caa-bff8-39bf0c8589a3', '1.0', 'Base',NULL,NULL); + + :return: + """ + logger = logging.getLogger('__name__') + logger.info('Inserting customer service data to SDNC DB') + csar_file = vcpecommon.find_file('rescust', 'csar', 'csar') + parser = csar_parser.CsarParser() + parser.parse_csar(csar_file) + cmds = [] + cmds.append("INSERT INTO SERVICE_MODEL (`service_uuid`, `model_yaml`,`invariant_uuid`,`version`,`name`," \ + "`description`,`type`,`category`,`ecomp_naming`,`service_instance_name_prefix`,`filename`," \ + "`naming_policy`) values ('{0}', null, '{1}',null,'{2}', 'vCPEService', 'Service','Network L1-3'," \ + "'N', 'vCPEService', '{3}/{4}',null);".format(parser.svc_model['modelVersionId'], + parser.svc_model['modelInvariantId'], + parser.svc_model['modelName'], + parser.svc_model['modelName'], + parser.svc_model['modelName'])) + + for model in parser.vnf_models: + if 'tunnel' in model['modelCustomizationName'].lower() or 'brg' in model['modelCustomizationName'].lower(): + cmds.append("INSERT INTO ALLOTTED_RESOURCE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`," \ + "`uuid`,`version`,`naming_policy`,`ecomp_generated_naming`,`depending_service`,`role`,`type`," \ + "`service_dependency`,`allotted_resource_type`) VALUES ('{0}',NULL,'{1}','{2}','1.0'," \ + "NULL,'Y', NULL,NULL,'TunnelXConnect'," \ + "NULL, 'TunnelXConnect');".format(model['modelCustomizationId'], model['modelInvariantId'], + model['modelVersionId'])) + else: + cmds.append("INSERT INTO VF_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`," \ + "`name`,`naming_policy`,`ecomp_generated_naming`,`avail_zone_max_count`,`nf_function`," \ + "`nf_code`,`nf_type`,`nf_role`,`vendor`,`vendor_version`) VALUES ('{0}',NULL,'{1}','{2}'," \ + "'1.0', '{3}',NULL,'Y',1,NULL,NULL,NULL,NULL,'vCPE'," \ + "'1.0');".format(model['modelCustomizationId'], model['modelInvariantId'], + model['modelVersionId'], model['modelCustomizationName'].split()[0])) + + model = parser.vfmodule_models[0] + cmds.append("INSERT INTO VF_MODULE_MODEL (`customization_uuid`,`model_yaml`,`invariant_uuid`,`uuid`,`version`," \ + "`vf_module_type`,`availability_zone_count`,`ecomp_generated_vm_assignments`) VALUES ('{0}', NULL," \ + "'{1}', '{2}', '1.0', 'Base',NULL,NULL)" \ + ";".format(model['modelCustomizationId'], model['modelInvariantId'], model['modelVersionId'])) + print('\n'.join(cmds)) + vcpecommon.insert_into_sdnc_db(cmds) + + +def insert_customer_service_to_so(vcpecommon): + logger = logging.getLogger('__name__') + logger.info('Inserting neutron HEAT template to SO DB and creating a recipe for customer service') + csar_file = vcpecommon.find_file('rescust', 'csar', 'csar') + parser = csar_parser.CsarParser() + parser.parse_csar(csar_file) + cmds = [] + cmds.append("INSERT INTO `service_recipe` (`ACTION`, `VERSION_STR`, `DESCRIPTION`, `ORCHESTRATION_URI`, " \ + "`SERVICE_PARAM_XSD`, `RECIPE_TIMEOUT`, `SERVICE_TIMEOUT_INTERIM`, `CREATION_TIMESTAMP`, " \ + "`SERVICE_MODEL_UUID`) VALUES ('createInstance','1','{0}'," \ + "'/mso/async/services/CreateVcpeResCustService',NULL,181,NULL, NOW()," \ + "'{1}');".format(parser.svc_model['modelName'], parser.svc_model['modelVersionId'])) + + cmds.append("delete from `heat_template_params` where" + "`HEAT_TEMPLATE_ARTIFACT_UUID`='efee1d84-b8ec-11e7-abc4-cec278b6b50a';") + cmds.append("delete from `heat_template` where ARTIFACT_UUID='efee1d84-b8ec-11e7-abc4-cec278b6b50a';") + network_tempalte_file = vcpecommon.find_file('neutron', 'yaml', 'preload_templates') + with open(network_tempalte_file, 'r') as fin: + lines = fin.readlines() + longtext = '\n'.join(lines) + cmds.append("INSERT INTO `heat_template`(`ARTIFACT_UUID`, `NAME`, `VERSION`, `BODY`, `TIMEOUT_MINUTES`, " \ + "`DESCRIPTION`, `CREATION_TIMESTAMP`, `ARTIFACT_CHECKSUM`) VALUES(" \ + "'efee1d84-b8ec-11e7-abc4-cec278b6b50a', 'Generic NeutronNet', '1', '{0}', 10, " \ + "'Generic Neutron Template', NOW(), 'MANUAL RECORD');".format(longtext)) + + cmds.append("INSERT INTO `heat_template_params`(`HEAT_TEMPLATE_ARTIFACT_UUID`, `PARAM_NAME`, `IS_REQUIRED`, " \ + "`PARAM_TYPE`, `PARAM_ALIAS`) VALUES('efee1d84-b8ec-11e7-abc4-cec278b6b50a', 'shared', 0, " \ + "'string', NULL);") + + print('\n'.join(cmds)) + vcpecommon.insert_into_so_db(cmds) + diff --git a/test/vcpe/csar_parser.py b/test/vcpe/csar_parser.py new file mode 100755 index 000000000..f101364d5 --- /dev/null +++ b/test/vcpe/csar_parser.py @@ -0,0 +1,231 @@ +#! /usr/bin/python +import os +import zipfile +import shutil +import yaml +import json +import logging + + +class CsarParser: + def __init__(self): + self.logger = logging.getLogger(__name__) + self.svc_model = {} + self.net_models = [] # there could be multiple networks + self.vnf_models = [] # this version only support a single VNF in the service template + self.vfmodule_models = [] # this version only support a single VF module in the service template + + def get_service_yaml_from_csar(self, csar_file): + """ + :param csar_file: csar file path name, e.g. 'csar/vgmux.csar' + :return: + """ + tmpdir = './__tmp' + if os.path.isdir(tmpdir): + shutil.rmtree(tmpdir) + os.mkdir(tmpdir) + + with zipfile.ZipFile(csar_file, "r") as zip_ref: + zip_ref.extractall(tmpdir) + + yamldir = tmpdir + '/Definitions' + if os.path.isdir(yamldir): + for filename in os.listdir(yamldir): + # look for service template like this: service-Vcpesvcbrgemu111601-template.yml + if filename.startswith('service-') and filename.endswith('-template.yml'): + return os.path.join(yamldir, filename) + + self.logger.error('Invalid file: ' + csar_file) + return '' + + def get_service_model_info(self, svc_template): + """ extract service model info from yaml and convert to what to be used in SO request + Sample from yaml: + { + "UUID": "aed4fc5e-b871-4e26-8531-ceabd46df85e", + "category": "Network L1-3", + "description": "Infra service", + "ecompGeneratedNaming": true, + "invariantUUID": "c806682a-5b3a-44d8-9e88-0708be151296", + "name": "vcpesvcinfra111601", + "namingPolicy": "", + "serviceEcompNaming": true, + "serviceRole": "", + "serviceType": "", + "type": "Service" + }, + + Convert to + { + "modelType": "service", + "modelInvariantId": "ca4c7a70-06fd-45d8-8b9e-c9745d25bf2b", + "modelVersionId": "5d8911b4-e50c-4096-a81e-727a8157193c", + "modelName": "vcpesvcbrgemu111601", + "modelVersion": "1.0" + }, + + """ + if svc_template['metadata']['type'] != 'Service': + self.logger.error('csar error: metadata->type is not Service') + return + + metadata = svc_template['metadata'] + self.svc_model = { + 'modelType': 'service', + 'modelInvariantId': metadata['invariantUUID'], + 'modelVersionId': metadata['UUID'], + 'modelName': metadata['name'] + } + if 'version' in metadata: + self.svc_model['modelVersion'] = metadata['version'] + else: + self.svc_model['modelVersion'] = '1.0' + + def get_vnf_and_network_model_info(self, svc_template): + """ extract vnf and network model info from yaml and convert to what to be used in SO request + Sample from yaml: + "topology_template": { + "node_templates": { + "CPE_PUBLIC": { + "metadata": { + "UUID": "33b2c367-a165-4bb3-81c3-0150cd06ceff", + "category": "Generic", + "customizationUUID": "db1d4ac2-62cd-4e5d-b2dc-300dbd1a5da1", + "description": "Generic NeutronNet", + "invariantUUID": "3d4c0e47-4794-4e98-a794-baaced668930", + "name": "Generic NeutronNet", + "resourceVendor": "ATT (Tosca)", + "resourceVendorModelNumber": "", + "resourceVendorRelease": "1.0.0.wd03", + "subcategory": "Network Elements", + "type": "VL", + "version": "1.0" + }, + "type": "org.openecomp.resource.vl.GenericNeutronNet" + }, + Convert to + { + "modelType": "network", + "modelInvariantId": "3d4c0e47-4794-4e98-a794-baaced668930", + "modelVersionId": "33b2c367-a165-4bb3-81c3-0150cd06ceff", + "modelName": "Generic NeutronNet", + "modelVersion": "1.0", + "modelCustomizationId": "db1d4ac2-62cd-4e5d-b2dc-300dbd1a5da1", + "modelCustomizationName": "CPE_PUBLIC" + }, + """ + node_dic = svc_template['topology_template']['node_templates'] + for node_name, v in node_dic.items(): + model = { + 'modelInvariantId': v['metadata']['invariantUUID'], + 'modelVersionId': v['metadata']['UUID'], + 'modelName': v['metadata']['name'], + 'modelVersion': v['metadata']['version'], + 'modelCustomizationId': v['metadata']['customizationUUID'], + 'modelCustomizationName': node_name + } + + if v['type'].startswith('org.openecomp.resource.vl.GenericNeutronNet'): + # a neutron network is found + self.logger.info('Parser found a network: ' + node_name) + model['modelType'] = 'network' + self.net_models.append(model) + elif v['type'].startswith('org.openecomp.resource.vf.'): + # a VNF is found + self.logger.info('Parser found a VNF: ' + node_name) + model['modelType'] = 'vnf' + self.vnf_models.append(model) + else: + self.logger.warning('Parser found a node that is neither a network nor a VNF: ' + node_name) + + def get_vfmodule_model_info(self, svc_template): + """ extract network model info from yaml and convert to what to be used in SO request + Sample from yaml: + "topology_template": { + "groups": { + "vspinfra1116010..Vspinfra111601..base_vcpe_infra..module-0": { + "metadata": { + "vfModuleModelCustomizationUUID": "11ddac51-30e3-4a3f-92eb-2eb99c2cb288", + "vfModuleModelInvariantUUID": "02f70416-581e-4f00-bde1-d65e69af95c5", + "vfModuleModelName": "Vspinfra111601..base_vcpe_infra..module-0", + "vfModuleModelUUID": "88c78078-f1fd-4f73-bdd9-10420b0f6353", + "vfModuleModelVersion": "1" + }, + "properties": { + "availability_zone_count": null, + "initial_count": 1, + "max_vf_module_instances": 1, + "min_vf_module_instances": 1, + "vf_module_description": null, + "vf_module_label": "base_vcpe_infra", + "vf_module_type": "Base", + "vfc_list": null, + "volume_group": false + }, + "type": "org.openecomp.groups.VfModule" + } + }, + Convert to + { + "modelType": "vfModule", + "modelInvariantId": "02f70416-581e-4f00-bde1-d65e69af95c5", + "modelVersionId": "88c78078-f1fd-4f73-bdd9-10420b0f6353", + "modelName": "Vspinfra111601..base_vcpe_infra..module-0", + "modelVersion": "1", + "modelCustomizationId": "11ddac51-30e3-4a3f-92eb-2eb99c2cb288", + "modelCustomizationName": "Vspinfra111601..base_vcpe_infra..module-0" + }, + """ + node_dic = svc_template['topology_template']['groups'] + for node_name, v in node_dic.items(): + if v['type'].startswith('org.openecomp.groups.VfModule'): + model = { + 'modelType': 'vfModule', + 'modelInvariantId': v['metadata']['vfModuleModelInvariantUUID'], + 'modelVersionId': v['metadata']['vfModuleModelUUID'], + 'modelName': v['metadata']['vfModuleModelName'], + 'modelVersion': v['metadata']['vfModuleModelVersion'], + 'modelCustomizationId': v['metadata']['vfModuleModelCustomizationUUID'], + 'modelCustomizationName': v['metadata']['vfModuleModelName'] + } + self.vfmodule_models.append(model) + self.logger.info('Parser found a VF module: ' + model['modelCustomizationName']) + + def parse_service_yaml(self, filename): + # clean up + self.svc_model = {} + self.net_models = [] # there could be multiple networks + self.vnf_models = [] # this version only support a single VNF in the service template + self.vfmodule_models = [] # this version only support a single VF module in the service template + + svc_template = yaml.load(file(filename, 'r')) + self.get_service_model_info(svc_template) + self.get_vnf_and_network_model_info(svc_template) + self.get_vfmodule_model_info(svc_template) + + return True + + def parse_csar(self, csar_file): + yaml_file = self.get_service_yaml_from_csar(csar_file) + if yaml_file != '': + return self.parse_service_yaml(yaml_file) + + def print_models(self): + print('---------Service Model----------') + print(json.dumps(self.svc_model, indent=2, sort_keys=True)) + + print('---------Network Model(s)----------') + for model in self.net_models: + print(json.dumps(model, indent=2, sort_keys=True)) + + print('---------VNF Model(s)----------') + for model in self.vnf_models: + print(json.dumps(model, indent=2, sort_keys=True)) + + print('---------VF Module Model(s)----------') + for model in self.vfmodule_models: + print(json.dumps(model, indent=2, sort_keys=True)) + + def test(self): + self.parse_csar('csar/service-Vcpesvcinfra111601-csar.csar') + self.print_models() diff --git a/test/vcpe/get_info.py b/test/vcpe/get_info.py new file mode 100755 index 000000000..5b0c6879b --- /dev/null +++ b/test/vcpe/get_info.py @@ -0,0 +1,26 @@ +#! /usr/bin/python + +import time +import logging +import json +import mysql.connector +import ipaddress +import re +import sys +import base64 +from vcpecommon import * +import preload +import vcpe_custom_service + + +logging.basicConfig(level=logging.INFO, format='%(message)s') + +vcpecommon = VcpeCommon() +nodes=['brg', 'bng', 'mux', 'dhcp'] +hosts = vcpecommon.get_vm_ip(nodes) +print(json.dumps(hosts, indent=4, sort_keys=True)) + + + + + diff --git a/test/vcpe/healthcheck.py b/test/vcpe/healthcheck.py new file mode 100755 index 000000000..b94848e13 --- /dev/null +++ b/test/vcpe/healthcheck.py @@ -0,0 +1,30 @@ +#! /usr/bin/python + +import logging +import json +from vcpecommon import * +import commands + + +logging.basicConfig(level=logging.INFO, format='%(message)s') +common = VcpeCommon() + +print('Checking vGMUX REST API from SDNC') +cmd = 'curl -u admin:admin -X GET http://10.0.101.21:8183/restconf/config/ietf-interfaces:interfaces' +ret = commands.getstatusoutput("ssh -i onap_dev root@sdnc '{0}'".format(cmd)) +sz = ret[-1].split('\n')[-1] +print('\n') +print(sz) + +print('Checking vBRG REST API from SDNC') +cmd = 'curl -u admin:admin -X GET http://10.3.0.2:8183/restconf/config/ietf-interfaces:interfaces' +ret = commands.getstatusoutput("ssh -i onap_dev root@sdnc '{0}'".format(cmd)) +sz = ret[-1].split('\n')[-1] +print('\n') +print(sz) + +print('Checking SDNC DB for vBRG MAC address') +mac = common.get_brg_mac_from_sdnc() +print(mac) + + diff --git a/test/vcpe/loop.py b/test/vcpe/loop.py new file mode 100755 index 000000000..ad5879715 --- /dev/null +++ b/test/vcpe/loop.py @@ -0,0 +1,37 @@ +#! /usr/bin/python + +import time +import logging +import json +import mysql.connector +import ipaddress +import re +import sys +import base64 +from vcpecommon import * +import preload +import commands +import vcpe_custom_service + + +logging.basicConfig(level=logging.INFO, format='%(message)s') + +cpecommon = VcpeCommon() +custom = vcpe_custom_service.CustomService(cpecommon) + +nodes=['mux'] +hosts = cpecommon.get_vm_ip(nodes) + +custom.del_vgmux_ves_mode(hosts['mux']) +time.sleep(2) +custom.del_vgmux_ves_collector(hosts['mux']) +exit() + +time.sleep(2) +logging.info('Setting vGMUX DCAE collector IP address') +custom.set_vgmux_ves_collector(hosts['mux']) +time.sleep(2) +vgmux_vnf_name = cpecommon.load_object('vgmux_vnf_name') +logging.info('vGMUX VNF instance name is %s', vgmux_vnf_name) +logging.info('Letting vGMUX report packet loss to DCAE') +custom.set_vgmux_packet_loss_rate(hosts['mux'], 55, vgmux_vnf_name) diff --git a/test/vcpe/preload.py b/test/vcpe/preload.py new file mode 100755 index 000000000..c4efafde6 --- /dev/null +++ b/test/vcpe/preload.py @@ -0,0 +1,216 @@ +#! /usr/bin/python + +import requests +import json +import sys +from datetime import datetime +from vcpecommon import * +import csar_parser +import logging +import base64 + + +class Preload: + def __init__(self, vcpecommon): + self.logger = logging.getLogger(__name__) + self.vcpecommon = vcpecommon + + def replace(self, sz, replace_dict): + for old_string, new_string in replace_dict.items(): + sz = sz.replace(old_string, new_string) + if self.vcpecommon.template_variable_symbol in sz: + self.logger.error('Error! Cannot find a value to replace ' + sz) + return sz + + def generate_json(self, template_file, replace_dict): + with open(template_file) as json_input: + json_data = json.load(json_input) + stk = [json_data] + while len(stk) > 0: + data = stk.pop() + for k, v in data.items(): + if type(v) is dict: + stk.append(v) + elif type(v) is list: + stk.extend(v) + elif type(v) is str or type(v) is unicode: + if self.vcpecommon.template_variable_symbol in v: + data[k] = self.replace(v, replace_dict) + else: + self.logger.warning('Unexpected line in template: %s. Look for value %s', template_file, v) + return json_data + + def reset_sniro(self): + self.logger.debug('Clearing SNIRO data') + r = requests.post(self.vcpecommon.sniro_url + '/reset', headers=self.vcpecommon.sniro_headers) + if 2 != r.status_code / 100: + self.logger.debug(r.content) + self.logger.error('Clearing SNIRO date failed.') + sys.exit() + + def preload_sniro(self, template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, vbrg_ar_name, + vgmux_svc_instance_uuid, vbrg_svc_instance_uuid): + self.reset_sniro() + self.logger.info('Preloading SNIRO for homing service') + replace_dict = {'${tunnelxconn_ar_name}': tunnelxconn_ar_name, + '${vgw_name}': vgw_name, + '${brg_ar_name}': vbrg_ar_name, + '${vgmux_svc_instance_uuid}': vgmux_svc_instance_uuid, + '${vbrg_svc_instance_uuid}': vbrg_svc_instance_uuid + } + sniro_data = self.generate_json(template_sniro_data, replace_dict) + self.logger.debug('SNIRO data:') + self.logger.debug(json.dumps(sniro_data, indent=4, sort_keys=True)) + + base64_sniro_data = base64.b64encode(json.dumps(sniro_data)) + self.logger.debug('SNIRO data: 64') + self.logger.debug(base64_sniro_data) + replace_dict = {'${base64_sniro_data}': base64_sniro_data, '${sniro_ip}': self.vcpecommon.hosts['robot']} + sniro_request = self.generate_json(template_sniro_request, replace_dict) + self.logger.debug('SNIRO request:') + self.logger.debug(json.dumps(sniro_request, indent=4, sort_keys=True)) + + r = requests.post(self.vcpecommon.sniro_url, headers=self.vcpecommon.sniro_headers, json=sniro_request) + if 2 != r.status_code / 100: + response = r.json() + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + self.logger.error('SNIRO preloading failed.') + sys.exit() + + return True + + def preload_network(self, template_file, network_role, subnet_start_ip, subnet_gateway, common_dict, name_suffix): + """ + :param template_file: + :param network_role: cpe_signal, cpe_public, brg_bng, bng_mux, mux_gw + :param subnet_start_ip: + :param subnet_gateway: + :param name_suffix: e.g. '201711201311' + :return: + """ + network_name = '_'.join([self.vcpecommon.instance_name_prefix['network'], network_role.lower(), name_suffix]) + subnet_name = self.vcpecommon.network_name_to_subnet_name(network_name) + common_dict['${' + network_role+'_net}'] = network_name + common_dict['${' + network_role+'_subnet}'] = subnet_name + replace_dict = {'${network_role}': network_role, + '${service_type}': 'vCPE', + '${network_type}': 'Generic NeutronNet', + '${network_name}': network_name, + '${subnet_start_ip}': subnet_start_ip, + '${subnet_gateway}': subnet_gateway + } + self.logger.info('Preloading network ' + network_role) + return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_network_url) + + def preload(self, template_file, replace_dict, url): + json_data = self.generate_json(template_file, replace_dict) + self.logger.debug(json.dumps(json_data, indent=4, sort_keys=True)) + r = requests.post(url, headers=self.vcpecommon.sdnc_headers, auth=self.vcpecommon.sdnc_userpass, json=json_data) + response = r.json() + if int(response.get('output', {}).get('response-code', 0)) != 200: + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + self.logger.error('Preloading failed.') + return False + return True + + def preload_vgw(self, template_file, brg_mac, commont_dict, name_suffix): + replace_dict = {'${brg_mac}': brg_mac, + '${suffix}': name_suffix + } + replace_dict.update(commont_dict) + self.logger.info('Preloading vGW') + return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_vnf_url) + + def preload_vfmodule(self, template_file, service_instance_id, vnf_model, vfmodule_model, common_dict, name_suffix): + """ + :param template_file: + :param service_instance_id: + :param vnf_model: parsing results from csar_parser + :param vfmodule_model: parsing results from csar_parser + :param common_dict: + :param name_suffix: + :return: + """ + + # examples: + # vfmodule_model['modelCustomizationName']: "Vspinfra111601..base_vcpe_infra..module-0", + # vnf_model['modelCustomizationName']: "vspinfra111601 0", + + vfmodule_name = '_'.join([self.vcpecommon.instance_name_prefix['vfmodule'], + vfmodule_model['modelCustomizationName'].split('..')[0].lower(), name_suffix]) + + # vnf_type and generic_vnf_type are identical + replace_dict = {'${vnf_type}': vfmodule_model['modelCustomizationName'], + '${generic_vnf_type}': vfmodule_model['modelCustomizationName'], + '${service_type}': service_instance_id, + '${generic_vnf_name}': vnf_model['modelCustomizationName'], + '${vnf_name}': vfmodule_name, + '${suffix}': name_suffix} + replace_dict.update(common_dict) + self.logger.info('Preloading VF Module ' + vfmodule_name) + return self.preload(template_file, replace_dict, self.vcpecommon.sdnc_preload_vnf_url) + + def preload_all_networks(self, template_file, name_suffix): + common_dict = {'${' + k + '}': v for k, v in self.vcpecommon.common_preload_config.items()} + for network, v in self.vcpecommon.preload_network_config.items(): + subnet_start_ip, subnet_gateway_ip = v + if not self.preload_network(template_file, network, subnet_start_ip, subnet_gateway_ip, + common_dict, name_suffix): + return None + return common_dict + + def test(self): + # this is for testing purpose + name_suffix = datetime.now().strftime('%Y%m%d%H%M') + vcpecommon = VcpeCommon() + preloader = Preload(vcpecommon) + + network_dict = {'${' + k + '}': v for k, v in self.vcpecommon.common_preload_config.items()} + template_file = 'preload_templates/template.network.json' + for k, v in self.vcpecommon.preload_network_config.items(): + if not preloader.preload_network(template_file, k, v[0], v[1], network_dict, name_suffix): + break + + print('---------------------------------------------------------------') + print('Network related replacement dictionary:') + print(json.dumps(network_dict, indent=4, sort_keys=True)) + print('---------------------------------------------------------------') + + keys = ['infra', 'bng', 'gmux', 'brg'] + for key in keys: + csar_file = self.vcpecommon.find_file(key, 'csar', 'csar') + template_file = self.vcpecommon.find_file(key, 'json', 'preload_templates') + if csar_file and template_file: + parser = csar_parser.CsarParser() + parser.parse_csar(csar_file) + service_instance_id = 'test112233' + preloader.preload_vfmodule(template_file, service_instance_id, parser.vnf_models[0], + parser.vfmodule_models[0], network_dict, name_suffix) + + def test_sniro(self): + template_sniro_data = self.vcpecommon.find_file('sniro_data', 'json', 'preload_templates') + template_sniro_request = self.vcpecommon.find_file('sniro_request', 'json', 'preload_templates') + + vcperescust_csar = self.vcpecommon.find_file('rescust', 'csar', 'csar') + parser = csar_parser.CsarParser() + parser.parse_csar(vcperescust_csar) + tunnelxconn_ar_name = None + brg_ar_name = None + vgw_name = None + for model in parser.vnf_models: + if 'tunnel' in model['modelCustomizationName']: + tunnelxconn_ar_name = model['modelCustomizationName'] + elif 'brg' in model['modelCustomizationName']: + brg_ar_name = model['modelCustomizationName'] + elif 'vgw' in model['modelCustomizationName']: + vgw_name = model['modelCustomizationName'] + + if not (tunnelxconn_ar_name and brg_ar_name and vgw_name): + self.logger.error('Cannot find all names from %s.', vcperescust_csar) + sys.exit() + + vgmux_svc_instance_uuid = '88888888888888' + vbrg_svc_instance_uuid = '999999999999999' + + self.preload_sniro(template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, brg_ar_name, + vgmux_svc_instance_uuid, vbrg_svc_instance_uuid) diff --git a/test/vcpe/soutils.py b/test/vcpe/soutils.py new file mode 100755 index 000000000..cc82068a6 --- /dev/null +++ b/test/vcpe/soutils.py @@ -0,0 +1,318 @@ +#! /usr/bin/python + +import sys +import logging +import requests +import json +from datetime import datetime +import progressbar +import time +import csar_parser +import preload +from vcpecommon import * + + +class SoUtils: + def __init__(self, vcpecommon, api_version): + """ + :param vcpecommon: + :param api_version: must be 'v4' or 'v5' + """ + self.logger = logging.getLogger(__name__) + self.vcpecommon = vcpecommon + if api_version not in self.vcpecommon.so_req_api_url: + self.logger.error('Incorrect SO API version: %s', api_version) + sys.exit() + self.service_req_api_url = self.vcpecommon.so_req_api_url[api_version] + + def submit_create_req(self, req_json, req_type, service_instance_id=None, vnf_instance_id=None): + """ + POST {serverRoot}/serviceInstances/v4 + POST {serverRoot}/serviceInstances/v4/{serviceInstanceId}/vnfs + POST {serverRoot}/serviceInstances/v4/{serviceInstanceId}/networks + POST {serverRoot}/serviceInstances/v4/{serviceInstanceId}/vnfs/{vnfInstanceId}/vfModules + :param req_json: + :param service_instance_id: this is required only for networks, vnfs, and vf modules + :param req_type: + :param vnf_instance_id: + :return: req_id, instance_id + """ + if req_type == 'service': + url = self.service_req_api_url + elif req_type == 'vnf': + url = '/'.join([self.service_req_api_url, service_instance_id, 'vnfs']) + elif req_type == 'network': + url = '/'.join([self.service_req_api_url, service_instance_id, 'networks']) + elif req_type == 'vfmodule': + url = '/'.join([self.service_req_api_url, service_instance_id, 'vnfs', vnf_instance_id, 'vfModules']) + else: + self.logger.error('Invalid request type: {0}. Can only be service/vnf/network/vfmodule'.format(req_type)) + return None, None + + r = requests.post(url, headers=self.vcpecommon.so_headers, auth=self.vcpecommon.so_userpass, json=req_json) + response = r.json() + + self.logger.debug('---------------------------------------------------------------') + self.logger.debug('------- Creation request submitted to SO, got response --------') + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + self.logger.debug('---------------------------------------------------------------') + req_id = response.get('requestReferences', {}).get('requestId', '') + instance_id = response.get('requestReferences', {}).get('instanceId', '') + return req_id, instance_id + + def check_progress(self, req_id, eta=0, interval=5): + if not req_id: + self.logger.error('Error when checking SO request progress, invalid request ID: ' + req_id) + return False + duration = 0.0 + bar = progressbar.ProgressBar(redirect_stdout=True) + url = self.vcpecommon.so_check_progress_api_url + '/' + req_id + + while True: + time.sleep(interval) + r = requests.get(url, headers=self.vcpecommon.so_headers, auth=self.vcpecommon.so_userpass) + response = r.json() + + duration += interval + if eta > 0: + percentage = min(95, 100 * duration / eta) + else: + percentage = int(response['request']['requestStatus']['percentProgress']) + + if response['request']['requestStatus']['requestState'] == 'IN_PROGRESS': + self.logger.debug('------------------Request Status-------------------------------') + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + bar.update(percentage) + else: + self.logger.debug('---------------------------------------------------------------') + self.logger.debug('----------------- Creation Request Results --------------------') + self.logger.debug(json.dumps(response, indent=4, sort_keys=True)) + self.logger.debug('---------------------------------------------------------------') + flag = response['request']['requestStatus']['requestState'] == 'COMPLETE' + if not flag: + self.logger.error('Request failed.') + self.logger.error(json.dumps(response, indent=4, sort_keys=True)) + bar.update(100) + bar.finish() + return flag + + def add_req_info(self, req_details, instance_name, product_family_id=None): + req_details['requestInfo'] = { + 'instanceName': instance_name, + 'source': 'VID', + 'suppressRollback': 'true', + 'requestorId': 'vCPE-Robot' + } + if product_family_id: + req_details['requestInfo']['productFamilyId'] = product_family_id + + def add_related_instance(self, req_details, instance_id, instance_model): + instance = {"instanceId": instance_id, "modelInfo": instance_model} + if 'relatedInstanceList' not in req_details: + req_details['relatedInstanceList'] = [{"relatedInstance": instance}] + else: + req_details['relatedInstanceList'].append({"relatedInstance": instance}) + + def generate_vnf_or_network_request(self, req_type, instance_name, vnf_or_network_model, service_instance_id, + service_model): + req_details = { + 'modelInfo': vnf_or_network_model, + 'cloudConfiguration': {"lcpCloudRegionId": self.vcpecommon.os_region_name, + "tenantId": self.vcpecommon.os_tenant_id}, + 'requestParameters': {"userParams": []} + } + self.add_req_info(req_details, instance_name, self.vcpecommon.product_family_id) + self.add_related_instance(req_details, service_instance_id, service_model) + return {'requestDetails': req_details} + + def generate_vfmodule_request(self, instance_name, vfmodule_model, service_instance_id, + service_model, vnf_instance_id, vnf_model): + req_details = { + 'modelInfo': vfmodule_model, + 'cloudConfiguration': {"lcpCloudRegionId": self.vcpecommon.os_region_name, + "tenantId": self.vcpecommon.os_tenant_id}, + 'requestParameters': {"usePreload": 'true'} + } + self.add_req_info(req_details, instance_name, self.vcpecommon.product_family_id) + self.add_related_instance(req_details, service_instance_id, service_model) + self.add_related_instance(req_details, vnf_instance_id, vnf_model) + return {'requestDetails': req_details} + + def generate_service_request(self, instance_name, model): + req_details = { + 'modelInfo': model, + 'subscriberInfo': {'globalSubscriberId': self.vcpecommon.global_subscriber_id}, + 'requestParameters': { + "userParams": [], + "subscriptionServiceType": "vCPE", + "aLaCarte": 'true' + } + } + self.add_req_info(req_details, instance_name) + return {'requestDetails': req_details} + + def generate_custom_service_request(self, instance_name, model, brg_mac): + req_details = { + 'modelInfo': model, + 'subscriberInfo': {'subscriberName': 'Kaneohe', + 'globalSubscriberId': self.vcpecommon.global_subscriber_id}, + 'cloudConfiguration': {"lcpCloudRegionId": self.vcpecommon.os_region_name, + "tenantId": self.vcpecommon.os_tenant_id}, + 'requestParameters': { + "userParams": [ + { + 'name': 'BRG_WAN_MAC_Address', + 'value': brg_mac + } + ], + "subscriptionServiceType": "vCPE", + 'aLaCarte': 'false' + } + } + self.add_req_info(req_details, instance_name, self.vcpecommon.custom_product_family_id) + return {'requestDetails': req_details} + + def create_custom_service(self, csar_file, brg_mac, name_suffix=None): + parser = csar_parser.CsarParser() + if not parser.parse_csar(csar_file): + return False + + # yyyymmdd_hhmm + if not name_suffix: + name_suffix = '_' + datetime.now().strftime('%Y%m%d%H%M') + + # create service + instance_name = '_'.join([self.vcpecommon.instance_name_prefix['service'], + parser.svc_model['modelName'], name_suffix]) + instance_name = instance_name.lower() + req = self.generate_custom_service_request(instance_name, parser.svc_model, brg_mac) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + self.logger.info('Creating custom service {0}.'.format(instance_name)) + req_id, svc_instance_id = self.submit_create_req(req, 'service') + if not self.check_progress(req_id, 140): + return False + return True + + def wait_for_aai(self, node_type, uuid): + self.logger.info('Waiting for AAI traversal to complete...') + bar = progressbar.ProgressBar() + for i in range(30): + time.sleep(1) + bar.update(i*100.0/30) + if self.vcpecommon.is_node_in_aai(node_type, uuid): + bar.update(100) + bar.finish() + return + + self.logger.error("AAI traversal didn't finish in 30 seconds. Something is wrong. Type {0}, UUID {1}".format( + node_type, uuid)) + sys.exit() + + def create_entire_service(self, csar_file, vnf_template_file, preload_dict, name_suffix, heatbridge=False): + """ + :param csar_file: + :param vnf_template_file: + :param preload_dict: + :param name_suffix: + :return: service instance UUID + """ + self.logger.info('\n----------------------------------------------------------------------------------') + self.logger.info('Start to create entire service defined in csar: {0}'.format(csar_file)) + parser = csar_parser.CsarParser() + self.logger.info('Parsing csar ...') + if not parser.parse_csar(csar_file): + self.logger.error('Cannot parse csar: {0}'.format(csar_file)) + return None + + # create service + instance_name = '_'.join([self.vcpecommon.instance_name_prefix['service'], + parser.svc_model['modelName'], name_suffix]) + instance_name = instance_name.lower() + self.logger.info('Creating service instance: {0}.'.format(instance_name)) + req = self.generate_service_request(instance_name, parser.svc_model) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + req_id, svc_instance_id = self.submit_create_req(req, 'service') + if not self.check_progress(req_id, eta=2, interval=1): + return None + + # wait for AAI to complete traversal + self.wait_for_aai('service', svc_instance_id) + + # create networks + for model in parser.net_models: + base_name = model['modelCustomizationName'].lower().replace('mux_vg', 'mux_gw') + network_name = '_'.join([self.vcpecommon.instance_name_prefix['network'], base_name, name_suffix]) + network_name = network_name.lower() + self.logger.info('Creating network: ' + network_name) + req = self.generate_vnf_or_network_request('network', network_name, model, svc_instance_id, + parser.svc_model) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + req_id, net_instance_id = self.submit_create_req(req, 'network', svc_instance_id) + if not self.check_progress(req_id, eta=20): + return None + + self.logger.info('Changing subnet name to ' + self.vcpecommon.network_name_to_subnet_name(network_name)) + self.vcpecommon.set_network_name(network_name) + subnet_name_changed = False + for i in range(20): + time.sleep(3) + if self.vcpecommon.set_subnet_name(network_name): + subnet_name_changed = True + break + + if not subnet_name_changed: + self.logger.error('Failed to change subnet name for ' + network_name) + return None + + + vnf_model = None + vnf_instance_id = None + # create VNF + if len(parser.vnf_models) == 1: + vnf_model = parser.vnf_models[0] + vnf_instance_name = '_'.join([self.vcpecommon.instance_name_prefix['vnf'], + vnf_model['modelCustomizationName'].split(' ')[0], name_suffix]) + vnf_instance_name = vnf_instance_name.lower() + self.logger.info('Creating VNF: ' + vnf_instance_name) + req = self.generate_vnf_or_network_request('vnf', vnf_instance_name, vnf_model, svc_instance_id, + parser.svc_model) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + req_id, vnf_instance_id = self.submit_create_req(req, 'vnf', svc_instance_id) + if not self.check_progress(req_id, eta=2, interval=1): + self.logger.error('Failed to create VNF {0}.'.format(vnf_instance_name)) + return False + + # wait for AAI to complete traversal + if not vnf_instance_id: + self.logger.error('No VNF instance ID returned!') + sys.exit() + self.wait_for_aai('vnf', vnf_instance_id) + + preloader = preload.Preload(self.vcpecommon) + preloader.preload_vfmodule(vnf_template_file, svc_instance_id, parser.vnf_models[0], parser.vfmodule_models[0], + preload_dict, name_suffix) + # create VF Module + if len(parser.vfmodule_models) == 1: + if not vnf_instance_id or not vnf_model: + self.logger.error('Invalid VNF instance ID or VNF model!') + sys.exit() + + model = parser.vfmodule_models[0] + vfmodule_instance_name = '_'.join([self.vcpecommon.instance_name_prefix['vfmodule'], + model['modelCustomizationName'].split('..')[0], name_suffix]) + vfmodule_instance_name = vfmodule_instance_name.lower() + self.logger.info('Creating VF Module: ' + vfmodule_instance_name) + req = self.generate_vfmodule_request(vfmodule_instance_name, model, svc_instance_id, parser.svc_model, + vnf_instance_id, vnf_model) + self.logger.debug(json.dumps(req, indent=2, sort_keys=True)) + req_id, vfmodule_instance_id = self.submit_create_req(req, 'vfmodule', svc_instance_id, vnf_instance_id) + if not self.check_progress(req_id, eta=70, interval=5): + self.logger.error('Failed to create VF Module {0}.'.format(vfmodule_instance_name)) + return None + + # run heatbridge + if heatbridge: + self.vcpecommon.headbridge(vfmodule_instance_name, svc_instance_id) + self.vcpecommon.save_vgmux_vnf_name(vnf_instance_name) + + return svc_instance_id diff --git a/test/vcpe/vcpe.py b/test/vcpe/vcpe.py new file mode 100755 index 000000000..7de86ae8d --- /dev/null +++ b/test/vcpe/vcpe.py @@ -0,0 +1,207 @@ +#! /usr/bin/python +import sys +import logging +from vcpecommon import * +import soutils +from datetime import datetime +import preload +import vcpe_custom_service +import csar_parser +import config_sdnc_so + + +def config_sniro(vcpecommon, vgmux_svc_instance_uuid, vbrg_svc_instance_uuid): + logger = logging.getLogger(__name__) + + logger.info('\n----------------------------------------------------------------------------------') + logger.info('Start to config SNIRO homing emulator') + + preloader = preload.Preload(vcpecommon) + template_sniro_data = vcpecommon.find_file('sniro_data', 'json', 'preload_templates') + template_sniro_request = vcpecommon.find_file('sniro_request', 'json', 'preload_templates') + + vcperescust_csar = vcpecommon.find_file('rescust', 'csar', 'csar') + parser = csar_parser.CsarParser() + parser.parse_csar(vcperescust_csar) + tunnelxconn_ar_name = None + brg_ar_name = None + vgw_name = None + for model in parser.vnf_models: + if 'tunnel' in model['modelCustomizationName']: + tunnelxconn_ar_name = model['modelCustomizationName'] + elif 'brg' in model['modelCustomizationName']: + brg_ar_name = model['modelCustomizationName'] + elif 'vgw' in model['modelCustomizationName']: + vgw_name = model['modelCustomizationName'] + + if not (tunnelxconn_ar_name and brg_ar_name and vgw_name): + logger.error('Cannot find all names from %s.', vcperescust_csar) + sys.exit() + + preloader.preload_sniro(template_sniro_data, template_sniro_request, tunnelxconn_ar_name, vgw_name, brg_ar_name, + vgmux_svc_instance_uuid, vbrg_svc_instance_uuid) + + +def create_one_service(vcpecommon, csar_file, vnf_template_file, preload_dict, suffix, heatbridge=False): + """ + :return: service instance UUID + """ + so = soutils.SoUtils(vcpecommon, 'v4') + return so.create_entire_service(csar_file, vnf_template_file, preload_dict, suffix, heatbridge) + +def deploy_brg_only(): + logging.basicConfig(level=logging.INFO, format='%(message)s') + logger = logging.getLogger(__name__) + + vcpecommon = VcpeCommon() + preload_dict = vcpecommon.load_preload_data() + name_suffix = preload_dict['${brg_bng_net}'].split('_')[-1] + + # create multiple services based on the pre-determined order + svc_instance_uuid = vcpecommon.load_object(vcpecommon.svc_instance_uuid_file) + for keyword in ['brg']: + heatbridge = 'gmux' == keyword + csar_file = vcpecommon.find_file(keyword, 'csar', 'csar') + vnf_template_file = vcpecommon.find_file(keyword, 'json', 'preload_templates') + svc_instance_uuid[keyword] = create_one_service(vcpecommon, csar_file, vnf_template_file, preload_dict, + name_suffix, heatbridge) + if not svc_instance_uuid[keyword]: + sys.exit() + + # Setting up SNIRO + config_sniro(vcpecommon, svc_instance_uuid['gmux'], svc_instance_uuid['brg']) + +def deploy_infra(): + logging.basicConfig(level=logging.INFO, format='%(message)s') + logger = logging.getLogger(__name__) + + vcpecommon = VcpeCommon() + + # preload all networks + network_template = vcpecommon.find_file('network', 'json', 'preload_templates') + name_suffix = datetime.now().strftime('%Y%m%d%H%M') + preloader = preload.Preload(vcpecommon) + preload_dict = preloader.preload_all_networks(network_template, name_suffix) + logger.debug('Initial preload dictionary:') + logger.debug(json.dumps(preload_dict, indent=4, sort_keys=True)) + if not preload_dict: + logger.error("Failed to preload networks.") + sys.exit() + vcpecommon.save_preload_data(preload_dict) + + # create multiple services based on the pre-determined order + svc_instance_uuid = {} + for keyword in ['infra', 'bng', 'gmux', 'brg']: + heatbridge = 'gmux' == keyword + csar_file = vcpecommon.find_file(keyword, 'csar', 'csar') + vnf_template_file = vcpecommon.find_file(keyword, 'json', 'preload_templates') + svc_instance_uuid[keyword] = create_one_service(vcpecommon, csar_file, vnf_template_file, preload_dict, + name_suffix, heatbridge) + if not svc_instance_uuid[keyword]: + sys.exit() + + vcpecommon.save_object(svc_instance_uuid, vcpecommon.svc_instance_uuid_file) + # Setting up SNIRO + config_sniro(vcpecommon, svc_instance_uuid['gmux'], svc_instance_uuid['brg']) + + print('----------------------------------------------------------------------------------------------------') + print('Congratulations! The following have been completed correctly:') + print(' - Infrastructure Service Instantiation: ') + print(' * 4 VMs: DHCP, AAA, DNS, Web Server') + print(' * 2 Networks: CPE_PUBLIC, CPE_SIGNAL') + print(' - vBNG Service Instantiation: ') + print(' * 1 VM: vBNG') + print(' * 2 Networks: BRG_BNG, BNG_MUX') + print(' - vGMUX Service Instantiation: ') + print(' * 1 VM: vGMUX') + print(' * 1 Network: MUX_GW') + print(' - vBRG Service Instantiation: ') + print(' * 1 VM: vBRG') + print(' - Adding vGMUX vServer information to AAI.') + print(' - SNIRO Homing Emulator configuration.') + + +def deploy_custom_service(): + nodes = ['brg', 'mux'] + vcpecommon = VcpeCommon(nodes) + custom_service = vcpe_custom_service.CustomService(vcpecommon) + + # clean up + host_dic = {k: vcpecommon.hosts[k] for k in nodes} + if not vcpecommon.delete_vxlan_interfaces(host_dic): + sys.exit() + + custom_service.clean_up_sdnc() + custom_service.del_all_vgw_stacks(vcpecommon.vgw_name_keyword) + + # create new service + csar_file = vcpecommon.find_file('rescust', 'csar', 'csar') + vgw_template_file = vcpecommon.find_file('vgw', 'json', 'preload_templates') + preload_dict = vcpecommon.load_preload_data() + custom_service.create_custom_service(csar_file, vgw_template_file, preload_dict) + + +def closed_loop(lossrate=0): + if lossrate > 0: + while 'y' != raw_input('Please enter docker container "drools" in Policy VM and type "policy stop". Then enter y here: ').lower(): + continue + nodes = ['brg', 'mux'] + logger = logging.getLogger('__name__') + vcpecommon = VcpeCommon(nodes) + logger.info('Cleaning up vGMUX data reporting settings') + vcpecommon.del_vgmux_ves_mode() + time.sleep(2) + vcpecommon.del_vgmux_ves_collector() + + logger.info('Staring vGMUX data reporting to DCAE') + time.sleep(2) + vcpecommon.set_vgmux_ves_collector() + + logger.info('Setting vGMUX to report packet loss rate: %s', lossrate) + time.sleep(2) + vcpecommon.set_vgmux_packet_loss_rate(lossrate, vcpecommon.load_vgmux_vnf_name()) + if lossrate > 0: + print('Please enter docker container "drools" in Policy VM and type "policy start". Then observe vGMUX being restarted.') + + +def init_so_sdnc(): + logger = logging.getLogger('__name__') + vcpecommon = VcpeCommon() + config_sdnc_so.insert_customer_service_to_so(vcpecommon) + config_sdnc_so.insert_customer_service_to_sdnc(vcpecommon) + + +if __name__ == '__main__': + logging.basicConfig(level=logging.INFO, format='%(message)s') + + print('----------------------------------------------------------------------------------------------------') + print(' vcpe.py: Brief info about this program') +# print(' vcpe.py sdc: Onboard VNFs, design and distribute vCPE services (under development)') + print(' vcpe.py init: Add customer service data to SDNC and SO DBs.') + print(' vcpe.py infra: Deploy infrastructure, including DHCP, AAA, DNS, Web Server, vBNG, vGMUX, vBRG.') + print(' vcpe.py customer: Deploy customer service, including vGW and VxLANs') + print(' vcpe.py loop: Test closed loop control') + print('----------------------------------------------------------------------------------------------------') + + if len(sys.argv) != 2: + sys.exit() + + if sys.argv[1] == 'sdc': + print('Under development') + elif sys.argv[1] == 'init': + if 'y' == raw_input('Ready to add customer service data to SDNC and SO DBs? This is needed only once.' + 'y/n: ').lower(): + init_so_sdnc() + elif sys.argv[1] == 'infra': + if 'y' == raw_input('Ready to deploy infrastructure? y/n: ').lower(): + deploy_infra() + elif sys.argv[1] == 'customer': + if 'y' == raw_input('Ready to deploy customer service? y/n: ').lower(): + deploy_custom_service() + elif sys.argv[1] == 'loop': + closed_loop(22) + elif sys.argv[1] == 'noloss': + closed_loop(0) + elif sys.argv[1] == 'brg': + deploy_brg_only() + diff --git a/test/vcpe/vcpe_custom_service.py b/test/vcpe/vcpe_custom_service.py new file mode 100755 index 000000000..d89129eef --- /dev/null +++ b/test/vcpe/vcpe_custom_service.py @@ -0,0 +1,80 @@ +#! /usr/bin/python + +import os +import requests +import time +from vcpecommon import * +from datetime import datetime +import soutils +import logging +import preload +import json + + +class CustomService: + def __init__(self, vcpecommon): + self.logger = logging.getLogger(__name__) + self.vcpecommon = vcpecommon + + # delete all vgw stacks + def del_all_vgw_stacks(self, keyword): + param = ' '.join([k + ' ' + v for k, v in self.vcpecommon.cloud.items()]) + openstackcmd = 'openstack ' + param + ' ' + + stacks = os.popen(openstackcmd + 'stack list').read() + found = False + for stack_description in stacks.split('\n'): + if keyword in stack_description: + found = True + stack_name = stack_description.split('|')[2].strip() + cmd = openstackcmd + 'stack delete -y ' + stack_name + self.logger.info('Deleting ' + stack_name) + os.popen(cmd) + + if not found: + self.logger.info('No vGW stack to delete') + + # clean up SDNC + def clean_up_sdnc(self): + items = ['tunnelxconn-allotted-resources', 'brg-allotted-resources'] + for res in items: + self.logger.info('Cleaning up ' + res + ' from SDNC') + requests.delete(self.vcpecommon.sdnc_ar_cleanup_url + res, auth=self.vcpecommon.sdnc_userpass) + + def print_success_info(self, print_instructions=True, nodes=None): + if not nodes: + nodes = ['brg', 'mux', 'gw', 'web'] + ip_dict = self.vcpecommon.get_vm_ip(nodes, self.vcpecommon.external_net_addr, + self.vcpecommon.external_net_prefix_len) + + print(json.dumps(ip_dict, indent=4, sort_keys=True)) + for node in ['brg', 'mux']: + print('VxLAN config in {0}:'.format(node)) + self.vcpecommon.get_vxlan_interfaces(ip_dict[node], print_info=True) + + print(json.dumps(ip_dict, indent=4, sort_keys=True)) + + if print_instructions: + print('----------------------------------------------------------------------------') + print('Custom service created successfully. See above for VxLAN configuration info.') + print('To test data plane connectivity, following the steps below.') + print(' 1. ssh to vGW at {0}'.format(ip_dict['gw'])) + print(' 2. Restart DHCP: systemctl restart isc-dhcp-server') + print(' 3. ssh to vBRG at {0}'.format(ip_dict['brg'])) + print(' 4. Get IP from vGW: dhclient lstack') + print(' 5. Add route to Internet: ip route add 10.2.0.0/24 via 192.168.1.254 dev lstack') + print(' 6. ping the web server: ping {0}'.format('10.2.0.10')) + print(' 7. wget http://{0}'.format('10.2.0.10')) + + def create_custom_service(self, csar_file, vgw_template_file, preload_dict=None): + name_suffix = datetime.now().strftime('%Y%m%d%H%M') + brg_mac = self.vcpecommon.get_brg_mac_from_sdnc() + # preload vGW + if preload_dict: + preloader = preload.Preload(self.vcpecommon) + preloader.preload_vgw(vgw_template_file, brg_mac, preload_dict, name_suffix) + + # create service + so = soutils.SoUtils(self.vcpecommon, 'v5') + if so.create_custom_service(csar_file, brg_mac, name_suffix): + self.print_success_info() diff --git a/test/vcpe/vcpecommon.py b/test/vcpe/vcpecommon.py new file mode 100755 index 000000000..5b3e009a3 --- /dev/null +++ b/test/vcpe/vcpecommon.py @@ -0,0 +1,414 @@ +import json +import logging +import os +import pickle +import re +import sys + +import ipaddress +import mysql.connector +import requests +import commands +import time + + +class VcpeCommon: + ############################################################################################# + # Start: configurations that you must change for a new ONAP installation + external_net_addr = '10.12.0.0' + external_net_prefix_len = 16 + ############################################################################################# + # set the openstack cloud access credentials here + cloud = { + '--os-auth-url': 'http://10.12.25.2:5000', + '--os-username': 'YOUR ID', + '--os-user-domain-id': 'default', + '--os-project-domain-id': 'default', + '--os-tenant-id': '087050388b204c73a3e418dd2c1fe30b', + '--os-region-name': 'RegionOne', + '--os-password': 'YOUR PASSWD', + '--os-project-domain-name': 'Integration-SB-01', + '--os-identity-api-version': '3' + } + + common_preload_config = { + 'oam_onap_net': 'oam_onap_c4Uw', + 'oam_onap_subnet': 'oam_onap_c4Uw', + 'public_net': 'external', + 'public_net_id': '971040b2-7059-49dc-b220-4fab50cb2ad4' + } + # End: configurations that you must change for a new ONAP installation + ############################################################################################# + + template_variable_symbol = '${' + ############################################################################################# + # preloading network config + # key=network role + # value = [subnet_start_ip, subnet_gateway_ip] + preload_network_config = { + 'cpe_public': ['10.2.0.2', '10.2.0.1'], + 'cpe_signal': ['10.4.0.2', '10.4.0.1'], + 'brg_bng': ['10.3.0.2', '10.3.0.1'], + 'bng_mux': ['10.1.0.10', '10.1.0.1'], + 'mux_gw': ['10.5.0.10', '10.5.0.1'] + } + + global_subscriber_id = 'SDN-ETHERNET-INTERNET' + + def __init__(self, extra_host_names=None): + self.logger = logging.getLogger(__name__) + self.logger.info('Initializing configuration') + + self.host_names = ['so', 'sdnc', 'robot', 'aai-inst1', 'dcaedoks00'] + if extra_host_names: + self.host_names.extend(extra_host_names) + # get IP addresses + self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len) + # this is the keyword used to name vgw stack, must not be used in other stacks + self.vgw_name_keyword = 'base_vcpe_vgw' + self.svc_instance_uuid_file = '__var/svc_instance_uuid' + self.preload_dict_file = '__var/preload_dict' + self.vgmux_vnf_name_file = '__var/vgmux_vnf_name' + self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116' + self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb' + self.instance_name_prefix = { + 'service': 'vcpe_svc', + 'network': 'vcpe_net', + 'vnf': 'vcpe_vnf', + 'vfmodule': 'vcpe_vfmodule' + } + self.aai_userpass = 'AAI', 'AAI' + self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh' + self.os_tenant_id = self.cloud['--os-tenant-id'] + self.os_region_name = self.cloud['--os-region-name'] + self.common_preload_config['pub_key'] = self.pub_key + self.sniro_url = 'http://' + self.hosts['robot'] + ':8080/__admin/mappings' + self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + + ############################################################################################# + # SDNC urls + self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U' + self.sdnc_db_name = 'sdnctl' + self.sdnc_db_user = 'sdnctl' + self.sdnc_db_pass = 'gamma' + self.sdnc_db_port = '32768' + self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \ + ':8282/restconf/operations/VNF-API:preload-network-topology-operation' + self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \ + ':8282/restconf/operations/VNF-API:preload-vnf-topology-operation' + self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':8282/restconf/config/GENERIC-RESOURCE-API:' + + ############################################################################################# + # SO urls, note: do NOT add a '/' at the end of the url + self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':8080/ecomp/mso/infra/serviceInstances/v4', + 'v5': 'http://' + self.hosts['so'] + ':8080/ecomp/mso/infra/serviceInstances/v5'} + self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':8080/ecomp/mso/infra/orchestrationRequests/v2' + self.so_userpass = 'InfraPortalClient', 'password1$' + self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.so_db_name = 'mso_catalog' + self.so_db_user = 'root' + self.so_db_pass = 'password' + self.so_db_port = '32768' + + self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces' + self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'} + self.vpp_api_userpass = ('admin', 'admin') + self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent' + + def headbridge(self, openstack_stack_name, svc_instance_uuid): + """ + Add vserver information to AAI + """ + self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name)) + cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid) + ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd)) + self.logger.debug('%s', ret) + + def get_brg_mac_from_sdnc(self): + """ + :return: BRG MAC address. Currently we only support one BRG instance. + """ + cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name, + host=self.hosts['sdnc'], port=self.sdnc_db_port) + cursor = cnx.cursor() + query = "SELECT * from DHCP_MAP" + cursor.execute(query) + + self.logger.debug('DHCP_MAP table in SDNC') + counter = 0 + mac = None + for mac, ip in cursor: + counter += 1 + self.logger.debug(mac + ':' + ip) + + cnx.close() + + if counter != 1: + self.logger.error('Found %s MAC addresses in DHCP_MAP', counter) + sys.exit() + else: + self.logger.debug('Found MAC addresses in DHCP_MAP: %s', mac) + return mac + + def insert_into_sdnc_db(self, cmds): + cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name, + host=self.hosts['sdnc'], port=self.sdnc_db_port) + cursor = cnx.cursor() + for cmd in cmds: + self.logger.debug(cmd) + cursor.execute(cmd) + self.logger.debug('%s', cursor) + cnx.commit() + cursor.close() + cnx.close() + + def insert_into_so_db(self, cmds): + cnx = mysql.connector.connect(user=self.so_db_user, password=self.so_db_pass, database=self.so_db_name, + host=self.hosts['so'], port=self.so_db_port) + cursor = cnx.cursor() + for cmd in cmds: + self.logger.debug(cmd) + cursor.execute(cmd) + self.logger.debug('%s', cursor) + cnx.commit() + cursor.close() + cnx.close() + + def find_file(self, file_name_keyword, file_ext, search_dir): + """ + :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra + :param file_ext: e.g., csar, json + :param search_dir path to search + :return: path name of the file + """ + file_name_keyword = file_name_keyword.lower() + file_ext = file_ext.lower() + if not file_ext.startswith('.'): + file_ext = '.' + file_ext + + filenamepath = None + for file_name in os.listdir(search_dir): + file_name_lower = file_name.lower() + if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext): + if filenamepath: + self.logger.error('Multiple files found for *{0}*.{1} in ' + 'directory {2}'.format(file_name_keyword, file_ext, search_dir)) + sys.exit() + filenamepath = os.path.abspath(os.path.join(search_dir, file_name)) + + if filenamepath: + return filenamepath + else: + self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir)) + sys.exit() + + @staticmethod + def network_name_to_subnet_name(network_name): + """ + :param network_name: example: vcpe_net_cpe_signal_201711281221 + :return: vcpe_net_cpe_signal_subnet_201711281221 + """ + fields = network_name.split('_') + fields.insert(-1, 'subnet') + return '_'.join(fields) + + def set_network_name(self, network_name): + param = ' '.join([k + ' ' + v for k, v in self.cloud.items()]) + openstackcmd = 'openstack ' + param + cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1']) + os.popen(cmd) + + def set_subnet_name(self, network_name): + """ + Example: network_name = vcpe_net_cpe_signal_201711281221 + set subnet name to vcpe_net_cpe_signal_subnet_201711281221 + :return: + """ + param = ' '.join([k + ' ' + v for k, v in self.cloud.items()]) + openstackcmd = 'openstack ' + param + + # expected results: | subnets | subnet_id | + subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|') + if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets': + subnet_id = subnet_info[2].strip() + subnet_name = self.network_name_to_subnet_name(network_name) + cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id]) + os.popen(cmd) + self.logger.info("Subnet name set to: " + subnet_name) + return True + else: + self.logger.error("Can't get subnet info from network name: " + network_name) + return False + + def is_node_in_aai(self, node_type, node_uuid): + key = None + search_node_type = None + if node_type == 'service': + search_node_type = 'service-instance' + key = 'service-instance-id' + elif node_type == 'vnf': + search_node_type = 'generic-vnf' + key = 'vnf-id' + else: + logging.error('Invalid node_type: ' + node_type) + sys.exit() + + url = 'https://{0}:8443/aai/v11/search/nodes-query?search-node-type={1}&filter={2}:EQUALS:{3}'.format( + self.hosts['aai-inst1'], search_node_type, key, node_uuid) + + headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot'} + requests.packages.urllib3.disable_warnings() + r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False) + response = r.json() + self.logger.debug('aai query: ' + url) + self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True)) + return 'result-data' in response + + @staticmethod + def extract_ip_from_str(net_addr, net_addr_len, sz): + """ + :param net_addr: e.g. 10.5.12.0 + :param net_addr_len: e.g. 24 + :param sz: a string + :return: the first IP address matching the network, e.g. 10.5.12.3 + """ + network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False) + ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz) + for ip in ip_list: + this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False) + if this_net == network: + return str(ip) + return None + + def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None): + """ + :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg'] + :param net_addr: e.g. 10.12.5.0 + :param net_addr_len: e.g. 24 + :return: dictionary {keyword: ip} + """ + if not net_addr: + net_addr = self.external_net_addr + + if not net_addr_len: + net_addr_len = self.external_net_prefix_len + + param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k]) + openstackcmd = 'nova ' + param + ' list' + self.logger.debug(openstackcmd) + + ip_dict = {} + results = os.popen(openstackcmd).read() + for line in results.split('\n'): + fields = line.split('|') + if len(fields) == 8: + vm_name = fields[2] + ip_info = fields[-2] + for keyword in keywords: + if keyword in vm_name: + ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info) + if ip: + ip_dict[keyword] = ip + if len(ip_dict) != len(keywords): + self.logger.error('Cannot find all desired IP addresses for %s.', keywords) + self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True)) + sys.exit() + return ip_dict + + def del_vgmux_ves_mode(self): + url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode' + r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + self.logger.debug('%s', r) + + def del_vgmux_ves_collector(self): + url = self.vpp_ves_url.format(self.hosts['mux']) + '/config' + r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + self.logger.debug('%s', r) + + def set_vgmux_ves_collector(self ): + url = self.vpp_ves_url.format(self.hosts['mux']) + data = {'config': + {'server-addr': self.hosts['dcaedoks00'], + 'server-port': '8080', + 'read-interval': '10', + 'is-add':'1' + } + } + r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data) + self.logger.debug('%s', r) + + def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name): + url = self.vpp_ves_url.format(self.hosts['mux']) + data = {"mode": + {"working-mode": "demo", + "base-packet-loss": str(lossrate), + "source-name": vg_vnf_instance_name + } + } + r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data) + self.logger.debug('%s', r) + + # return all the VxLAN interface names of BRG or vGMUX based on the IP address + def get_vxlan_interfaces(self, ip, print_info=False): + url = self.vpp_inf_url.format(ip) + self.logger.debug('url is this: %s', url) + r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + data = r.json()['interfaces']['interface'] + if print_info: + for inf in data: + if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel': + print(json.dumps(inf, indent=4, sort_keys=True)) + + return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel'] + + # delete all VxLAN interfaces of each hosts + def delete_vxlan_interfaces(self, host_dic): + for host, ip in host_dic.items(): + deleted = False + self.logger.info('{0}: Getting VxLAN interfaces'.format(host)) + inf_list = self.get_vxlan_interfaces(ip) + for inf in inf_list: + deleted = True + time.sleep(2) + self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf)) + url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2' + requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + + for inf in inf_list: + deleted = True + time.sleep(2) + self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf)) + url = self.vpp_inf_url.format(ip) + '/interface/' + inf + requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass) + + if len(self.get_vxlan_interfaces(ip)) > 0: + self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip)) + return False + + if not deleted: + self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host)) + return True + + @staticmethod + def save_object(obj, filepathname): + with open(filepathname, 'wb') as fout: + pickle.dump(obj, fout) + + @staticmethod + def load_object(filepathname): + with open(filepathname, 'rb') as fin: + return pickle.load(fin) + + def save_preload_data(self, preload_data): + self.save_object(preload_data, self.preload_dict_file) + + def load_preload_data(self): + return self.load_object(self.preload_dict_file) + + def save_vgmux_vnf_name(self, vgmux_vnf_name): + self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file) + + def load_vgmux_vnf_name(self): + return self.load_object(self.vgmux_vnf_name_file) + |