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
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'
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'
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 #############################################################################################
50 template_variable_symbol = '${'
51 cpe_vm_prefix = 'zdcpe'
52 #############################################################################################
53 # preloading network config
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']
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'
70 def __init__(self, extra_host_names=None):
71 self.logger = logging.getLogger(__name__)
72 self.logger.info('Initializing configuration')
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'
81 self.host_names = ['so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name]
83 self.host_names.extend(extra_host_names)
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',
99 'vfmodule': 'vcpe_vfmodule'
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"
116 #############################################################################################
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:'
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'
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'
150 def headbridge(self, openstack_stack_name, svc_instance_uuid):
152 Add vserver information to AAI
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)
160 print('To add vGMUX vserver info to AAI, do the following:')
161 print('- ssh to rancher')
163 print('- cd /root/oom/kubernetes/robot')
164 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
166 def get_brg_mac_from_sdnc(self):
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.
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)
177 self.logger.debug('DHCP_MAP table in SDNC')
180 for mac, ip in cursor:
181 self.logger.debug(mac + ':' + ip)
182 this_host = int(ip.split('.')[-1])
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)
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)
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()
204 self.logger.debug(cmd)
206 self.logger.debug('%s', cursor)
211 def find_file(self, file_name_keyword, file_ext, search_dir):
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
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
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):
228 self.logger.error('Multiple files found for *{0}*.{1} in '
229 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
231 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
236 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
240 def network_name_to_subnet_name(network_name):
242 :param network_name: example: vcpe_net_cpe_signal_201711281221
243 :return: vcpe_net_cpe_signal_subnet_201711281221
245 fields = network_name.split('_')
246 fields.insert(-1, 'subnet')
247 return '_'.join(fields)
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'])
255 def set_subnet_name(self, network_name):
257 Example: network_name = vcpe_net_cpe_signal_201711281221
258 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
261 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
262 openstackcmd = 'openstack ' + param
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])
271 self.logger.info("Subnet name set to: " + subnet_name)
274 self.logger.error("Can't get subnet info from network name: " + network_name)
277 def is_node_in_aai(self, node_type, node_uuid):
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'
287 logging.error('Invalid node_type: ' + node_type)
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)
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)
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
302 def extract_ip_from_str(net_addr, net_addr_len, sz):
304 :param net_addr: e.g. 10.5.12.0
305 :param net_addr_len: e.g. 24
307 :return: the first IP address matching the network, e.g. 10.5.12.3
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)
312 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
313 if this_net == network:
317 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
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}
325 net_addr = self.external_net_addr
328 net_addr_len = self.external_net_prefix_len
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)
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)
340 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
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')
349 def get_oom_onap_vm_ip(self, keywords):
351 onap_vm_list = set(['so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
353 if vm in onap_vm_list:
354 vm_ip[vm] = self.oom_so_sdnc_aai_ip
357 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
359 for line in novalist_results.split('\n'):
360 fields = line.split('|')
364 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
365 vm_ip_dict[vm_name] = ip
369 def remove_old_vms(self, vm_list, prefix):
371 For vms with format name_timestamp, only keep the one with the latest timestamp.
373 zdcpe1cpe01brgemu01_201805222148 (drop this)
374 zdcpe1cpe01brgemu01_201805222229 (keep this)
375 zdcpe1cpe01gw01_201805162201
378 same_type_vm_dict = {}
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
385 new_vm_list.append(vm)
387 new_vm_list.extend(same_type_vm_dict.values())
390 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
392 for keyword in vm_name_keyword_list:
393 for vm, ip in all_vm_ip_dict.items():
395 vm_ip_dict[keyword] = ip
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)
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)
409 def set_vgmux_ves_collector(self ):
410 url = self.vpp_ves_url.format(self.hosts['mux'])
412 {'server-addr': self.hosts[self.dcae_ves_collector_name],
413 'server-port': '30235' if self.oom_mode else '8081',
414 'read-interval': '10',
418 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
419 self.logger.debug('%s', r)
421 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
422 url = self.vpp_ves_url.format(self.hosts['mux'])
424 {"working-mode": "demo",
425 "base-packet-loss": str(lossrate),
426 "source-name": vg_vnf_instance_name
429 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
430 self.logger.debug('%s', r)
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']
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))
443 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
445 # delete all VxLAN interfaces of each hosts
446 def delete_vxlan_interfaces(self, host_dic):
447 for host, ip in host_dic.items():
449 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
450 inf_list = self.get_vxlan_interfaces(ip)
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)
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)
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))
470 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
474 def save_object(obj, filepathname):
475 with open(filepathname, 'wb') as fout:
476 pickle.dump(obj, fout)
479 def load_object(filepathname):
480 with open(filepathname, 'rb') as fin:
481 return pickle.load(fin)
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])
496 ipaddr_or_vni[-1] = str(number)
497 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
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)
503 def save_preload_data(self, preload_data):
504 self.save_object(preload_data, self.preload_dict_file)
506 def load_preload_data(self):
507 return self.load_object(self.preload_dict_file)
509 def save_vgmux_vnf_name(self, vgmux_vnf_name):
510 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
512 def load_vgmux_vnf_name(self):
513 return self.load_object(self.vgmux_vnf_name_file)