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': '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'
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'
42 sdnc_controller_pod = 'dev-sdnc-sdnc-0'
44 #############################################################################################
46 template_variable_symbol = '${'
47 cpe_vm_prefix = 'zdcpe'
48 #############################################################################################
49 # preloading network config
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']
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'
66 def __init__(self, extra_host_names=None):
67 self.logger = logging.getLogger(__name__)
68 self.logger.info('Initializing configuration')
70 # 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
71 self.sdnc_brg_bng_ip = self.get_pod_node_oam_ip(self.sdnc_controller_pod)[1]
72 # OOM: this is a k8 host external IP
73 self.oom_so_sdnc_aai_ip = '10.12.5.18'
74 # OOM: this is a k8 host external IP can be same as oom_so_sdnc_aai_ip
75 self.oom_dcae_ves_collector = '10.12.5.18'
76 # OOM: this is a k8 host external IP can be same as oom_so_sdnc_aai_ip
77 self.mr_ip_addr = '10.12.5.18'
78 self.mr_ip_port = '30227'
79 self.so_nbi_port = '30277' if self.oom_mode else '8080'
80 self.sdnc_preloading_port = '30202' if self.oom_mode else '8282'
81 self.aai_query_port = '30233' if self.oom_mode else '8443'
82 self.sniro_port = '30288' if self.oom_mode else '8080'
84 self.host_names = ['so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name]
86 self.host_names.extend(extra_host_names)
88 self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len)
89 # this is the keyword used to name vgw stack, must not be used in other stacks
90 self.vgw_name_keyword = 'base_vcpe_vgw'
91 # this is the file that will keep the index of last assigned SO name
92 self.vgw_vfmod_name_index_file= '__var/vgw_vfmod_name_index'
93 self.svc_instance_uuid_file = '__var/svc_instance_uuid'
94 self.preload_dict_file = '__var/preload_dict'
95 self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
96 self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
97 self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
98 self.instance_name_prefix = {
99 'service': 'vcpe_svc',
100 'network': 'vcpe_net',
102 'vfmodule': 'vcpe_vfmodule'
104 self.aai_userpass = 'AAI', 'AAI'
105 self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh'
106 self.os_tenant_id = self.cloud['--os-tenant-id']
107 self.os_region_name = self.cloud['--os-region-name']
108 self.common_preload_config['pub_key'] = self.pub_key
109 self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
110 self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
111 self.homing_solution = 'sniro' # value is either 'sniro' or 'oof'
112 # self.homing_solution = 'oof'
113 self.customer_location_used_by_oof = {
114 "customerLatitude": "32.897480",
115 "customerLongitude": "-97.040443",
116 "customerName": "some_company"
119 #############################################################################################
121 self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
122 self.sdnc_db_name = 'sdnctl'
123 self.sdnc_db_user = 'sdnctl'
124 self.sdnc_db_pass = 'gamma'
125 self.sdnc_db_port = '32774'
126 self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
127 self.sdnc_preload_network_url = 'http://' + self.hosts['sdnc'] + \
128 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
129 self.sdnc_preload_vnf_url = 'http://' + self.hosts['sdnc'] + \
130 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
131 self.sdnc_preload_gra_url = 'http://' + self.hosts['sdnc'] + \
132 ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
133 self.sdnc_ar_cleanup_url = 'http://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
134 '/restconf/config/GENERIC-RESOURCE-API:'
136 #############################################################################################
137 # SO urls, note: do NOT add a '/' at the end of the url
138 self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances',
139 'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infraserviceInstantiation/v7/serviceInstances'}
140 self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/orchestrationRequests/v6'
141 self.so_userpass = 'InfraPortalClient', 'password1$'
142 self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
143 self.so_db_name = 'catalogdb'
144 self.so_db_user = 'root'
145 self.so_db_pass = 'password'
146 self.so_db_port = '30252' if self.oom_mode else '32769'
148 self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
149 self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
150 self.vpp_api_userpass = ('admin', 'admin')
151 self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
153 def headbridge(self, openstack_stack_name, svc_instance_uuid):
155 Add vserver information to AAI
157 self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
158 if not self.oom_mode:
159 cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
160 ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
161 self.logger.debug('%s', ret)
163 print('To add vGMUX vserver info to AAI, do the following:')
164 print('- ssh to rancher')
166 print('- cd /root/oom/kubernetes/robot')
167 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
169 def get_brg_mac_from_sdnc(self):
171 Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
172 Note that there might be multiple BRGs, the most recently instantiated BRG always has the largest IP address.
174 cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
175 host=self.hosts['sdnc'], port=self.sdnc_db_port)
176 cursor = cnx.cursor()
177 query = "SELECT * from DHCP_MAP"
178 cursor.execute(query)
180 self.logger.debug('DHCP_MAP table in SDNC')
183 for mac, ip in cursor:
184 self.logger.debug(mac + ':' + ip)
185 this_host = int(ip.split('.')[-1])
195 def execute_cmds_sdnc_db(self, cmds):
196 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
197 self.hosts['sdnc'], self.sdnc_db_port)
199 def execute_cmds_so_db(self, cmds):
200 self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
201 self.hosts['so'], self.so_db_port)
203 def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
204 cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
205 cursor = cnx.cursor()
207 self.logger.debug(cmd)
209 self.logger.debug('%s', cursor)
214 def find_file(self, file_name_keyword, file_ext, search_dir):
216 :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra
217 :param file_ext: e.g., csar, json
218 :param search_dir path to search
219 :return: path name of the file
221 file_name_keyword = file_name_keyword.lower()
222 file_ext = file_ext.lower()
223 if not file_ext.startswith('.'):
224 file_ext = '.' + file_ext
227 for file_name in os.listdir(search_dir):
228 file_name_lower = file_name.lower()
229 if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
231 self.logger.error('Multiple files found for *{0}*.{1} in '
232 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
234 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
239 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
243 def network_name_to_subnet_name(network_name):
245 :param network_name: example: vcpe_net_cpe_signal_201711281221
246 :return: vcpe_net_cpe_signal_subnet_201711281221
248 fields = network_name.split('_')
249 fields.insert(-1, 'subnet')
250 return '_'.join(fields)
252 def set_network_name(self, network_name):
253 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
254 openstackcmd = 'openstack ' + param
255 cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
258 def set_subnet_name(self, network_name):
260 Example: network_name = vcpe_net_cpe_signal_201711281221
261 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
264 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
265 openstackcmd = 'openstack ' + param
267 # expected results: | subnets | subnet_id |
268 subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
269 if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
270 subnet_id = subnet_info[2].strip()
271 subnet_name = self.network_name_to_subnet_name(network_name)
272 cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
274 self.logger.info("Subnet name set to: " + subnet_name)
277 self.logger.error("Can't get subnet info from network name: " + network_name)
280 def is_node_in_aai(self, node_type, node_uuid):
282 search_node_type = None
283 if node_type == 'service':
284 search_node_type = 'service-instance'
285 key = 'service-instance-id'
286 elif node_type == 'vnf':
287 search_node_type = 'generic-vnf'
290 logging.error('Invalid node_type: ' + node_type)
293 url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
294 self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
296 headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
297 requests.packages.urllib3.disable_warnings()
298 r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
300 self.logger.debug('aai query: ' + url)
301 self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
302 return 'result-data' in response
305 def extract_ip_from_str(net_addr, net_addr_len, sz):
307 :param net_addr: e.g. 10.5.12.0
308 :param net_addr_len: e.g. 24
310 :return: the first IP address matching the network, e.g. 10.5.12.3
312 network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
313 ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
315 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
316 if this_net == network:
320 def get_pod_node_oam_ip(self, pod):
322 :Assuming kubectl is available
323 :param pod: pod name as a string, e.g. 'dev-sdnc-sdnc-0'
324 :return pod's node oam ip (10.0.0.0/16)
326 cmd = "kubectl -n onap describe pod {0} |grep Node:|cut -d'/' -f2".format(pod)
327 ret = commands.getstatusoutput(cmd)
328 self.logger.debug("cmd = %s, ret = %s", cmd, ret)
331 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
333 :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
334 :param net_addr: e.g. 10.12.5.0
335 :param net_addr_len: e.g. 24
336 :return: dictionary {keyword: ip}
339 net_addr = self.external_net_addr
342 net_addr_len = self.external_net_prefix_len
344 param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
345 openstackcmd = 'nova ' + param + ' list'
346 self.logger.debug(openstackcmd)
348 results = os.popen(openstackcmd).read()
349 all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
350 latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
351 latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
352 ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
354 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
356 if len(ip_dict) != len(keywords):
357 self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
358 self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
359 self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 316')
363 def get_oom_onap_vm_ip(self, keywords):
365 onap_vm_list = set(['so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
367 if vm in onap_vm_list:
368 vm_ip[vm] = self.oom_so_sdnc_aai_ip
371 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
373 for line in novalist_results.split('\n'):
374 fields = line.split('|')
378 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
379 vm_ip_dict[vm_name] = ip
383 def remove_old_vms(self, vm_list, prefix):
385 For vms with format name_timestamp, only keep the one with the latest timestamp.
387 zdcpe1cpe01brgemu01_201805222148 (drop this)
388 zdcpe1cpe01brgemu01_201805222229 (keep this)
389 zdcpe1cpe01gw01_201805162201
392 same_type_vm_dict = {}
394 fields = vm.split('_')
395 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
396 if vm > same_type_vm_dict.get(fields[0], '0'):
397 same_type_vm_dict[fields[0]] = vm
399 new_vm_list.append(vm)
401 new_vm_list.extend(same_type_vm_dict.values())
404 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
406 for keyword in vm_name_keyword_list:
407 for vm, ip in all_vm_ip_dict.items():
409 vm_ip_dict[keyword] = ip
413 def del_vgmux_ves_mode(self):
414 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
415 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
416 self.logger.debug('%s', r)
418 def del_vgmux_ves_collector(self):
419 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
420 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
421 self.logger.debug('%s', r)
423 def set_vgmux_ves_collector(self ):
424 url = self.vpp_ves_url.format(self.hosts['mux'])
426 {'server-addr': self.hosts[self.dcae_ves_collector_name],
427 'server-port': '30235' if self.oom_mode else '8081',
428 'read-interval': '10',
432 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
433 self.logger.debug('%s', r)
435 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
436 url = self.vpp_ves_url.format(self.hosts['mux'])
438 {"working-mode": "demo",
439 "base-packet-loss": str(lossrate),
440 "source-name": vg_vnf_instance_name
443 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
444 self.logger.debug('%s', r)
446 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
447 def get_vxlan_interfaces(self, ip, print_info=False):
448 url = self.vpp_inf_url.format(ip)
449 self.logger.debug('url is this: %s', url)
450 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
451 data = r.json()['interfaces']['interface']
454 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
455 print(json.dumps(inf, indent=4, sort_keys=True))
457 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
459 # delete all VxLAN interfaces of each hosts
460 def delete_vxlan_interfaces(self, host_dic):
461 for host, ip in host_dic.items():
463 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
464 inf_list = self.get_vxlan_interfaces(ip)
468 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
469 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
470 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
475 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
476 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
477 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
479 if len(self.get_vxlan_interfaces(ip)) > 0:
480 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
484 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
488 def save_object(obj, filepathname):
489 with open(filepathname, 'wb') as fout:
490 pickle.dump(obj, fout)
493 def load_object(filepathname):
494 with open(filepathname, 'rb') as fin:
495 return pickle.load(fin)
498 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
499 with open(vnf_template_file) as json_input:
500 json_data = json.load(json_input)
501 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
502 for param in param_list:
503 if param['vnf-parameter-name'] in vnf_parameter_name_list:
504 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
505 number = int(ipaddr_or_vni[-1])
510 ipaddr_or_vni[-1] = str(number)
511 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
513 assert json_data is not None
514 with open(vnf_template_file, 'w') as json_output:
515 json.dump(json_data, json_output, indent=4, sort_keys=True)
517 def save_preload_data(self, preload_data):
518 self.save_object(preload_data, self.preload_dict_file)
520 def load_preload_data(self):
521 return self.load_object(self.preload_dict_file)
523 def save_vgmux_vnf_name(self, vgmux_vnf_name):
524 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
526 def load_vgmux_vnf_name(self):
527 return self.load_object(self.vgmux_vnf_name_file)