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