X-Git-Url: https://osm.etsi.org/gitweb/?a=blobdiff_plain;f=rwcal%2Fplugins%2Fvala%2Frwcal_openstack%2Frwcal_openstack.py;h=a3703306fdb67db234923dfa3193ee7d30409e8e;hb=af804410b4063fe28658e38d23e3108b1d88a799;hp=e5dcb67f5c366f7f4571829214d6f784b4eaaefd;hpb=97b74b6a87ca8827749782653e89865d9d1108a4;p=osm%2FSO.git diff --git a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py index e5dcb67f..a3703306 100644 --- a/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py +++ b/rwcal/plugins/vala/rwcal_openstack/rwcal_openstack.py @@ -20,6 +20,8 @@ import logging import os import subprocess import uuid +import tempfile +import yaml import rift.rwcal.openstack as openstack_drv import rw_status @@ -27,6 +29,10 @@ import rift.cal.rwcal_status as rwcal_status import rwlogger import neutronclient.common.exceptions as NeutronException import keystoneclient.exceptions as KeystoneExceptions +import tornado +import gi + +gi.require_version('RwSdn', '1.0') from gi.repository import ( GObject, @@ -75,7 +81,6 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): self._rwlog_handler = None RwcalOpenstackPlugin.instance_num += 1 - @contextlib.contextmanager def _use_driver(self, account): if self._rwlog_handler is None: @@ -83,12 +88,14 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): with rwlogger.rwlog_root_handler(self._rwlog_handler): try: - drv = self._driver_class(username = account.openstack.key, - password = account.openstack.secret, - auth_url = account.openstack.auth_url, - tenant_name = account.openstack.tenant, - mgmt_network = account.openstack.mgmt_network, - cert_validate = account.openstack.cert_validate ) + drv = self._driver_class(username = account.openstack.key, + password = account.openstack.secret, + auth_url = account.openstack.auth_url, + tenant_name = account.openstack.tenant, + mgmt_network = account.openstack.mgmt_network, + cert_validate = account.openstack.cert_validate, + user_domain_name = account.openstack.user_domain, + project_domain_name = account.openstack.project_domain) except (KeystoneExceptions.Unauthorized, KeystoneExceptions.AuthorizationFailure, NeutronException.NotFound) as e: raise @@ -362,6 +369,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): image = drv.glance_image_get(image_id) return RwcalOpenstackPlugin._fill_image_info(image) + # This is being deprecated. Please do not use for new SW development @rwstatus(ret_on_failure=[""]) def do_create_vm(self, account, vminfo): """Create a new virtual machine. @@ -1180,6 +1188,8 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): if ('fixed_ips' in port_info) and (len(port_info['fixed_ips']) >= 1): if 'ip_address' in port_info['fixed_ips'][0]: c_point.ip_address = port_info['fixed_ips'][0]['ip_address'] + if 'mac_address' in port_info : + c_point.mac_addr = port_info['mac_address'] if port_info['status'] == 'ACTIVE': c_point.state = 'active' else: @@ -1228,7 +1238,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): return link @staticmethod - def _fill_vdu_info(vm_info, flavor_info, mgmt_network, port_list, server_group, volume_list = None): + def _fill_vdu_info(drv, vm_info, flavor_info, mgmt_network, port_list, server_group, volume_list = None): """Create a GI object for VDUInfoParams Converts VM information dictionary object returned by openstack @@ -1259,6 +1269,14 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): for key, value in vm_info['metadata'].items(): if key == 'node_id': vdu.node_id = value + else: + custommetadata = vdu.supplemental_boot_data.custom_meta_data.add() + custommetadata.name = key + custommetadata.value = str(value) + + # Look for config_drive + if ('config_drive' in vm_info): + vdu.supplemental_boot_data.boot_data_drive = vm_info['config_drive'] if ('image' in vm_info) and ('id' in vm_info['image']): vdu.image_id = vm_info['image']['id'] if ('flavor' in vm_info) and ('id' in vm_info['flavor']): @@ -1297,6 +1315,16 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): continue volr.name = vol_name volr.volume_id = os_volume['volumeId'] + try: + vol_details = drv.cinder_volume_get(volr.volume_id) + except: + continue + if vol_details is None: + continue + for key, value in vol_details.metadata.items(): + volmd = volr.custom_meta_data.add() + volmd.name = key + volmd.value = value return vdu @@ -1472,6 +1500,12 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): else: raise NotImplementedError("Port Type: %s not supported" %(c_point.type_yang)) + # By default port gets created with post_security enaled as True + if c_point.port_security_enabled is not None and c_point.port_security_enabled == False: + kwargs['port_security_enabled'] = False + else: + kwargs['port_security_enabled'] = True + with self._use_driver(account) as drv: if c_point.has_field('security_group'): group = drv.neutron_security_group_by_name(c_point.security_group) @@ -1909,12 +1943,33 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): kwargs['network_list'] = network_list metadata = {} + files = {} + config_drive = False # Add all metadata related fields if vduinfo.has_field('node_id'): metadata['node_id'] = vduinfo.node_id if pci_assignement is not None: metadata['pci_assignement'] = pci_assignement + if vduinfo.has_field('supplemental_boot_data'): + if vduinfo.supplemental_boot_data.has_field('custom_meta_data'): + for custom_meta_item in vduinfo.supplemental_boot_data.custom_meta_data: + if custom_meta_item.data_type == "STRING": + metadata[custom_meta_item.name] = custom_meta_item.value + elif custom_meta_item.data_type == "JSON": + metadata[custom_meta_item.name] = tornado.escape.json_decode(custom_meta_item.value) + else: + raise OpenstackCALOperationFailure("Create-vdu operation failed. Unsupported data-type {} for custom-meta-data name {} ".format(custom_meta_item.data_type, custom_meta_item.name)) + if vduinfo.supplemental_boot_data.has_field('config_file'): + for custom_config_file in vduinfo.supplemental_boot_data.config_file: + files[custom_config_file.dest] = custom_config_file.source + + if vduinfo.supplemental_boot_data.has_field('boot_data_drive'): + if vduinfo.supplemental_boot_data.boot_data_drive is True: + config_drive = True + kwargs['metadata'] = metadata + kwargs['files'] = files + kwargs['config_drive'] = config_drive if vduinfo.has_field('availability_zone') and vduinfo.availability_zone.has_field('name'): kwargs['availability_zone'] = vduinfo.availability_zone @@ -1927,13 +1982,14 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): kwargs['scheduler_hints'] = None kwargs['block_device_mapping_v2'] = None + vol_metadata = False if vduinfo.has_field('volumes') : kwargs['block_device_mapping_v2'] = [] with self._use_driver(account) as drv: # Only support image->volume for volume in vduinfo.volumes: block_map = dict() - block_map['boot_index'] = volume.boot_params.boot_priority + block_map['boot_index'] = volume.boot_priority if "image" in volume: # Support image->volume # Match retrived image info with volume based image name and checksum @@ -1953,9 +2009,9 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): block_map['destination_type'] = "volume" block_map['volume_size'] = volume.size block_map['delete_on_termination'] = True - if volume.guest_params.has_field('device_type') and volume.guest_params.device_type == 'cdrom': + if volume.has_field('device_type') and volume.device_type == 'cdrom': block_map['device_type'] = 'cdrom' - if volume.guest_params.has_field('device_bus') and volume.guest_params.device_bus == 'ide': + if volume.has_field('device_bus') and volume.device_bus == 'ide': block_map['disk_bus'] = 'ide' kwargs['block_device_mapping_v2'].append(block_map) @@ -1963,7 +2019,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): with self._use_driver(account) as drv: vm_id = drv.nova_server_create(**kwargs) if floating_ip: - self.prepare_vdu_on_boot(account, vm_id, floating_ip) + self.prepare_vdu_on_boot(account, vm_id, floating_ip, vduinfo.volumes) return vm_id @@ -2010,7 +2066,10 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): The vdu_id """ ### First create required number of ports aka connection points + # Add the mgmt_ntwk by default. + mgmt_network_id = None with self._use_driver(account) as drv: + mgmt_network_id = drv._mgmt_network_id ### If floating_ip is required and we don't have one, better fail before any further allocation if vdu_init.has_field('allocate_public_address') and vdu_init.allocate_public_address: if account.openstack.has_field('floating_ip_pool'): @@ -2024,7 +2083,12 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): port_list = [] network_list = [] imageinfo_list = [] + is_explicit_mgmt_defined = False for c_point in vdu_init.connection_points: + # if the user has specified explicit mgmt_network connection point + # then remove the mgmt_network from the VM list + if c_point.virtual_link_id == mgmt_network_id: + is_explicit_mgmt_defined = True if c_point.virtual_link_id in network_list: assert False, "Only one port per network supported. Refer: http://specs.openstack.org/openstack/nova-specs/specs/juno/implemented/nfv-multiple-if-1-net.html" else: @@ -2086,7 +2150,8 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): with self._use_driver(account) as drv: ### Now Create VM vm_network_list = [] - vm_network_list.append(drv._mgmt_network_id) + if not is_explicit_mgmt_defined: + vm_network_list.append(drv._mgmt_network_id) if vdu_init.has_field('volumes'): # Only combination supported: Image->Volume @@ -2095,16 +2160,12 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): err_str = ("VDU Volume source not supported yet") self.log.error(err_str) raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) - if "guest_params" not in volume: - err_str = ("VDU Volume destination parameters '%s' not defined") - self.log.error(err_str) - raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) - if not volume.guest_params.has_field('device_type'): - err_str = ("VDU Volume destination type '%s' not defined") + if not volume.has_field('device_type'): + err_str = ("VDU Volume destination type not defined") self.log.error(err_str) raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) - if volume.guest_params.device_type not in ['disk', 'cdrom'] : - err_str = ("VDU Volume destination type '%s' not supported" % volume.guest_params.device_type) + if volume.device_type not in ['disk', 'cdrom'] : + err_str = ("VDU Volume destination type '%s' not supported" % volume.device_type) self.log.error(err_str) raise OpenstackCALOperationFailure("Create-vdu operation failed. Error- %s" % err_str) @@ -2123,7 +2184,6 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): vm.user_tags.pci_assignement = pci_assignement vm_id = self._create_vm(account, vdu_init, pci_assignement=pci_assignement, server_group=server_group, port_list=port_list, network_list=vm_network_list, imageinfo_list = imageinfo_list) - self.prepare_vdu_on_boot(account, vm_id, floating_ip) return vm_id def prepare_vpci_metadata(self, drv, vdu_init): @@ -2169,7 +2229,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): - def prepare_vdu_on_boot(self, account, server_id, floating_ip): + def prepare_vdu_on_boot(self, account, server_id, floating_ip, volumes=None): cmd = PREPARE_VM_CMD.format(auth_url = account.openstack.auth_url, username = account.openstack.key, password = account.openstack.secret, @@ -2180,6 +2240,24 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): if floating_ip is not None: cmd += (" --floating_ip "+ floating_ip.ip) + vol_metadata = False + if volumes is not None: + for volume in volumes: + if volume.has_field('custom_meta_data'): + vol_metadata = True + break + + if vol_metadata is True: + tmp_file = None + with tempfile.NamedTemporaryFile(mode='w', delete=False) as tmp_file: + vol_list = list() + for volume in volumes: + vol_dict = volume.as_dict() + vol_list.append(vol_dict) + + yaml.dump(vol_list, tmp_file) + cmd += (" --vol_metadata {}").format(tmp_file.name) + exec_path = 'python3 ' + os.path.dirname(openstack_drv.__file__) exec_cmd = exec_path+'/'+cmd self.log.info("Running command: %s" %(exec_cmd)) @@ -2261,9 +2339,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): Object of type RwcalYang.VDUInfoParams """ with self._use_driver(account) as drv: - - ### Get list of ports excluding the one for management network - port_list = [p for p in drv.neutron_port_list(**{'device_id': vdu_id}) if p['network_id'] != drv.get_mgmt_network_id()] + port_list = drv.neutron_port_list(**{'device_id': vdu_id}) vm = drv.nova_server_get(vdu_id) @@ -2277,7 +2353,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): openstack_group_list = drv.nova_server_group_list() server_group = [ i['name'] for i in openstack_group_list if vm['id'] in i['members']] openstack_srv_volume_list = drv.nova_volume_list(vm['id']) - vdu_info = RwcalOpenstackPlugin._fill_vdu_info(vm, + vdu_info = RwcalOpenstackPlugin._fill_vdu_info(drv, vm, flavor_info, account.openstack.mgmt_network, port_list, @@ -2309,8 +2385,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): with self._use_driver(account) as drv: vms = drv.nova_server_list() for vm in vms: - ### Get list of ports excluding one for management network - port_list = [p for p in drv.neutron_port_list(**{'device_id': vm['id']}) if p['network_id'] != drv.get_mgmt_network_id()] + port_list = drv.neutron_port_list(**{'device_id': vm['id']}) flavor_info = None @@ -2327,7 +2402,7 @@ class RwcalOpenstackPlugin(GObject.Object, RwCal.Cloud): server_group = [ i['name'] for i in openstack_group_list if vm['id'] in i['members']] openstack_srv_volume_list = drv.nova_volume_list(vm['id']) - vdu = RwcalOpenstackPlugin._fill_vdu_info(vm, + vdu = RwcalOpenstackPlugin._fill_vdu_info(drv, vm, flavor_info, account.openstack.mgmt_network, port_list,