11 import mysql.connector
16 from novaclient import client as openstackclient
17 from kubernetes import client, config
18 from netaddr import IPAddress, IPNetwork
22 def __init__(self, extra_host_names=None, cfg_file=None):
23 self.logger = logging.getLogger(__name__)
24 self.logger.setLevel(logging.DEBUG)
25 self.logger.info('Initializing configuration')
26 self.default_config = 'vcpeconfig.yaml'
28 # Read configuration from config file
29 self._load_config(cfg_file)
31 self.sdnc_controller_pod = '-'.join([self.onap_environment, 'sdnc-sdnc-0'])
32 # OOM: this is the address that the brg and bng will nat for sdnc access - 10.0.0.x address of k8 host for sdnc-0 container
33 self.sdnc_oam_ip = self.get_pod_node_oam_ip(self.sdnc_controller_pod)
34 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
35 self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip(self.sdnc_controller_pod)
36 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
37 self.oom_dcae_ves_collector = self.oom_so_sdnc_aai_ip
38 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
39 self.mr_ip_addr = self.oom_so_sdnc_aai_ip
40 self.mr_ip_port = '30227'
41 self.so_nbi_port = '30277' if self.oom_mode else '8080'
42 self.sdnc_preloading_port = '30267' if self.oom_mode else '8282'
43 self.aai_query_port = '30233' if self.oom_mode else '8443'
44 self.sniro_port = '30288' if self.oom_mode else '8080'
46 self.host_names = ['sdc', 'so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name, 'mariadb-galera']
48 self.host_names.extend(extra_host_names)
50 self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len)
51 # this is the keyword used to name vgw stack, must not be used in other stacks
52 self.vgw_name_keyword = 'base_vcpe_vgw'
53 # this is the file that will keep the index of last assigned SO name
54 self.vgw_vfmod_name_index_file= '__var/vgw_vfmod_name_index'
55 self.svc_instance_uuid_file = '__var/svc_instance_uuid'
56 self.preload_dict_file = '__var/preload_dict'
57 self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
58 self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
59 self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
60 self.instance_name_prefix = {
61 'service': 'vcpe_svc',
62 'network': 'vcpe_net',
64 'vfmodule': 'vcpe_vfmodule'
66 self.aai_userpass = 'AAI', 'AAI'
67 self.os_tenant_id = self.cloud['--os-tenant-id']
68 self.os_region_name = self.cloud['--os-region-name']
69 self.common_preload_config['pub_key'] = self.pub_key
70 self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
71 self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
72 self.homing_solution = 'sniro' # value is either 'sniro' or 'oof'
73 # self.homing_solution = 'oof'
74 self.customer_location_used_by_oof = {
75 "customerLatitude": "32.897480",
76 "customerLongitude": "-97.040443",
77 "customerName": "some_company"
80 #############################################################################################
82 self.sdc_be_port = '30204'
83 self.sdc_be_request_userpass = 'vid', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
84 self.sdc_be_request_headers = {'X-ECOMP-InstanceID': 'VID'}
85 self.sdc_be_url_prefix = 'https://' + self.hosts['sdc'] + ':' + self.sdc_be_port
86 self.sdc_service_list_url = self.sdc_be_url_prefix + '/sdc/v1/catalog/services'
88 self.sdc_fe_port = '30207'
89 self.sdc_fe_request_userpass = 'beep', 'boop'
90 self.sdc_fe_request_headers = {'USER_ID': 'demo', 'Content-Type': 'application/json'}
91 self.sdc_fe_url_prefix = 'https://' + self.hosts['sdc'] + ':' + self.sdc_fe_port
92 self.sdc_get_category_list_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/categories'
93 self.sdc_create_allotted_resource_subcategory_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/category/resources/resourceNewCategory.allotted%20resource/subCategory'
95 #############################################################################################
97 self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
98 self.sdnc_db_name = 'sdnctl'
99 self.sdnc_db_user = 'sdnctl'
100 self.sdnc_db_pass = 'gamma'
101 self.sdnc_db_port = self.get_k8s_service_endpoint_info('mariadb-galera','port') if self.oom_mode else '3306'
102 self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
103 self.sdnc_preload_network_url = 'https://' + self.hosts['sdnc'] + \
104 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
105 self.sdnc_preload_network_gra_url = 'https://' + self.hosts['sdnc'] + \
106 ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-network-topology-operation'
107 self.sdnc_preload_vnf_url = 'https://' + self.hosts['sdnc'] + \
108 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
109 self.sdnc_preload_gra_url = 'https://' + self.hosts['sdnc'] + \
110 ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
111 self.sdnc_ar_cleanup_url = 'https://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
112 '/restconf/config/GENERIC-RESOURCE-API:'
114 #############################################################################################
115 # MARIADB-GALERA settings
116 self.mariadb_galera_endpoint_ip = self.get_k8s_service_endpoint_info('mariadb-galera','ip')
117 self.mariadb_galera_endpoint_port = self.get_k8s_service_endpoint_info('mariadb-galera','port')
119 #############################################################################################
120 # SO urls, note: do NOT add a '/' at the end of the url
121 self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances',
122 'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances'}
123 self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/orchestrationRequests/v6'
124 self.so_userpass = 'InfraPortalClient', 'password1$'
125 self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
126 self.so_db_name = 'catalogdb'
127 self.so_db_user = 'root'
128 self.so_db_pass = 'secretpassword'
129 self.so_db_host = self.mariadb_galera_endpoint_ip if self.oom_mode else self.hosts['so']
130 self.so_db_port = self.mariadb_galera_endpoint_port if self.oom_mode else '3306'
132 self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
133 self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
134 self.vpp_api_userpass = ('admin', 'admin')
135 self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
137 #############################################################################################
139 self.policy_userpass = ('healthcheck', 'zb!XztG34')
140 self.policy_headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
141 self.policy_api_url = 'https://{0}:6969/policy/api/v1/policytypes/onap.policies.controlloop.Operational/versions/1.0.0/policies'
142 self.policy_pap_get_url = 'https://{0}:6969/policy/pap/v1/pdps'
143 self.policy_pap_json = {'policies': [{'policy-id': 'operational.vcpe'}]}
144 self.policy_pap_post_url = self.policy_pap_get_url + '/policies'
145 self.policy_api_service_name = 'policy-api'
146 self.policy_pap_service_name = 'policy-pap'
148 #############################################################################################
150 self.aai_region_query_url = 'https://' + self.oom_so_sdnc_aai_ip + ':' +\
151 self.aai_query_port +\
152 '/aai/v14/cloud-infrastructure/cloud-regions/cloud-region/CloudOwner/' +\
153 self.cloud['--os-region-name']
154 self.aai_headers = {'Accept': 'application/json',
155 'Content-Type': 'application/json',
156 'X-FromAppId': 'postman', 'X-TransactionId': '9999'}
158 def _load_config(self, cfg_file):
160 Reads vcpe config file and injects settings as object's attributes
161 :param cfg_file: Configuration file path
165 cfg_file = self.default_config
168 with open(cfg_file, 'r') as cfg:
169 cfg_yml = yaml.full_load(cfg)
170 except Exception as e:
171 self.logger.error('Error loading configuration: ' + str(e))
174 self.logger.debug('\n' + yaml.dump(cfg_yml))
176 # Use setattr to load config file keys as VcpeCommon class' object
179 # Check config isn't empty
180 if cfg_yml is not None:
181 for cfg_key in cfg_yml:
182 setattr(self, cfg_key, cfg_yml[cfg_key])
183 except TypeError as e:
184 self.logger.error('Unable to parse config file: ' + str(e))
187 def heatbridge(self, openstack_stack_name, svc_instance_uuid):
189 Add vserver information to AAI
191 self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
192 if not self.oom_mode:
193 cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
194 ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
195 self.logger.debug('%s', ret)
197 print('To add vGMUX vserver info to AAI, do the following:')
198 print('- ssh to rancher')
200 print('- cd /root/oom/kubernetes/robot')
201 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
203 def get_brg_mac_from_sdnc(self):
205 Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
206 Note that there might be multiple BRGs, the most recently instantiated BRG always has the largest IP address.
209 db_host=self.mariadb_galera_endpoint_ip
211 db_host=self.hosts['mariadb-galera']
213 cnx = mysql.connector.connect(user=self.sdnc_db_user,
214 password=self.sdnc_db_pass,
215 database=self.sdnc_db_name,
217 port=self.sdnc_db_port)
218 cursor = cnx.cursor()
219 query = "SELECT * from DHCP_MAP"
220 cursor.execute(query)
222 self.logger.debug('DHCP_MAP table in SDNC')
225 for mac, ip in cursor:
226 self.logger.debug(mac + ' - ' + ip)
227 this_host = int(ip.split('.')[-1])
236 except AssertionError:
237 self.logger.error('Failed to obtain BRG MAC address from database')
242 def execute_cmds_mariadb(self, cmds):
243 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass,
244 self.sdnc_db_name, self.mariadb_galera_endpoint_ip,
245 self.mariadb_galera_endpoint_port)
247 def execute_cmds_sdnc_db(self, cmds):
248 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
249 self.hosts['sdnc'], self.sdnc_db_port)
251 def execute_cmds_so_db(self, cmds):
252 self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
253 self.so_db_host, self.so_db_port)
255 def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
256 cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
257 cursor = cnx.cursor()
259 self.logger.debug(cmd)
261 self.logger.debug('%s', cursor)
266 def find_file(self, file_name_keyword, file_ext, search_dir):
268 :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra
269 :param file_ext: e.g., csar, json
270 :param search_dir path to search
271 :return: path name of the file
273 file_name_keyword = file_name_keyword.lower()
274 file_ext = file_ext.lower()
275 if not file_ext.startswith('.'):
276 file_ext = '.' + file_ext
279 for file_name in os.listdir(search_dir):
280 file_name_lower = file_name.lower()
281 if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
283 self.logger.error('Multiple files found for *{0}*.{1} in '
284 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
286 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
291 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
295 def network_name_to_subnet_name(network_name):
297 :param network_name: example: vcpe_net_cpe_signal_201711281221
298 :return: vcpe_net_cpe_signal_subnet_201711281221
300 fields = network_name.split('_')
301 fields.insert(-1, 'subnet')
302 return '_'.join(fields)
304 def set_network_name(self, network_name):
305 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
306 openstackcmd = 'openstack ' + param
307 cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
310 def set_subnet_name(self, network_name):
312 Example: network_name = vcpe_net_cpe_signal_201711281221
313 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
316 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
317 openstackcmd = 'openstack ' + param
319 # expected results: | subnets | subnet_id |
320 subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
321 if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
322 subnet_id = subnet_info[2].strip()
323 subnet_name = self.network_name_to_subnet_name(network_name)
324 cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
326 self.logger.info("Subnet name set to: " + subnet_name)
329 self.logger.error("Can't get subnet info from network name: " + network_name)
332 def set_closed_loop_policy(self, policy_template_file):
333 # Gather policy services cluster ips
334 p_api_cluster_ip = self.get_k8s_service_cluster_ip(self.policy_api_service_name)
335 p_pap_cluster_ip = self.get_k8s_service_cluster_ip(self.policy_pap_service_name)
337 # Read policy json from file
338 with open(policy_template_file) as f:
340 policy_json = json.load(f)
342 self.logger.error(policy_template_file + " doesn't seem to contain valid JSON data")
345 # Check policy already applied
346 policy_exists_req = requests.get(self.policy_pap_get_url.format(
347 p_pap_cluster_ip), auth=self.policy_userpass,
348 verify=False, headers=self.policy_headers)
349 if policy_exists_req.status_code != 200:
350 self.logger.error('Failure in checking CL policy existence. '
351 'Policy-pap responded with HTTP code {0}'.format(
352 policy_exists_req.status_code))
356 policy_exists_json = policy_exists_req.json()
357 except ValueError as e:
358 self.logger.error('Policy-pap request failed: ' + e.message)
362 assert policy_exists_json['groups'][0]['pdpSubgroups'] \
363 [1]['policies'][0]['name'] != 'operational.vcpe'
364 except AssertionError:
365 self.logger.info('vCPE closed loop policy already exists, not applying')
368 pass # policy doesn't exist
371 policy_create_req = requests.post(self.policy_api_url.format(
372 p_api_cluster_ip), auth=self.policy_userpass,
373 json=policy_json, verify=False,
374 headers=self.policy_headers)
375 # Get the policy id from policy-api response
376 if policy_create_req.status_code != 200:
377 self.logger.error('Failed creating policy. Policy-api responded'
378 ' with HTTP code {0}'.format(policy_create_req.status_code))
382 policy_version = json.loads(policy_create_req.text)['policy-version']
383 except (KeyError, ValueError):
384 self.logger.error('Policy API response not understood:')
385 self.logger.debug('\n' + str(policy_create_req.text))
387 # Inject the policy into Policy PAP
388 self.policy_pap_json['policies'].append({'policy-version': policy_version})
389 policy_insert_req = requests.post(self.policy_pap_post_url.format(
390 p_pap_cluster_ip), auth=self.policy_userpass,
391 json=self.policy_pap_json, verify=False,
392 headers=self.policy_headers)
393 if policy_insert_req.status_code != 200:
394 self.logger.error('Policy PAP request failed with HTTP code'
395 '{0}'.format(policy_insert_req.status_code))
397 self.logger.info('Successully pushed closed loop Policy')
399 def is_node_in_aai(self, node_type, node_uuid):
401 search_node_type = None
402 if node_type == 'service':
403 search_node_type = 'service-instance'
404 key = 'service-instance-id'
405 elif node_type == 'vnf':
406 search_node_type = 'generic-vnf'
409 logging.error('Invalid node_type: ' + node_type)
412 url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
413 self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
415 headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
416 r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
418 self.logger.debug('aai query: ' + url)
419 self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
420 return 'result-data' in response
423 def extract_ip_from_str(net_addr, net_addr_len, sz):
425 :param net_addr: e.g. 10.5.12.0
426 :param net_addr_len: e.g. 24
428 :return: the first IP address matching the network, e.g. 10.5.12.3
430 network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
431 ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
433 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
434 if this_net == network:
438 def get_pod_node_oam_ip(self, pod):
440 :Assuming kubectl is available and configured by default config (~/.kube/config)
441 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
442 :return pod's cluster node oam ip (10.0.0.0/16)
445 config.load_kube_config()
446 api = client.CoreV1Api()
447 kslogger = logging.getLogger('kubernetes')
448 kslogger.setLevel(logging.INFO)
449 res = api.list_pod_for_all_namespaces()
451 if pod in i.metadata.name:
452 self.logger.debug("found {0}\t{1}\t{2}".format(i.metadata.name, i.status.host_ip, i.spec.node_name))
453 ret = i.status.host_ip
457 ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node OAM IP address(10.0.0.0/16): ")
460 def get_pod_node_public_ip(self, pod):
462 :Assuming kubectl is available and configured by default config (~/.kube/config)
463 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
464 :return pod's cluster node public ip (i.e. 10.12.0.0/16)
467 config.load_kube_config()
468 api = client.CoreV1Api()
469 kslogger = logging.getLogger('kubernetes')
470 kslogger.setLevel(logging.INFO)
471 res = api.list_pod_for_all_namespaces()
473 if pod in i.metadata.name:
474 ret = self.get_vm_public_ip_by_nova(i.spec.node_name)
475 self.logger.debug("found node {0} public ip: {1}".format(i.spec.node_name, ret))
479 ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node public IP address(i.e. " + self.external_net_addr + "): ")
482 def get_vm_public_ip_by_nova(self, vm):
484 This method uses openstack nova api to retrieve vm public ip
488 subnet = IPNetwork('{0}/{1}'.format(self.external_net_addr, self.external_net_prefix_len))
489 nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url'])
490 for i in nova.servers.list():
492 for k, v in i.networks.items():
494 if IPAddress(ip) in subnet:
498 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
500 :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
501 :param net_addr: e.g. 10.12.5.0
502 :param net_addr_len: e.g. 24
503 :return: dictionary {keyword: ip}
506 net_addr = self.external_net_addr
509 net_addr_len = self.external_net_prefix_len
511 param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
512 openstackcmd = 'nova ' + param + ' list'
513 self.logger.debug(openstackcmd)
515 results = os.popen(openstackcmd).read()
516 all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
517 latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
518 latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
519 ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
521 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
523 if len(ip_dict) != len(keywords):
524 self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
525 self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
526 self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 396')
530 def get_oom_onap_vm_ip(self, keywords):
533 if vm in self.host_names:
534 vm_ip[vm] = self.oom_so_sdnc_aai_ip
537 def get_k8s_service_cluster_ip(self, service):
539 Returns cluster IP for a given service
540 :param service: name of the service
543 config.load_kube_config()
544 api = client.CoreV1Api()
545 kslogger = logging.getLogger('kubernetes')
546 kslogger.setLevel(logging.INFO)
548 resp = api.read_namespaced_service(service, self.onap_namespace)
549 except client.rest.ApiException as e:
550 self.logger.error('Error while making k8s API request: ' + e.body)
553 return resp.spec.cluster_ip
555 def get_k8s_service_endpoint_info(self, service, subset):
557 Returns endpoint data for a given service and subset. If there
558 is more than one endpoint returns data for the first one from
559 the list that API returned.
560 :param service: name of the service
561 :param subset: subset name, one of "ip","port"
564 config.load_kube_config()
565 api = client.CoreV1Api()
566 kslogger = logging.getLogger('kubernetes')
567 kslogger.setLevel(logging.INFO)
569 resp = api.read_namespaced_endpoints(service, self.onap_namespace)
570 except client.rest.ApiException as e:
571 self.logger.error('Error while making k8s API request: ' + e.body)
575 return resp.subsets[0].addresses[0].ip
576 elif subset == "port":
577 return resp.subsets[0].ports[0].port
579 self.logger.error("Unsupported subset type")
581 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
583 for line in novalist_results.split('\n'):
584 fields = line.split('|')
588 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
589 vm_ip_dict[vm_name] = ip
593 def remove_old_vms(self, vm_list, prefix):
595 For vms with format name_timestamp, only keep the one with the latest timestamp.
597 zdcpe1cpe01brgemu01_201805222148 (drop this)
598 zdcpe1cpe01brgemu01_201805222229 (keep this)
599 zdcpe1cpe01gw01_201805162201
602 same_type_vm_dict = {}
604 fields = vm.split('_')
605 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
606 if vm > same_type_vm_dict.get(fields[0], '0'):
607 same_type_vm_dict[fields[0]] = vm
609 new_vm_list.append(vm)
611 new_vm_list.extend(same_type_vm_dict.values())
614 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
616 for keyword in vm_name_keyword_list:
617 for vm, ip in all_vm_ip_dict.items():
619 vm_ip_dict[keyword] = ip
623 def del_vgmux_ves_mode(self):
624 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
625 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
626 self.logger.debug('%s', r)
628 def del_vgmux_ves_collector(self):
629 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
630 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
631 self.logger.debug('%s', r)
633 def set_vgmux_ves_collector(self ):
634 url = self.vpp_ves_url.format(self.hosts['mux'])
636 {'server-addr': self.hosts[self.dcae_ves_collector_name],
637 'server-port': '30235' if self.oom_mode else '8081',
638 'read-interval': '10',
642 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
643 self.logger.debug('%s', r)
645 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
646 url = self.vpp_ves_url.format(self.hosts['mux'])
648 {"working-mode": "demo",
649 "base-packet-loss": str(lossrate),
650 "source-name": vg_vnf_instance_name
653 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
654 self.logger.debug('%s', r)
656 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
657 def get_vxlan_interfaces(self, ip, print_info=False):
658 url = self.vpp_inf_url.format(ip)
659 self.logger.debug('url is this: %s', url)
660 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
661 data = r.json()['interfaces']['interface']
664 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
665 print(json.dumps(inf, indent=4, sort_keys=True))
667 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
669 # delete all VxLAN interfaces of each hosts
670 def delete_vxlan_interfaces(self, host_dic):
671 for host, ip in host_dic.items():
673 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
674 inf_list = self.get_vxlan_interfaces(ip)
678 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
679 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
680 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
685 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
686 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
687 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
689 if len(self.get_vxlan_interfaces(ip)) > 0:
690 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
694 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
698 def save_object(obj, filepathname):
699 with open(filepathname, 'wb') as fout:
700 pickle.dump(obj, fout)
703 def load_object(filepathname):
704 with open(filepathname, 'rb') as fin:
705 return pickle.load(fin)
708 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
709 with open(vnf_template_file) as json_input:
710 json_data = json.load(json_input)
711 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
712 for param in param_list:
713 if param['vnf-parameter-name'] in vnf_parameter_name_list:
714 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
715 number = int(ipaddr_or_vni[-1])
720 ipaddr_or_vni[-1] = str(number)
721 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
723 assert json_data is not None
724 with open(vnf_template_file, 'w') as json_output:
725 json.dump(json_data, json_output, indent=4, sort_keys=True)
727 def save_preload_data(self, preload_data):
728 self.save_object(preload_data, self.preload_dict_file)
730 def load_preload_data(self):
731 return self.load_object(self.preload_dict_file)
733 def save_vgmux_vnf_name(self, vgmux_vnf_name):
734 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
736 def load_vgmux_vnf_name(self):
737 return self.load_object(self.vgmux_vnf_name_file)