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 = '30223' 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 self.svc_instance_uuid_file = '__var/svc_instance_uuid'
89 self.preload_dict_file = '__var/preload_dict'
90 self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
91 self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
92 self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
93 self.instance_name_prefix = {
94 'service': 'vcpe_svc',
95 'network': 'vcpe_net',
97 'vfmodule': 'vcpe_vfmodule'
99 self.aai_userpass = 'AAI', 'AAI'
100 self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh'
101 self.os_tenant_id = self.cloud['--os-tenant-id']
102 self.os_region_name = self.cloud['--os-region-name']
103 self.common_preload_config['pub_key'] = self.pub_key
104 self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
105 self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
106 self.homing_solution = 'sniro' # value is either 'sniro' or 'oof'
107 # self.homing_solution = 'oof'
108 self.customer_location_used_by_oof = {
109 "customerLatitude": "32.897480",
110 "customerLongitude": "-97.040443",
111 "customerName": "some_company"
114 #############################################################################################
116 self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
117 self.sdnc_db_name = 'sdnctl'
118 self.sdnc_db_user = 'sdnctl'
119 self.sdnc_db_pass = 'gamma'
120 self.sdnc_db_port = '32774'
121 self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
122 self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \
123 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
124 self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \
125 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
126 self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
127 '/restconf/config/GENERIC-RESOURCE-API:'
129 #############################################################################################
130 # SO urls, note: do NOT add a '/' at the end of the url
131 self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/ecomp/mso/infra/serviceInstances/v4',
132 'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/ecomp/mso/infra/serviceInstances/v5'}
133 self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/ecomp/mso/infra/orchestrationRequests/v5'
134 self.so_userpass = 'InfraPortalClient', 'password1$'
135 self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
136 self.so_db_name = 'mso_catalog'
137 self.so_db_user = 'root'
138 self.so_db_pass = 'password'
139 self.so_db_port = '30252' if self.oom_mode else '32769'
141 self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
142 self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
143 self.vpp_api_userpass = ('admin', 'admin')
144 self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
146 def headbridge(self, openstack_stack_name, svc_instance_uuid):
148 Add vserver information to AAI
150 self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
151 if not self.oom_mode:
152 cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
153 ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
154 self.logger.debug('%s', ret)
156 print('To add vGMUX vserver info to AAI, do the following:')
157 print('- ssh to rancher')
159 print('- cd /root/oom/kubernetes/robot')
160 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
162 def get_brg_mac_from_sdnc(self):
164 Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
165 Note that there might be multiple BRGs, the most recently instantiated BRG always has the largest IP address.
167 cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
168 host=self.hosts['sdnc'], port=self.sdnc_db_port)
169 cursor = cnx.cursor()
170 query = "SELECT * from DHCP_MAP"
171 cursor.execute(query)
173 self.logger.debug('DHCP_MAP table in SDNC')
176 for mac, ip in cursor:
177 self.logger.debug(mac + ':' + ip)
178 this_host = int(ip.split('.')[-1])
188 def execute_cmds_sdnc_db(self, cmds):
189 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
190 self.hosts['sdnc'], self.sdnc_db_port)
192 def execute_cmds_so_db(self, cmds):
193 self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
194 self.hosts['so'], self.so_db_port)
196 def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
197 cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
198 cursor = cnx.cursor()
200 self.logger.debug(cmd)
202 self.logger.debug('%s', cursor)
207 def find_file(self, file_name_keyword, file_ext, search_dir):
209 :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra
210 :param file_ext: e.g., csar, json
211 :param search_dir path to search
212 :return: path name of the file
214 file_name_keyword = file_name_keyword.lower()
215 file_ext = file_ext.lower()
216 if not file_ext.startswith('.'):
217 file_ext = '.' + file_ext
220 for file_name in os.listdir(search_dir):
221 file_name_lower = file_name.lower()
222 if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
224 self.logger.error('Multiple files found for *{0}*.{1} in '
225 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
227 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
232 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
236 def network_name_to_subnet_name(network_name):
238 :param network_name: example: vcpe_net_cpe_signal_201711281221
239 :return: vcpe_net_cpe_signal_subnet_201711281221
241 fields = network_name.split('_')
242 fields.insert(-1, 'subnet')
243 return '_'.join(fields)
245 def set_network_name(self, network_name):
246 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
247 openstackcmd = 'openstack ' + param
248 cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
251 def set_subnet_name(self, network_name):
253 Example: network_name = vcpe_net_cpe_signal_201711281221
254 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
257 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
258 openstackcmd = 'openstack ' + param
260 # expected results: | subnets | subnet_id |
261 subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
262 if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
263 subnet_id = subnet_info[2].strip()
264 subnet_name = self.network_name_to_subnet_name(network_name)
265 cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
267 self.logger.info("Subnet name set to: " + subnet_name)
270 self.logger.error("Can't get subnet info from network name: " + network_name)
273 def is_node_in_aai(self, node_type, node_uuid):
275 search_node_type = None
276 if node_type == 'service':
277 search_node_type = 'service-instance'
278 key = 'service-instance-id'
279 elif node_type == 'vnf':
280 search_node_type = 'generic-vnf'
283 logging.error('Invalid node_type: ' + node_type)
286 url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
287 self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
289 headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
290 requests.packages.urllib3.disable_warnings()
291 r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
293 self.logger.debug('aai query: ' + url)
294 self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
295 return 'result-data' in response
298 def extract_ip_from_str(net_addr, net_addr_len, sz):
300 :param net_addr: e.g. 10.5.12.0
301 :param net_addr_len: e.g. 24
303 :return: the first IP address matching the network, e.g. 10.5.12.3
305 network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
306 ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
308 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
309 if this_net == network:
313 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
315 :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
316 :param net_addr: e.g. 10.12.5.0
317 :param net_addr_len: e.g. 24
318 :return: dictionary {keyword: ip}
321 net_addr = self.external_net_addr
324 net_addr_len = self.external_net_prefix_len
326 param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
327 openstackcmd = 'nova ' + param + ' list'
328 self.logger.debug(openstackcmd)
330 results = os.popen(openstackcmd).read()
331 all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
332 latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
333 latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
334 ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
336 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
338 if len(ip_dict) != len(keywords):
339 self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
340 self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
341 self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 316')
345 def get_oom_onap_vm_ip(self, keywords):
347 onap_vm_list = set(['so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
349 if vm in onap_vm_list:
350 vm_ip[vm] = self.oom_so_sdnc_aai_ip
353 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
355 for line in novalist_results.split('\n'):
356 fields = line.split('|')
360 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
361 vm_ip_dict[vm_name] = ip
365 def remove_old_vms(self, vm_list, prefix):
367 For vms with format name_timestamp, only keep the one with the latest timestamp.
369 zdcpe1cpe01brgemu01_201805222148 (drop this)
370 zdcpe1cpe01brgemu01_201805222229 (keep this)
371 zdcpe1cpe01gw01_201805162201
374 same_type_vm_dict = {}
376 fields = vm.split('_')
377 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
378 if vm > same_type_vm_dict.get(fields[0], '0'):
379 same_type_vm_dict[fields[0]] = vm
381 new_vm_list.append(vm)
383 new_vm_list.extend(same_type_vm_dict.values())
386 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
388 for keyword in vm_name_keyword_list:
389 for vm, ip in all_vm_ip_dict.items():
391 vm_ip_dict[keyword] = ip
395 def del_vgmux_ves_mode(self):
396 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
397 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
398 self.logger.debug('%s', r)
400 def del_vgmux_ves_collector(self):
401 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
402 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
403 self.logger.debug('%s', r)
405 def set_vgmux_ves_collector(self ):
406 url = self.vpp_ves_url.format(self.hosts['mux'])
408 {'server-addr': self.hosts[self.dcae_ves_collector_name],
409 'server-port': '30235' if self.oom_mode else '8081',
410 'read-interval': '10',
414 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
415 self.logger.debug('%s', r)
417 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
418 url = self.vpp_ves_url.format(self.hosts['mux'])
420 {"working-mode": "demo",
421 "base-packet-loss": str(lossrate),
422 "source-name": vg_vnf_instance_name
425 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
426 self.logger.debug('%s', r)
428 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
429 def get_vxlan_interfaces(self, ip, print_info=False):
430 url = self.vpp_inf_url.format(ip)
431 self.logger.debug('url is this: %s', url)
432 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
433 data = r.json()['interfaces']['interface']
436 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
437 print(json.dumps(inf, indent=4, sort_keys=True))
439 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
441 # delete all VxLAN interfaces of each hosts
442 def delete_vxlan_interfaces(self, host_dic):
443 for host, ip in host_dic.items():
445 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
446 inf_list = self.get_vxlan_interfaces(ip)
450 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
451 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
452 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
457 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
458 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
459 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
461 if len(self.get_vxlan_interfaces(ip)) > 0:
462 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
466 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
470 def save_object(obj, filepathname):
471 with open(filepathname, 'wb') as fout:
472 pickle.dump(obj, fout)
475 def load_object(filepathname):
476 with open(filepathname, 'rb') as fin:
477 return pickle.load(fin)
480 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
481 with open(vnf_template_file) as json_input:
482 json_data = json.load(json_input)
483 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
484 for param in param_list:
485 if param['vnf-parameter-name'] in vnf_parameter_name_list:
486 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
487 number = int(ipaddr_or_vni[-1])
492 ipaddr_or_vni[-1] = str(number)
493 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
495 assert json_data is not None
496 with open(vnf_template_file, 'w') as json_output:
497 json.dump(json_data, json_output, indent=4, sort_keys=True)
499 def save_preload_data(self, preload_data):
500 self.save_object(preload_data, self.preload_dict_file)
502 def load_preload_data(self):
503 return self.load_object(self.preload_dict_file)
505 def save_vgmux_vnf_name(self, vgmux_vnf_name):
506 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
508 def load_vgmux_vnf_name(self):
509 return self.load_object(self.vgmux_vnf_name_file)