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):
554 if vm in self.host_names:
555 vm_ip[vm] = self.oom_so_sdnc_aai_ip
558 def get_k8s_service_cluster_ip(self, service):
560 Returns cluster IP for a given service
561 :param service: name of the service
564 config.load_kube_config()
565 api = client.CoreV1Api()
566 kslogger = logging.getLogger('kubernetes')
567 kslogger.setLevel(logging.INFO)
569 resp = api.read_namespaced_service(service, self.onap_namespace)
570 except client.rest.ApiException as e:
571 self.logger.error('Error while making k8s API request: ' + e.body)
574 return resp.spec.cluster_ip
576 def get_k8s_service_endpoint_info(self, service, subset):
578 Returns endpoint data for a given service and subset. If there
579 is more than one endpoint returns data for the first one from
580 the list that API returned.
581 :param service: name of the service
582 :param subset: subset name, one of "ip","port"
585 config.load_kube_config()
586 api = client.CoreV1Api()
587 kslogger = logging.getLogger('kubernetes')
588 kslogger.setLevel(logging.INFO)
590 resp = api.read_namespaced_endpoints(service, self.onap_namespace)
591 except client.rest.ApiException as e:
592 self.logger.error('Error while making k8s API request: ' + e.body)
596 return resp.subsets[0].addresses[0].ip
597 elif subset == "port":
598 return resp.subsets[0].ports[0].port
600 self.logger.error("Unsupported subset type")
602 def extract_vm_ip_as_dict(self, novalist_results, net_addr, net_addr_len):
604 for line in novalist_results.split('\n'):
605 fields = line.split('|')
609 ip = self.extract_ip_from_str(net_addr, net_addr_len, ip_info)
610 vm_ip_dict[vm_name] = ip
614 def remove_old_vms(self, vm_list, prefix):
616 For vms with format name_timestamp, only keep the one with the latest timestamp.
618 zdcpe1cpe01brgemu01_201805222148 (drop this)
619 zdcpe1cpe01brgemu01_201805222229 (keep this)
620 zdcpe1cpe01gw01_201805162201
623 same_type_vm_dict = {}
625 fields = vm.split('_')
626 if vm.startswith(prefix) and len(fields) == 2 and len(fields[-1]) == len('201805222148') and fields[-1].isdigit():
627 if vm > same_type_vm_dict.get(fields[0], '0'):
628 same_type_vm_dict[fields[0]] = vm
630 new_vm_list.append(vm)
632 new_vm_list.extend(same_type_vm_dict.values())
635 def select_subset_vm_ip(self, all_vm_ip_dict, vm_name_keyword_list):
637 for keyword in vm_name_keyword_list:
638 for vm, ip in all_vm_ip_dict.items():
640 vm_ip_dict[keyword] = ip
644 def del_vgmux_ves_mode(self):
645 url = self.vpp_ves_url.format(self.hosts['mux']) + '/mode'
646 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
647 self.logger.debug('%s', r)
649 def del_vgmux_ves_collector(self):
650 url = self.vpp_ves_url.format(self.hosts['mux']) + '/config'
651 r = requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
652 self.logger.debug('%s', r)
654 def set_vgmux_ves_collector(self ):
655 url = self.vpp_ves_url.format(self.hosts['mux'])
657 {'server-addr': self.hosts[self.dcae_ves_collector_name],
658 'server-port': '30235' if self.oom_mode else '8081',
659 'read-interval': '10',
663 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
664 self.logger.debug('%s', r)
666 def set_vgmux_packet_loss_rate(self, lossrate, vg_vnf_instance_name):
667 url = self.vpp_ves_url.format(self.hosts['mux'])
669 {"working-mode": "demo",
670 "base-packet-loss": str(lossrate),
671 "source-name": vg_vnf_instance_name
674 r = requests.post(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass, json=data)
675 self.logger.debug('%s', r)
677 # return all the VxLAN interface names of BRG or vGMUX based on the IP address
678 def get_vxlan_interfaces(self, ip, print_info=False):
679 url = self.vpp_inf_url.format(ip)
680 self.logger.debug('url is this: %s', url)
681 r = requests.get(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
682 data = r.json()['interfaces']['interface']
685 if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel':
686 print(json.dumps(inf, indent=4, sort_keys=True))
688 return [inf['name'] for inf in data if 'name' in inf and 'type' in inf and inf['type'] == 'v3po:vxlan-tunnel']
690 # delete all VxLAN interfaces of each hosts
691 def delete_vxlan_interfaces(self, host_dic):
692 for host, ip in host_dic.items():
694 self.logger.info('{0}: Getting VxLAN interfaces'.format(host))
695 inf_list = self.get_vxlan_interfaces(ip)
699 self.logger.info("{0}: Deleting VxLAN crossconnect {1}".format(host, inf))
700 url = self.vpp_inf_url.format(ip) + '/interface/' + inf + '/v3po:l2'
701 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
706 self.logger.info("{0}: Deleting VxLAN interface {1}".format(host, inf))
707 url = self.vpp_inf_url.format(ip) + '/interface/' + inf
708 requests.delete(url, headers=self.vpp_api_headers, auth=self.vpp_api_userpass)
710 if len(self.get_vxlan_interfaces(ip)) > 0:
711 self.logger.error("Error deleting VxLAN from {0}, try to restart the VM, IP is {1}.".format(host, ip))
715 self.logger.info("{0}: no VxLAN interface found, nothing to delete".format(host))
719 def save_object(obj, filepathname):
720 with open(filepathname, 'wb') as fout:
721 pickle.dump(obj, fout)
724 def load_object(filepathname):
725 with open(filepathname, 'rb') as fin:
726 return pickle.load(fin)
729 def increase_ip_address_or_vni_in_template(vnf_template_file, vnf_parameter_name_list):
730 with open(vnf_template_file) as json_input:
731 json_data = json.load(json_input)
732 param_list = json_data['VNF-API:input']['VNF-API:vnf-topology-information']['VNF-API:vnf-parameters']
733 for param in param_list:
734 if param['vnf-parameter-name'] in vnf_parameter_name_list:
735 ipaddr_or_vni = param['vnf-parameter-value'].split('.')
736 number = int(ipaddr_or_vni[-1])
741 ipaddr_or_vni[-1] = str(number)
742 param['vnf-parameter-value'] = '.'.join(ipaddr_or_vni)
744 assert json_data is not None
745 with open(vnf_template_file, 'w') as json_output:
746 json.dump(json_data, json_output, indent=4, sort_keys=True)
748 def save_preload_data(self, preload_data):
749 self.save_object(preload_data, self.preload_dict_file)
751 def load_preload_data(self):
752 return self.load_object(self.preload_dict_file)
754 def save_vgmux_vnf_name(self, vgmux_vnf_name):
755 self.save_object(vgmux_vnf_name, self.vgmux_vnf_name_file)
757 def load_vgmux_vnf_name(self):
758 return self.load_object(self.vgmux_vnf_name_file)