aboutsummaryrefslogtreecommitdiffstats
path: root/test/vcpe
diff options
context:
space:
mode:
authorKang Xi <kang.xi@huawei.com>2018-04-06 16:56:04 -0400
committerKang Xi <kang.xi@huawei.com>2018-04-13 14:52:52 -0400
commit11d278c5adf571bbc1b9d184e78ad3309f8f04d4 (patch)
treec9d2b2aeb990be44e58b4e2d3ffb120d4b1d260c /test/vcpe
parentf5b6edf04f07ff98c41a0d25e5639e8844d9377c (diff)
Initial code import for vcpe automation.
Initial code import for vCPE use case automation. Source code was validated for Amsterdam release. Changes may be needed for Bejing. - Remove unneeded files: test.py and onap_dev Issue-ID: INT-461 Change-Id: Ifc2665bf8069fdcebd052cecc95266a4b831d2e8 Signed-off-by: Kang Xi <kang.xi@huawei.com>
Diffstat (limited to 'test/vcpe')
-rwxr-xr-xtest/vcpe/config_sdnc_so.py89
-rwxr-xr-xtest/vcpe/csar_parser.py231
-rwxr-xr-xtest/vcpe/get_info.py26
-rwxr-xr-xtest/vcpe/healthcheck.py30
-rwxr-xr-xtest/vcpe/loop.py37
-rwxr-xr-xtest/vcpe/preload.py216
-rwxr-xr-xtest/vcpe/soutils.py318
-rwxr-xr-xtest/vcpe/vcpe.py207
-rwxr-xr-xtest/vcpe/vcpe_custom_service.py80
-rwxr-xr-xtest/vcpe/vcpecommon.py414
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)
+