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