11 import mysql.connector
15 from novaclient import client as openstackclient
16 from kubernetes import client, config
17 from netaddr import IPAddress, IPNetwork
19 ######################################################################
20 # Parts which must be updated / cross-checked during each deployment #
21 # are marked as CHANGEME #
22 ######################################################################
25 #############################################################################################
26 # Set network prefix of k8s host external address; it's used for pod public IP autodetection
27 # but can be overriden from user in case of autodetection failure
28 external_net_addr = '10.12.0.0'
29 external_net_prefix_len = 16
31 #############################################################################################
32 # set the openstack cloud access credentials here
35 ###########################
36 # set Openstack credentials
39 '--os-auth-url': 'http://10.12.25.2:5000',
40 '--os-username': 'kxi',
41 '--os-user-domain-id': 'default',
42 '--os-project-domain-id': 'default',
43 '--os-tenant-id': 'bc43d50ffcb84750bac0c1707a9a765b' if oom_mode else '1e097c6713e74fd7ac8e4295e605ee1e',
44 '--os-region-name': 'RegionOne',
45 '--os-password': 'n3JhGMGuDzD8',
46 '--os-project-domain-name': 'Integration-SB-03' if oom_mode else 'Integration-SB-07',
47 '--os-identity-api-version': '3'
50 ############################################################################
51 # set oam and public network which must exist in openstack before deployment
53 common_preload_config = {
54 'oam_onap_net': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
55 'oam_onap_subnet': 'oam_network_2No2' if oom_mode else 'oam_onap_lAky',
56 'public_net': 'external',
57 'public_net_id': '971040b2-7059-49dc-b220-4fab50cb2ad4'
60 #############################################################################
61 # Set name of Onap's k8s namespace and sdnc controller pod
63 onap_namespace = 'onap'
64 onap_environment = 'dev'
65 sdnc_controller_pod = '-'.join([onap_environment, 'sdnc-sdnc-0'])
67 template_variable_symbol = '${'
68 cpe_vm_prefix = 'zdcpe'
70 #############################################################################################
71 # preloading network config
73 # value = [subnet_start_ip, subnet_gateway_ip]
74 preload_network_config = {
75 'cpe_public': ['10.2.0.2', '10.2.0.1'],
76 'cpe_signal': ['10.4.0.2', '10.4.0.1'],
77 'brg_bng': ['10.3.0.2', '10.3.0.1'],
78 'bng_mux': ['10.1.0.10', '10.1.0.1'],
79 'mux_gw': ['10.5.0.10', '10.5.0.1']
82 dcae_ves_collector_name = 'dcae-bootstrap'
83 global_subscriber_id = 'SDN-ETHERNET-INTERNET'
84 project_name = 'Project-Demonstration'
85 owning_entity_id = '520cc603-a3c4-4ec2-9ef4-ca70facd79c0'
86 owning_entity_name = 'OE-Demonstration1'
88 def __init__(self, extra_host_names=None):
89 self.logger = logging.getLogger(__name__)
90 self.logger.setLevel(logging.DEBUG)
91 self.logger.info('Initializing configuration')
93 ##################################################################################################################################
94 # following param must be updated e.g. from csar file (grep for VfModuleModelInvariantUuid string) before vcpe.py customer call !!
95 # vgw_VfModuleModelInvariantUuid is in rescust service csar,
96 # look in service-VcpesvcRescust1118-template.yml for groups vgw module metadata. TODO: read this value automatically
98 self.vgw_VfModuleModelInvariantUuid = '26d6a718-17b2-4ba8-8691-c44343b2ecd2'
100 # OOM: this is the address that the brg and bng will nat for sdnc access - 10.0.0.x address of k8 host for sdnc-0 container
101 self.sdnc_oam_ip = self.get_pod_node_oam_ip(self.sdnc_controller_pod)
102 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
103 self.oom_so_sdnc_aai_ip = self.get_pod_node_public_ip(self.sdnc_controller_pod)
104 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
105 self.oom_dcae_ves_collector = self.oom_so_sdnc_aai_ip
106 # OOM: this is a k8s host external IP, e.g. oom-k8s-01 IP
107 self.mr_ip_addr = self.oom_so_sdnc_aai_ip
108 self.mr_ip_port = '30227'
109 self.so_nbi_port = '30277' if self.oom_mode else '8080'
110 self.sdnc_preloading_port = '30267' if self.oom_mode else '8282'
111 self.aai_query_port = '30233' if self.oom_mode else '8443'
112 self.sniro_port = '30288' if self.oom_mode else '8080'
114 self.host_names = ['sdc', 'so', 'sdnc', 'robot', 'aai-inst1', self.dcae_ves_collector_name]
116 self.host_names.extend(extra_host_names)
118 self.hosts = self.get_vm_ip(self.host_names, self.external_net_addr, self.external_net_prefix_len)
119 # this is the keyword used to name vgw stack, must not be used in other stacks
120 self.vgw_name_keyword = 'base_vcpe_vgw'
121 # this is the file that will keep the index of last assigned SO name
122 self.vgw_vfmod_name_index_file= '__var/vgw_vfmod_name_index'
123 self.svc_instance_uuid_file = '__var/svc_instance_uuid'
124 self.preload_dict_file = '__var/preload_dict'
125 self.vgmux_vnf_name_file = '__var/vgmux_vnf_name'
126 self.product_family_id = 'f9457e8c-4afd-45da-9389-46acd9bf5116'
127 self.custom_product_family_id = 'a9a77d5a-123e-4ca2-9eb9-0b015d2ee0fb'
128 self.instance_name_prefix = {
129 'service': 'vcpe_svc',
130 'network': 'vcpe_net',
132 'vfmodule': 'vcpe_vfmodule'
134 self.aai_userpass = 'AAI', 'AAI'
136 ############################################################################################################
137 # following key is overriding public key from vCPE heat templates, it's important to use correct one in here
139 self.pub_key = 'ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDKXDgoo3+WOqcUG8/5uUbk81+yczgwC4Y8ywTmuQqbNxlY1oQ0YxdMUqUnhitSXs5S/yRuAVOYHwGg2mCs20oAINrP+mxBI544AMIb9itPjCtgqtE2EWo6MmnFGbHB4Sx3XioE7F4VPsh7japsIwzOjbrQe+Mua1TGQ5d4nfEOQaaglXLLPFfuc7WbhbJbK6Q7rHqZfRcOwAMXgDoBqlyqKeiKwnumddo2RyNT8ljYmvB6buz7KnMinzo7qB0uktVT05FH9Rg0CTWH5norlG5qXgP2aukL0gk1ph8iAt7uYLf1ktp+LJI2gaF6L0/qli9EmVCSLr1uJ38Q8CBflhkh'
141 self.os_tenant_id = self.cloud['--os-tenant-id']
142 self.os_region_name = self.cloud['--os-region-name']
143 self.common_preload_config['pub_key'] = self.pub_key
144 self.sniro_url = 'http://' + self.hosts['robot'] + ':' + self.sniro_port + '/__admin/mappings'
145 self.sniro_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
146 self.homing_solution = 'sniro' # value is either 'sniro' or 'oof'
147 # self.homing_solution = 'oof'
148 self.customer_location_used_by_oof = {
149 "customerLatitude": "32.897480",
150 "customerLongitude": "-97.040443",
151 "customerName": "some_company"
154 #############################################################################################
156 self.sdc_be_port = '30204'
157 self.sdc_be_request_userpass = 'vid', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
158 self.sdc_be_request_headers = {'X-ECOMP-InstanceID': 'VID'}
159 self.sdc_be_url_prefix = 'https://' + self.hosts['sdc'] + ':' + self.sdc_be_port
160 self.sdc_service_list_url = self.sdc_be_url_prefix + '/sdc/v1/catalog/services'
162 self.sdc_fe_port = '30207'
163 self.sdc_fe_request_userpass = 'beep', 'boop'
164 self.sdc_fe_request_headers = {'USER_ID': 'demo', 'Content-Type': 'application/json'}
165 self.sdc_fe_url_prefix = 'https://' + self.hosts['sdc'] + ':' + self.sdc_fe_port
166 self.sdc_get_category_list_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/categories'
167 self.sdc_create_allotted_resource_subcategory_url = self.sdc_fe_url_prefix + '/sdc1/feProxy/rest/v1/category/resources/resourceNewCategory.allotted%20resource/subCategory'
169 #############################################################################################
171 self.sdnc_userpass = 'admin', 'Kp8bJ4SXszM0WXlhak3eHlcse2gAw84vaoGGmJvUy2U'
172 self.sdnc_db_name = 'sdnctl'
173 self.sdnc_db_user = 'sdnctl'
174 self.sdnc_db_pass = 'gamma'
175 self.sdnc_db_port = '32774'
176 self.sdnc_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
177 self.sdnc_preload_network_url = 'https://' + self.hosts['sdnc'] + \
178 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-network-topology-operation'
179 self.sdnc_preload_vnf_url = 'https://' + self.hosts['sdnc'] + \
180 ':' + self.sdnc_preloading_port + '/restconf/operations/VNF-API:preload-vnf-topology-operation'
181 self.sdnc_preload_gra_url = 'https://' + self.hosts['sdnc'] + \
182 ':' + self.sdnc_preloading_port + '/restconf/operations/GENERIC-RESOURCE-API:preload-vf-module-topology-operation'
183 self.sdnc_ar_cleanup_url = 'https://' + self.hosts['sdnc'] + ':' + self.sdnc_preloading_port + \
184 '/restconf/config/GENERIC-RESOURCE-API:'
186 #############################################################################################
187 # SO urls, note: do NOT add a '/' at the end of the url
188 self.so_req_api_url = {'v4': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances',
189 'v5': 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/serviceInstantiation/v7/serviceInstances'}
190 self.so_check_progress_api_url = 'http://' + self.hosts['so'] + ':' + self.so_nbi_port + '/onap/so/infra/orchestrationRequests/v6'
191 self.so_userpass = 'InfraPortalClient', 'password1$'
192 self.so_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
193 self.so_db_name = 'catalogdb'
194 self.so_db_user = 'root'
195 self.so_db_pass = 'secretpassword'
196 self.so_db_port = '30252' if self.oom_mode else '32769'
198 self.vpp_inf_url = 'http://{0}:8183/restconf/config/ietf-interfaces:interfaces'
199 self.vpp_api_headers = {'Content-Type': 'application/json', 'Accept': 'application/json'}
200 self.vpp_api_userpass = ('admin', 'admin')
201 self.vpp_ves_url= 'http://{0}:8183/restconf/config/vesagent:vesagent'
203 #############################################################################################
205 self.policy_userpass = ('healthcheck', 'zb!XztG34')
206 self.policy_headers = {'Accept': 'application/json', 'Content-Type': 'application/json'}
207 self.policy_api_url = 'https://{0}:6969/policy/api/v1/policytypes/onap.policies.controlloop.Operational/versions/1.0.0/policies'
208 self.policy_pap_get_url = 'https://{0}:6969/policy/pap/v1/pdps'
209 self.policy_pap_json = {'policies': [{'policy-id': 'operational.vcpe'}]}
210 self.policy_pap_post_url = self.policy_pap_get_url + '/policies'
211 self.policy_api_service_name = 'policy-api'
212 self.policy_pap_service_name = 'policy-pap'
214 #############################################################################################
215 # MARIADB-GALERA settings
216 self.mariadb_galera_endpoint_ip = self.get_k8s_service_endpoint_info('mariadb-galera','ip')
217 self.mariadb_galera_endpoint_port = self.get_k8s_service_endpoint_info('mariadb-galera','port')
219 def heatbridge(self, openstack_stack_name, svc_instance_uuid):
221 Add vserver information to AAI
223 self.logger.info('Adding vServer information to AAI for {0}'.format(openstack_stack_name))
224 if not self.oom_mode:
225 cmd = '/opt/demo.sh heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid)
226 ret = commands.getstatusoutput("ssh -i onap_dev root@{0} '{1}'".format(self.hosts['robot'], cmd))
227 self.logger.debug('%s', ret)
229 print('To add vGMUX vserver info to AAI, do the following:')
230 print('- ssh to rancher')
232 print('- cd /root/oom/kubernetes/robot')
233 print('- ./demo-k8s.sh onap heatbridge {0} {1} vCPE'.format(openstack_stack_name, svc_instance_uuid))
235 def get_brg_mac_from_sdnc(self):
237 Check table DHCP_MAP in the SDNC DB. Find the newly instantiated BRG MAC address.
238 Note that there might be multiple BRGs, the most recently instantiated BRG always has the largest IP address.
240 cnx = mysql.connector.connect(user=self.sdnc_db_user, password=self.sdnc_db_pass, database=self.sdnc_db_name,
241 host=self.hosts['sdnc'], port=self.sdnc_db_port)
242 cursor = cnx.cursor()
243 query = "SELECT * from DHCP_MAP"
244 cursor.execute(query)
246 self.logger.debug('DHCP_MAP table in SDNC')
249 for mac, ip in cursor:
250 self.logger.debug(mac + ':' + ip)
251 this_host = int(ip.split('.')[-1])
261 def execute_cmds_mariadb(self, cmds):
262 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass,
263 self.sdnc_db_name, self.mariadb_galera_endpoint_ip,
264 self.mariadb_galera_endpoint_port)
266 def execute_cmds_sdnc_db(self, cmds):
267 self.execute_cmds_db(cmds, self.sdnc_db_user, self.sdnc_db_pass, self.sdnc_db_name,
268 self.hosts['sdnc'], self.sdnc_db_port)
270 def execute_cmds_so_db(self, cmds):
271 self.execute_cmds_db(cmds, self.so_db_user, self.so_db_pass, self.so_db_name,
272 self.hosts['so'], self.so_db_port)
274 def execute_cmds_db(self, cmds, dbuser, dbpass, dbname, host, port):
275 cnx = mysql.connector.connect(user=dbuser, password=dbpass, database=dbname, host=host, port=port)
276 cursor = cnx.cursor()
278 self.logger.debug(cmd)
280 self.logger.debug('%s', cursor)
285 def find_file(self, file_name_keyword, file_ext, search_dir):
287 :param file_name_keyword: keyword used to look for the csar file, case insensitive matching, e.g, infra
288 :param file_ext: e.g., csar, json
289 :param search_dir path to search
290 :return: path name of the file
292 file_name_keyword = file_name_keyword.lower()
293 file_ext = file_ext.lower()
294 if not file_ext.startswith('.'):
295 file_ext = '.' + file_ext
298 for file_name in os.listdir(search_dir):
299 file_name_lower = file_name.lower()
300 if file_name_keyword in file_name_lower and file_name_lower.endswith(file_ext):
302 self.logger.error('Multiple files found for *{0}*.{1} in '
303 'directory {2}'.format(file_name_keyword, file_ext, search_dir))
305 filenamepath = os.path.abspath(os.path.join(search_dir, file_name))
310 self.logger.error("Cannot find *{0}*{1} in directory {2}".format(file_name_keyword, file_ext, search_dir))
314 def network_name_to_subnet_name(network_name):
316 :param network_name: example: vcpe_net_cpe_signal_201711281221
317 :return: vcpe_net_cpe_signal_subnet_201711281221
319 fields = network_name.split('_')
320 fields.insert(-1, 'subnet')
321 return '_'.join(fields)
323 def set_network_name(self, network_name):
324 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
325 openstackcmd = 'openstack ' + param
326 cmd = ' '.join([openstackcmd, 'network set --name', network_name, 'ONAP-NW1'])
329 def set_subnet_name(self, network_name):
331 Example: network_name = vcpe_net_cpe_signal_201711281221
332 set subnet name to vcpe_net_cpe_signal_subnet_201711281221
335 param = ' '.join([k + ' ' + v for k, v in self.cloud.items()])
336 openstackcmd = 'openstack ' + param
338 # expected results: | subnets | subnet_id |
339 subnet_info = os.popen(openstackcmd + ' network show ' + network_name + ' |grep subnets').read().split('|')
340 if len(subnet_info) > 2 and subnet_info[1].strip() == 'subnets':
341 subnet_id = subnet_info[2].strip()
342 subnet_name = self.network_name_to_subnet_name(network_name)
343 cmd = ' '.join([openstackcmd, 'subnet set --name', subnet_name, subnet_id])
345 self.logger.info("Subnet name set to: " + subnet_name)
348 self.logger.error("Can't get subnet info from network name: " + network_name)
351 def set_closed_loop_policy(self, policy_template_file):
352 # Gather policy services cluster ips
353 p_api_cluster_ip = self.get_k8s_service_cluster_ip(self.policy_api_service_name)
354 p_pap_cluster_ip = self.get_k8s_service_cluster_ip(self.policy_pap_service_name)
356 # Read policy json from file
357 with open(policy_template_file) as f:
359 policy_json = json.load(f)
361 self.logger.error(policy_template_file + " doesn't seem to contain valid JSON data")
364 # Check policy already applied
365 requests.packages.urllib3.disable_warnings()
366 policy_exists_req = requests.get(self.policy_pap_get_url.format(
367 p_pap_cluster_ip), auth=self.policy_userpass,
368 verify=False, headers=self.policy_headers)
369 if policy_exists_req.status_code != 200:
370 self.logger.error('Failure in checking CL policy existence. '
371 'Policy-pap responded with HTTP code {0}'.format(
372 policy_exists_req.status_code))
376 policy_exists_json = policy_exists_req.json()
377 except ValueError as e:
378 self.logger.error('Policy-pap request failed: ' + e.message)
382 assert policy_exists_json['groups'][0]['pdpSubgroups'] \
383 [1]['policies'][0]['name'] != 'operational.vcpe'
384 except AssertionError:
385 self.logger.info('vCPE closed loop policy already exists, not applying')
388 pass # policy doesn't exist
391 policy_create_req = requests.post(self.policy_api_url.format(
392 p_api_cluster_ip), auth=self.policy_userpass,
393 json=policy_json, verify=False,
394 headers=self.policy_headers)
395 # Get the policy id from policy-api response
396 if policy_create_req.status_code != 200:
397 self.logger.error('Failed creating policy. Policy-api responded'
398 ' with HTTP code {0}'.format(policy_create_req.status_code))
402 policy_version = json.loads(policy_create_req.text)['policy-version']
403 except (KeyError, ValueError):
404 self.logger.error('Policy API response not understood:')
405 self.logger.debug('\n' + str(policy_create_req.text))
407 # Inject the policy into Policy PAP
408 self.policy_pap_json['policies'].append({'policy-version': policy_version})
409 policy_insert_req = requests.post(self.policy_pap_post_url.format(
410 p_pap_cluster_ip), auth=self.policy_userpass,
411 json=self.policy_pap_json, verify=False,
412 headers=self.policy_headers)
413 if policy_insert_req.status_code != 200:
414 self.logger.error('Policy PAP request failed with HTTP code'
415 '{0}'.format(policy_insert_req.status_code))
417 self.logger.info('Successully pushed closed loop Policy')
419 def is_node_in_aai(self, node_type, node_uuid):
421 search_node_type = None
422 if node_type == 'service':
423 search_node_type = 'service-instance'
424 key = 'service-instance-id'
425 elif node_type == 'vnf':
426 search_node_type = 'generic-vnf'
429 logging.error('Invalid node_type: ' + node_type)
432 url = 'https://{0}:{1}/aai/v11/search/nodes-query?search-node-type={2}&filter={3}:EQUALS:{4}'.format(
433 self.hosts['aai-inst1'], self.aai_query_port, search_node_type, key, node_uuid)
435 headers = {'Content-Type': 'application/json', 'Accept': 'application/json', 'X-FromAppID': 'vCPE-Robot', 'X-TransactionId': 'get_aai_subscr'}
436 requests.packages.urllib3.disable_warnings()
437 r = requests.get(url, headers=headers, auth=self.aai_userpass, verify=False)
439 self.logger.debug('aai query: ' + url)
440 self.logger.debug('aai response:\n' + json.dumps(response, indent=4, sort_keys=True))
441 return 'result-data' in response
444 def extract_ip_from_str(net_addr, net_addr_len, sz):
446 :param net_addr: e.g. 10.5.12.0
447 :param net_addr_len: e.g. 24
449 :return: the first IP address matching the network, e.g. 10.5.12.3
451 network = ipaddress.ip_network(unicode('{0}/{1}'.format(net_addr, net_addr_len)), strict=False)
452 ip_list = re.findall(r'[0-9]+(?:\.[0-9]+){3}', sz)
454 this_net = ipaddress.ip_network(unicode('{0}/{1}'.format(ip, net_addr_len)), strict=False)
455 if this_net == network:
459 def get_pod_node_oam_ip(self, pod):
461 :Assuming kubectl is available and configured by default config (~/.kube/config)
462 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
463 :return pod's cluster node oam ip (10.0.0.0/16)
466 config.load_kube_config()
467 api = client.CoreV1Api()
468 kslogger = logging.getLogger('kubernetes')
469 kslogger.setLevel(logging.INFO)
470 res = api.list_pod_for_all_namespaces()
472 if pod in i.metadata.name:
473 self.logger.debug("found {0}\t{1}\t{2}".format(i.metadata.name, i.status.host_ip, i.spec.node_name))
474 ret = i.status.host_ip
478 ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node OAM IP address(10.0.0.0/16): ")
481 def get_pod_node_public_ip(self, pod):
483 :Assuming kubectl is available and configured by default config (~/.kube/config)
484 :param pod: pod name substring, e.g. 'sdnc-sdnc-0'
485 :return pod's cluster node public ip (i.e. 10.12.0.0/16)
488 config.load_kube_config()
489 api = client.CoreV1Api()
490 kslogger = logging.getLogger('kubernetes')
491 kslogger.setLevel(logging.INFO)
492 res = api.list_pod_for_all_namespaces()
494 if pod in i.metadata.name:
495 ret = self.get_vm_public_ip_by_nova(i.spec.node_name)
496 self.logger.debug("found node {0} public ip: {1}".format(i.spec.node_name, ret))
500 ret = raw_input("Enter " + self.sdnc_controller_pod + " pod cluster node public IP address(i.e. " + self.external_net_addr + "): ")
503 def get_vm_public_ip_by_nova(self, vm):
505 This method uses openstack nova api to retrieve vm public ip
509 subnet = IPNetwork('{0}/{1}'.format(self.external_net_addr, self.external_net_prefix_len))
510 nova = openstackclient.Client(2, self.cloud['--os-username'], self.cloud['--os-password'], self.cloud['--os-tenant-id'], self.cloud['--os-auth-url'])
511 for i in nova.servers.list():
513 for k, v in i.networks.items():
515 if IPAddress(ip) in subnet:
519 def get_vm_ip(self, keywords, net_addr=None, net_addr_len=None):
521 :param keywords: list of keywords to search for vm, e.g. ['bng', 'gmux', 'brg']
522 :param net_addr: e.g. 10.12.5.0
523 :param net_addr_len: e.g. 24
524 :return: dictionary {keyword: ip}
527 net_addr = self.external_net_addr
530 net_addr_len = self.external_net_prefix_len
532 param = ' '.join([k + ' ' + v for k, v in self.cloud.items() if 'identity' not in k])
533 openstackcmd = 'nova ' + param + ' list'
534 self.logger.debug(openstackcmd)
536 results = os.popen(openstackcmd).read()
537 all_vm_ip_dict = self.extract_vm_ip_as_dict(results, net_addr, net_addr_len)
538 latest_vm_list = self.remove_old_vms(all_vm_ip_dict.keys(), self.cpe_vm_prefix)
539 latest_vm_ip_dict = {vm: all_vm_ip_dict[vm] for vm in latest_vm_list}
540 ip_dict = self.select_subset_vm_ip(latest_vm_ip_dict, keywords)
542 ip_dict.update(self.get_oom_onap_vm_ip(keywords))
544 if len(ip_dict) != len(keywords):
545 self.logger.error('Cannot find all desired IP addresses for %s.', keywords)
546 self.logger.error(json.dumps(ip_dict, indent=4, sort_keys=True))
547 self.logger.error('Temporarily continue.. remember to check back vcpecommon.py line: 396')
551 def get_oom_onap_vm_ip(self, keywords):
553 onap_vm_list = set(['sdc', 'so', 'sdnc', 'aai-inst1', 'robot', self.dcae_ves_collector_name])
555 if vm in onap_vm_list:
556 vm_ip[vm] = self.oom_so_sdnc_aai_ip
559 def get_k8s_service_cluster_ip(self, service):
561 Returns cluster IP for a given service
562 :param service: name of the service
565 config.load_kube_config()
566 api = client.CoreV1Api()
567 kslogger = logging.getLogger('kubernetes')
568 kslogger.setLevel(logging.INFO)
570 resp = api.read_namespaced_service(service, self.onap_namespace)
571 except client.rest.ApiException as e:
572 self.logger.error('Error while making k8s API request: ' + e.body)
575 return resp.spec.cluster_ip
577 def get_k8s_service_endpoint_info(self, service, subset):
579 Returns endpoint data for a given service and subset. If there
580 is more than one endpoint returns data for the first one from
581 the list that API returned.
582 :param service: name of the service
583 :param subset: subset name, one of "ip","port"
586 config.load_kube_config()
587 api = client.CoreV1Api()
588 kslogger = logging.getLogger('kubernetes')
589 kslogger.setLevel(logging.INFO)
591 resp = api.read_namespaced_endpoints(service, self.onap_namespace)
592 except client.rest.ApiException as e:
593 self.logger.error('Error while making k8s API request: ' + e.body)
597 return resp.subsets[0].addresses[0].ip
598 elif subset == "port":
599 return resp.subsets[0].ports[0].port
601 self.logger.error("Unsupported subset type")
603 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
605 for line in novalist_results.split('\n'):
606 fields = line.split('|')
610 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
611 vm_ip_dict[vm_name] = ip
615 def remove_old_vms(self, vm_list, prefix):
617 For vms with format name_timestamp, only keep the one with the latest timestamp.
619 zdcpe1cpe01brgemu01_201805222148 (drop this)
620 zdcpe1cpe01brgemu01_201805222229 (keep this)
621 zdcpe1cpe01gw01_201805162201
624 same_type_vm_dict = {}
626 fields = vm.split('_')
627 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
628 if vm > same_type_vm_dict.get(fields[0], '0'):
629 same_type_vm_dict[fields[0]] = vm
631 new_vm_list.append(vm)
633 new_vm_list.extend(same_type_vm_dict.values())
636 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
638 for keyword in vm_name_keyword_list:
639 for vm, ip in all_vm_ip_dict.items():
641 vm_ip_dict[keyword] = ip
645 def del_vgmux_ves_mode(self):
646 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
647 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
648 self.logger.debug('%s', r)
650 def del_vgmux_ves_collector(self):
651 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
652 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
653 self.logger.debug('%s', r)
655 def set_vgmux_ves_collector(self ):
656 url = self.vpp_ves_url.format(self.hosts['mux'])
658 {'server-addr': self.hosts[self.dcae_ves_collector_name],
659 'server-port': '30235' if self.oom_mode else '8081',
660 'read-interval': '10',
664 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
665 self.logger.debug('%s', r)
667 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
668 url = self.vpp_ves_url.format(self.hosts['mux'])
670 {"working-mode": "demo",
671 "base-packet-loss": str(lossrate),
672 "source-name": vg_vnf_instance_name
675 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
676 self.logger.debug('%s', r)
678 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
679 def get_vxlan_interfaces(self, ip, print_info=False):
680 url = self.vpp_inf_url.format(ip)
681 self.logger.debug('url is this: %s', url)
682 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
683 data = r.json()['interfaces']['interface']
686 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
687 print(json.dumps(inf, indent=4, sort_keys=True))
689 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
691 # delete all VxLAN interfaces of each hosts
692 def delete_vxlan_interfaces(self, host_dic):
693 for host, ip in host_dic.items():
695 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
696 inf_list = self.get_vxlan_interfaces(ip)
700 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
701 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
702 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
707 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
708 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
709 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
711 if len(self.get_vxlan_interfaces(ip)) > 0:
712 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
716 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
720 def save_object(obj, filepathname):
721 with open(filepathname, 'wb') as fout:
722 pickle.dump(obj, fout)
725 def load_object(filepathname):
726 with open(filepathname, 'rb') as fin:
727 return pickle.load(fin)
730 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
731 with open(vnf_template_file) as json_input:
732 json_data = json.load(json_input)
733 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
734 for param in param_list:
735 if param['vnf-parameter-name'] in vnf_parameter_name_list:
736 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
737 number = int(ipaddr_or_vni[-1])
742 ipaddr_or_vni[-1] = str(number)
743 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
745 assert json_data is not None
746 with open(vnf_template_file, 'w') as json_output:
747 json.dump(json_data, json_output, indent=4, sort_keys=True)
749 def save_preload_data(self, preload_data):
750 self.save_object(preload_data, self.preload_dict_file)
752 def load_preload_data(self):
753 return self.load_object(self.preload_dict_file)
755 def save_vgmux_vnf_name(self, vgmux_vnf_name):
756 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
758 def load_vgmux_vnf_name(self):
759 return self.load_object(self.vgmux_vnf_name_file)