Improve external_net_addr property description in vCPE scripts
[integration.git] / test / vcpe / vcpecommon.py
1 import json
2 import logging
3 import os
4 import pickle
5 import re
6 import sys
7
8 import ipaddress
9 import mysql.connector
10 import requests
11 import commands
12 import time
13 from novaclient import client as openstackclient
14 from kubernetes import client, config
15 from netaddr import IPAddress, IPNetwork
16
17 class VcpeCommon:
18     #############################################################################################
19     #     Start: configurations that you must change for a new ONAP installation
20
21     #############################################################################################
22     # Set network prefix of k8s host external address; mainly used for pod public IP autodetection
23     external_net_addr = '10.12.0.0'
24     external_net_prefix_len = 16
25
26     #############################################################################################
27     # set the openstack cloud access credentials here
28     oom_mode = True
29
30     cloud = {
31         '--os-auth-url': 'http://10.12.25.2:5000',
32         '--os-username': 'kxi',
33         '--os-user-domain-id': 'default',
34         '--os-project-domain-id': 'default',
35         '--os-tenant-id': 'bc43d50ffcb84750bac0c1707a9a765b' if oom_mode else '1e097c6713e74fd7ac8e4295e605ee1e',
36         '--os-region-name': 'RegionOne',
37         '--os-password': 'n3JhGMGuDzD8',
38         '--os-project-domain-name': 'Integration-SB-03' if oom_mode else 'Integration-SB-07',
39         '--os-identity-api-version': '3'
40     }
41
42     common_preload_config = {
43         'oam_onap_net': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
44         'oam_onap_subnet': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
45         'public_net': 'external',
46         'public_net_id': '971040b2-7059-49dc-b220-4fab50cb2ad4'
47     }
48     sdnc_controller_pod = 'dev-sdnc-sdnc-0'
49
50     #############################################################################################
51
52     template_variable_symbol = '${'
53     cpe_vm_prefix = 'zdcpe'
54     #############################################################################################
55     # preloading network config
56     #  key=network role
57     #  value = [subnet_start_ip, subnet_gateway_ip]
58     preload_network_config = {
59         'cpe_public': ['10.2.0.2', '10.2.0.1'],
60         'cpe_signal': ['10.4.0.2', '10.4.0.1'],
61         'brg_bng': ['10.3.0.2', '10.3.0.1'],
62         'bng_mux': ['10.1.0.10', '10.1.0.1'],
63         'mux_gw': ['10.5.0.10', '10.5.0.1']
64     }
65
66     dcae_ves_collector_name = 'dcae-bootstrap'
67     global_subscriber_id = 'SDN-ETHERNET-INTERNET'
68     project_name = 'Project-Demonstration'
69     owning_entity_id = '520cc603-a3c4-4ec2-9ef4-ca70facd79c0'
70     owning_entity_name = 'OE-Demonstration1'
71
72     def __init__(self, extra_host_names=None):
73         self.logger = logging.getLogger(__name__)
74         self.logger.setLevel(logging.DEBUG)
75         self.logger.info('Initializing configuration')
76
77         # CHANGEME: vgw_VfModuleModelInvariantUuid is in rescust service csar, look in service-VcpesvcRescust1118-template.yml for groups vgw module metadata. TODO: read this value automcatically
78         self.vgw_VfModuleModelInvariantUuid = '26d6a718-17b2-4ba8-8691-c44343b2ecd2'
79         # CHANGEME: 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
80         self.sdnc_oam_ip = self.get_pod_node_oam_ip('sdnc-sdnc-0')
81         # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP 
82         self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip('sdnc-sdnc-0')
83         # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
84         self.oom_dcae_ves_collector = self.oom_so_sdnc_aai_ip
85         # CHANGEME: OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
86         self.mr_ip_addr = self.oom_so_sdnc_aai_ip
87         self.mr_ip_port = '30227'
88         self.so_nbi_port = '30277' if self.oom_mode else '8080'
89         self.sdnc_preloading_port = '30202' if self.oom_mode else '8282'
90         self.aai_query_port = '30233' if self.oom_mode else '8443'
91         self.sniro_port = '30288' if self.oom_mode else '8080'
92
93         self.host_names = ['sdc', 'so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name]
94         if extra_host_names:
95             self.host_names.extend(extra_host_names)
96         # get IP addresses
97         self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len)
98         # this is the keyword used to name vgw stack, must not be used in other stacks
99         self.vgw_name_keyword = 'base_vcpe_vgw'
100         # this is the file that will keep the index of last assigned SO name
101         self.vgw_vfmod_name_index_file= '__var/vgw_vfmod_name_index'
102         self.svc_instance_uuid_file = '__var/svc_instance_uuid'
103         self.preload_dict_file = '__var/preload_dict'
104         self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
105         self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
106         self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
107         self.instance_name_prefix = {
108             'service': 'vcpe_svc',
109             'network': 'vcpe_net',
110             'vnf': 'vcpe_vnf',
111             'vfmodule': 'vcpe_vfmodule'
112         }
113         self.aai_userpass = 'AAI', 'AAI'
114         self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh'
115         self.os_tenant_id = self.cloud['--os-tenant-id']
116         self.os_region_name = self.cloud['--os-region-name']
117         self.common_preload_config['pub_key'] = self.pub_key
118         self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
119         self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
120         self.homing_solution = 'sniro'  # value is either 'sniro' or 'oof'
121 #        self.homing_solution = 'oof'
122         self.customer_location_used_by_oof = {
123             "customerLatitude": "32.897480",
124             "customerLongitude": "-97.040443",
125             "customerName": "some_company"
126         }
127
128         #############################################################################################
129         # SDC urls
130         self.sdc_be_port = '30205'
131         self.sdc_be_request_userpass = 'vid', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
132         self.sdc_be_request_headers = {'X-ECOMP-InstanceID': 'VID'}
133         self.sdc_be_url_prefix = 'http://' + self.hosts['sdc'] + ':' + self.sdc_be_port
134         self.sdc_service_list_url = self.sdc_be_url_prefix + '/sdc/v1/catalog/services'
135
136         self.sdc_fe_port = '30206'
137         self.sdc_fe_request_userpass = 'beep', 'boop'
138         self.sdc_fe_request_headers = {'USER_ID': 'demo', 'Content-Type': 'application/json'}
139         self.sdc_fe_url_prefix = 'http://' + self.hosts['sdc'] + ':' + self.sdc_fe_port
140         self.sdc_get_category_list_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/categories'
141         self.sdc_create_allotted_resource_subcategory_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/category/resources/resourceNewCategory.allotted%20resource/subCategory'
142
143         #############################################################################################
144         # SDNC urls
145         self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
146         self.sdnc_db_name = 'sdnctl'
147         self.sdnc_db_user = 'sdnctl'
148         self.sdnc_db_pass = 'gamma'
149         self.sdnc_db_port = '32774'
150         self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
151         self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \
152                                         ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
153         self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \
154                                     ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
155         self.sdnc_preload_gra_url = 'http://' + self.hosts['sdnc'] + \
156                                     ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
157         self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
158                                    '/restconf/config/GENERIC-RESOURCE-API:'
159
160         #############################################################################################
161         # SO urls, note: do NOT add a '/' at the end of the url
162         self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances',
163                            'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances'}
164         self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/orchestrationRequests/v6'
165         self.so_userpass = 'InfraPortalClient', 'password1$'
166         self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
167         self.so_db_name = 'catalogdb'
168         self.so_db_user = 'root'
169         self.so_db_pass = 'secretpassword'
170         self.so_db_port = '30252' if self.oom_mode else '32769'
171
172         self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
173         self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
174         self.vpp_api_userpass = ('admin', 'admin')
175         self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
176
177     def headbridge(self, openstack_stack_name, svc_instance_uuid):
178         """
179         Add vserver information to AAI
180         """
181         self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
182         if not self.oom_mode:
183             cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
184             ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
185             self.logger.debug('%s', ret)
186         else:
187             print('To add vGMUX vserver info to AAI, do the following:')
188             print('- ssh to rancher')
189             print('- sudo su -')
190             print('- cd /root/oom/kubernetes/robot')
191             print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
192
193     def get_brg_mac_from_sdnc(self):
194         """
195         Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
196         Note that there might be multiple BRGs, the most recently instantiated BRG always has the largest IP address.
197         """
198         cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
199                                       host=self.hosts['sdnc'], port=self.sdnc_db_port)
200         cursor = cnx.cursor()
201         query = "SELECT * from DHCP_MAP"
202         cursor.execute(query)
203
204         self.logger.debug('DHCP_MAP table in SDNC')
205         mac_recent = None
206         host = -1
207         for mac, ip in cursor:
208             self.logger.debug(mac + ':' + ip)
209             this_host = int(ip.split('.')[-1])
210             if host < this_host:
211                 host = this_host
212                 mac_recent = mac
213
214         cnx.close()
215
216         assert mac_recent
217         return mac_recent
218
219     def execute_cmds_sdnc_db(self, cmds):
220         self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
221                              self.hosts['sdnc'], self.sdnc_db_port)
222
223     def execute_cmds_so_db(self, cmds):
224         self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
225                              self.hosts['so'], self.so_db_port)
226
227     def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
228         cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
229         cursor = cnx.cursor()
230         for cmd in cmds:
231             self.logger.debug(cmd)
232             cursor.execute(cmd)
233             self.logger.debug('%s', cursor)
234         cnx.commit()
235         cursor.close()
236         cnx.close()
237
238     def find_file(self, file_name_keyword, file_ext, search_dir):
239         """
240         :param file_name_keyword:  keyword used to look for the csar file, case insensitive matching, e.g, infra
241         :param file_ext: e.g., csar, json
242         :param search_dir path to search
243         :return: path name of the file
244         """
245         file_name_keyword = file_name_keyword.lower()
246         file_ext = file_ext.lower()
247         if not file_ext.startswith('.'):
248             file_ext = '.' + file_ext
249
250         filenamepath = None
251         for file_name in os.listdir(search_dir):
252             file_name_lower = file_name.lower()
253             if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
254                 if filenamepath:
255                     self.logger.error('Multiple files found for *{0}*.{1} in '
256                                       'directory {2}'.format(file_name_keyword, file_ext, search_dir))
257                     sys.exit()
258                 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
259
260         if filenamepath:
261             return filenamepath
262         else:
263             self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
264             sys.exit()
265
266     @staticmethod
267     def network_name_to_subnet_name(network_name):
268         """
269         :param network_name: example: vcpe_net_cpe_signal_201711281221
270         :return: vcpe_net_cpe_signal_subnet_201711281221
271         """
272         fields = network_name.split('_')
273         fields.insert(-1, 'subnet')
274         return '_'.join(fields)
275
276     def set_network_name(self, network_name):
277         param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
278         openstackcmd = 'openstack ' + param
279         cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
280         os.popen(cmd)
281
282     def set_subnet_name(self, network_name):
283         """
284         Example: network_name =  vcpe_net_cpe_signal_201711281221
285         set subnet name to vcpe_net_cpe_signal_subnet_201711281221
286         :return:
287         """
288         param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
289         openstackcmd = 'openstack ' + param
290
291         # expected results: | subnets | subnet_id |
292         subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
293         if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
294             subnet_id = subnet_info[2].strip()
295             subnet_name = self.network_name_to_subnet_name(network_name)
296             cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
297             os.popen(cmd)
298             self.logger.info("Subnet name set to: " + subnet_name)
299             return True
300         else:
301             self.logger.error("Can't get subnet info from network name: " + network_name)
302             return False
303
304     def is_node_in_aai(self, node_type, node_uuid):
305         key = None
306         search_node_type = None
307         if node_type == 'service':
308             search_node_type = 'service-instance'
309             key = 'service-instance-id'
310         elif node_type == 'vnf':
311             search_node_type = 'generic-vnf'
312             key = 'vnf-id'
313         else:
314             logging.error('Invalid node_type: ' + node_type)
315             sys.exit()
316
317         url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
318             self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
319
320         headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
321         requests.packages.urllib3.disable_warnings()
322         r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
323         response = r.json()
324         self.logger.debug('aai query: ' + url)
325         self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
326         return 'result-data' in response
327
328     @staticmethod
329     def extract_ip_from_str(net_addr, net_addr_len, sz):
330         """
331         :param net_addr:  e.g. 10.5.12.0
332         :param net_addr_len: e.g. 24
333         :param sz: a string
334         :return: the first IP address matching the network, e.g. 10.5.12.3
335         """
336         network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
337         ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
338         for ip in ip_list:
339             this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
340             if this_net == network:
341                 return str(ip)
342         return None
343
344     def get_pod_node_oam_ip(self, pod):
345         """
346         :Assuming kubectl is available and configured by default config (~/.kube/config) 
347         :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
348         :return pod's cluster node oam ip (10.0.0.0/16)
349         """
350         ret = None
351         config.load_kube_config()
352         api = client.CoreV1Api()
353         kslogger = logging.getLogger('kubernetes')
354         kslogger.setLevel(logging.INFO)
355         res = api.list_pod_for_all_namespaces()
356         for i in res.items:
357             if pod in i.metadata.name:
358                 self.logger.debug("found {0}\t{1}\t{2}".format(i.metadata.name, i.status.host_ip, i.spec.node_name))
359                 ret = i.status.host_ip
360                 break
361
362         if ret is None:
363             ret = raw_input("Enter sdnc-sdnc-0 pod cluster node OAM IP address(10.0.0.0/16): ")
364         return ret
365
366     def get_pod_node_public_ip(self, pod):
367         """
368         :Assuming kubectl is available and configured by default config (~/.kube/config) 
369         :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
370         :return pod's cluster node public ip (i.e. 10.12.0.0/16)
371         """
372         ret = None
373         config.load_kube_config()
374         api = client.CoreV1Api()
375         kslogger = logging.getLogger('kubernetes')
376         kslogger.setLevel(logging.INFO)
377         res = api.list_pod_for_all_namespaces()
378         for i in res.items:
379             if pod in i.metadata.name:
380                 ret = self.get_vm_public_ip_by_nova(i.spec.node_name)
381                 self.logger.debug("found node {0} public ip: {1}".format(i.spec.node_name, ret))
382                 break
383
384         if ret is None:
385             ret = raw_input("Enter sdnc-sdnc-0 pod cluster node public IP address(i.e. 10.12.0.0/16): ")
386         return ret
387
388     def get_vm_public_ip_by_nova(self, vm):
389         """
390         This method uses openstack nova api to retrieve vm public ip
391         :param vm: vm name
392         :return vm public ip
393         """
394         subnet = IPNetwork('{0}/{1}'.format(self.external_net_addr, self.external_net_prefix_len))
395         nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url']) 
396         for i in nova.servers.list():
397             if i.name == vm:
398                 for k, v in i.networks.items():
399                     for ip in v:
400                         if IPAddress(ip) in subnet:
401                             return ip
402         return None
403
404     def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
405         """
406         :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
407         :param net_addr: e.g. 10.12.5.0
408         :param net_addr_len: e.g. 24
409         :return: dictionary {keyword: ip}
410         """
411         if not net_addr:
412             net_addr = self.external_net_addr
413
414         if not net_addr_len:
415             net_addr_len = self.external_net_prefix_len
416
417         param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
418         openstackcmd = 'nova ' + param + ' list'
419         self.logger.debug(openstackcmd)
420
421         results = os.popen(openstackcmd).read()
422         all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
423         latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
424         latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
425         ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
426         if self.oom_mode:
427             ip_dict.update(self.get_oom_onap_vm_ip(keywords))
428
429         if len(ip_dict) != len(keywords):
430             self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
431             self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
432             self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 396')
433 #            sys.exit()
434         return ip_dict
435
436     def get_oom_onap_vm_ip(self, keywords):
437         vm_ip = {}
438         onap_vm_list = set(['sdc', 'so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
439         for vm in keywords:
440             if vm in onap_vm_list:
441                 vm_ip[vm] = self.oom_so_sdnc_aai_ip
442         return vm_ip
443
444     def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
445         vm_ip_dict = {}
446         for line in novalist_results.split('\n'):
447             fields = line.split('|')
448             if len(fields) == 8:
449                 vm_name = fields[2]
450                 ip_info = fields[-2]
451                 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
452                 vm_ip_dict[vm_name] = ip
453
454         return vm_ip_dict
455
456     def remove_old_vms(self, vm_list, prefix):
457         """
458         For vms with format name_timestamp, only keep the one with the latest timestamp.
459         E.g.,
460             zdcpe1cpe01brgemu01_201805222148        (drop this)
461             zdcpe1cpe01brgemu01_201805222229        (keep this)
462             zdcpe1cpe01gw01_201805162201
463         """
464         new_vm_list = []
465         same_type_vm_dict = {}
466         for vm in vm_list:
467             fields = vm.split('_')
468             if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
469                 if vm > same_type_vm_dict.get(fields[0], '0'):
470                     same_type_vm_dict[fields[0]] = vm
471             else:
472                 new_vm_list.append(vm)
473
474         new_vm_list.extend(same_type_vm_dict.values())
475         return new_vm_list
476
477     def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
478         vm_ip_dict = {}
479         for keyword in vm_name_keyword_list:
480             for vm, ip in all_vm_ip_dict.items():
481                 if keyword in vm:
482                     vm_ip_dict[keyword] = ip
483                     break
484         return vm_ip_dict
485
486     def del_vgmux_ves_mode(self):
487         url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
488         r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
489         self.logger.debug('%s', r)
490
491     def del_vgmux_ves_collector(self):
492         url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
493         r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
494         self.logger.debug('%s', r)
495
496     def set_vgmux_ves_collector(self ):
497         url = self.vpp_ves_url.format(self.hosts['mux'])
498         data = {'config':
499                     {'server-addr': self.hosts[self.dcae_ves_collector_name],
500                      'server-port': '30235' if self.oom_mode else '8081',
501                      'read-interval': '10',
502                      'is-add':'1'
503                      }
504                 }
505         r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
506         self.logger.debug('%s', r)
507
508     def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
509         url = self.vpp_ves_url.format(self.hosts['mux'])
510         data = {"mode":
511                     {"working-mode": "demo",
512                      "base-packet-loss": str(lossrate),
513                      "source-name": vg_vnf_instance_name
514                      }
515                 }
516         r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
517         self.logger.debug('%s', r)
518
519         # return all the VxLAN interface names of BRG or vGMUX based on the IP address
520     def get_vxlan_interfaces(self, ip, print_info=False):
521         url = self.vpp_inf_url.format(ip)
522         self.logger.debug('url is this: %s', url)
523         r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
524         data = r.json()['interfaces']['interface']
525         if print_info:
526             for inf in data:
527                 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
528                     print(json.dumps(inf, indent=4, sort_keys=True))
529
530         return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
531
532     # delete all VxLAN interfaces of each hosts
533     def delete_vxlan_interfaces(self, host_dic):
534         for host, ip in host_dic.items():
535             deleted = False
536             self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
537             inf_list = self.get_vxlan_interfaces(ip)
538             for inf in inf_list:
539                 deleted = True
540                 time.sleep(2)
541                 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
542                 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
543                 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
544
545             for inf in inf_list:
546                 deleted = True
547                 time.sleep(2)
548                 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
549                 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
550                 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
551
552             if len(self.get_vxlan_interfaces(ip)) > 0:
553                 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
554                 return False
555
556             if not deleted:
557                 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
558         return True
559
560     @staticmethod
561     def save_object(obj, filepathname):
562         with open(filepathname, 'wb') as fout:
563             pickle.dump(obj, fout)
564
565     @staticmethod
566     def load_object(filepathname):
567         with open(filepathname, 'rb') as fin:
568             return pickle.load(fin)
569
570     @staticmethod
571     def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
572         with open(vnf_template_file) as json_input:
573             json_data = json.load(json_input)
574             param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
575             for param in param_list:
576                 if param['vnf-parameter-name'] in vnf_parameter_name_list:
577                     ipaddr_or_vni = param['vnf-parameter-value'].split('.')
578                     number = int(ipaddr_or_vni[-1])
579                     if 254 == number:
580                         number = 10
581                     else:
582                         number = number + 1
583                     ipaddr_or_vni[-1] = str(number)
584                     param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
585
586         assert json_data is not None
587         with open(vnf_template_file, 'w') as json_output:
588             json.dump(json_data, json_output, indent=4, sort_keys=True)
589
590     def save_preload_data(self, preload_data):
591         self.save_object(preload_data, self.preload_dict_file)
592
593     def load_preload_data(self):
594         return self.load_object(self.preload_dict_file)
595
596     def save_vgmux_vnf_name(self, vgmux_vnf_name):
597         self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
598
599     def load_vgmux_vnf_name(self):
600         return self.load_object(self.vgmux_vnf_name_file)
601