diff --git a/nova/virt/lxd/common.py b/nova/virt/lxd/common.py index 5b70fa0..c67e452 100644 --- a/nova/virt/lxd/common.py +++ b/nova/virt/lxd/common.py @@ -17,6 +17,8 @@ from nova import conf +from oslo_log import log as logging +LOG = logging.getLogger(__name__) _InstanceAttributes = collections.namedtuple('InstanceAttributes', [ 'instance_dir', 'console_path', 'storage_path', 'container_path']) @@ -31,3 +33,55 @@ def InstanceAttributes(instance): conf.CONF.lxd.root_dir, 'containers', instance.name) return _InstanceAttributes( instance_dir, console_path, storage_path, container_path) + +""" +Pureport - The following functions facilitate finding LXD containers by name +in the event that the instance name template changes in OpenStack. +""" + +def get_instance_criteria(instance): + """Returns the list of possible instance name criteria used to find a container""" + + return [ + instance.name, + "instance-%08x" % instance.id + ] + +def get_container(client, instance, suffix = ''): + """Returns the container associated to the specified instance. + + Queries based on multiple possible sets of criteria to support scenarios + where OpenStack instance name template changes. + """ + + instance_criteria = map(lambda x : x + suffix, get_instance_criteria(instance)) + + LOG.info("Instance = {}".format(instance)) + for criteria in instance_criteria: + try: + instance = client.containers.get(criteria) + if instance != None: + LOG.info("Instance {} found by criteria {}", instance, criteria) + return instance + except: + LOG.warn("Unable to find instance {}".format(criteria), instance=instance) + + return None + +def get_rescue_container(client, instance): + """Returns the rescue container for the specified instance if it exists.""" + return get_container(client, instance, '-rescue') + +def get_profile(client, instance): + """Returns the profile for the specified instance """ + instance_criteria = get_instance_criteria(instance) + for criteria in instance_criteria: + try: + instance = client.profiles.get(criteria) + if instance != None: + LOG.info("Instance {} found by criteria {}", instance, criteria) + return instance + except: + LOG.warn("Unable to find instance {}".format(criteria), instance=instance) + + return None diff --git a/nova/virt/lxd/driver.py b/nova/virt/lxd/driver.py index 5ca7c59..4971cf5 100644 --- a/nova/virt/lxd/driver.py +++ b/nova/virt/lxd/driver.py @@ -462,7 +462,7 @@ def cleanup_host(self, host): def get_info(self, instance): """Return an InstanceInfo object for the instance.""" try: - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) except lxd_exceptions.NotFound: raise exception.InstanceNotFound(instance_id=instance.uuid) @@ -488,7 +488,7 @@ def spawn(self, context, instance, image_meta, injected_files, information. """ try: - self.client.containers.get(instance.name) + common.get_container(self.client, instance) raise exception.InstanceExists(name=instance.name) except lxd_exceptions.LXDAPIException as e: if e.response.status_code != 404: @@ -568,7 +568,7 @@ def spawn(self, context, instance, image_meta, injected_files, injected_files, admin_password, network_info) - profile = self.client.profiles.get(instance.name) + profile = common.get_profile(self.client, instance) config_drive = { 'configdrive': { 'path': '/config-drive', @@ -606,13 +606,12 @@ def destroy(self, context, instance, network_info, block_device_info=None, information. """ try: - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) if container.status != 'Stopped': container.stop(wait=True) container.delete(wait=True) if (instance.vm_state == vm_states.RESCUED): - rescued_container = self.client.containers.get( - '%s-rescue' % instance.name) + rescued_container = get_rescue_container(self.client, instance) if rescued_container.status != 'Stopped': rescued_container.stop(wait=True) rescued_container.delete(wait=True) @@ -654,7 +653,7 @@ def cleanup(self, context, instance, network_info, block_device_info=None, shutil.rmtree(container_dir) try: - self.client.profiles.get(instance.name).delete() + common.get_profile(self.client, instance).delete() except lxd_exceptions.LXDAPIException as e: if e.response.status_code == 404: LOG.warning('Failed to delete instance. ' @@ -674,7 +673,7 @@ def reboot(self, context, instance, network_info, reboot_type, See `nova.virt.driver.ComputeDriver.cleanup` for more information. """ - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) container.restart(force=True, wait=True) def get_console_output(self, context, instance): @@ -715,7 +714,7 @@ def attach_volume(self, context, connection_info, instance, mountpoint, See `nova.virt.driver.ComputeDriver.attach_volume' for more information/ """ - profile = self.client.profiles.get(instance.name) + profile = common.get_profile(self.client, instance) protocol = connection_info['driver_volume_type'] storage_driver = brick_get_connector(protocol) device_info = storage_driver.connect_volume( @@ -748,7 +747,7 @@ def detach_volume(self, connection_info, instance, mountpoint, See `nova.virt.driver.Computedriver.detach_volume` for more information. """ - profile = self.client.profiles.get(instance.name) + profile = common.get_profile(self.client, instance) vol_id = connection_info['data']['volume_id'] if vol_id in profile.devices: del profile.devices[vol_id] @@ -762,7 +761,7 @@ def attach_interface(self, context, instance, image_meta, vif): self.vif_driver.plug(instance, vif) self.firewall_driver.setup_basic_filtering(instance, vif) - profile = self.client.profiles.get(instance.name) + profile = common.get_profile(self.client, instance) net_device = lxd_vif.get_vif_devname(vif) config_update = { @@ -778,7 +777,7 @@ def attach_interface(self, context, instance, image_meta, vif): profile.save(wait=True) def detach_interface(self, context, instance, vif): - profile = self.client.profiles.get(instance.name) + profile = common.get_profile(self.client, instance) devname = lxd_vif.get_vif_devname(vif) # NOTE(jamespage): Attempt to remove device using @@ -810,7 +809,7 @@ def migrate_disk_and_power_off( flavor.to_profile( self.client, instance, network_info, block_device_info, update=True) - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) container.stop(wait=True) return '' @@ -823,7 +822,7 @@ def snapshot(self, context, instance, image_id, update_task_state): update_task_state(task_state=task_states.IMAGE_PENDING_UPLOAD) - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) if container.status != 'Stopped': container.stop(wait=True) image = container.publish(wait=True) @@ -846,7 +845,7 @@ def pause(self, instance): See `nova.virt.driver.ComputeDriver.pause` for more information. """ - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) container.freeze(wait=True) def unpause(self, instance): @@ -855,7 +854,7 @@ def unpause(self, instance): See `nova.virt.driver.ComputeDriver.unpause` for more information. """ - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) container.unfreeze(wait=True) def suspend(self, context, instance): @@ -910,12 +909,12 @@ def rescue(self, context, instance, network_info, image_meta, """ rescue = '%s-rescue' % instance.name - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) container_rootfs = os.path.join( nova.conf.CONF.lxd.root_dir, 'containers', instance.name, 'rootfs') container.rename(rescue, wait=True) - profile = self.client.profiles.get(instance.name) + profile = common.get_profile(self.client, instance) rescue_dir = { 'rescue': { @@ -950,18 +949,16 @@ def unrescue(self, instance, network_info): See 'nova.virt.drvier.ComputeDriver.unrescue` for more information. """ - rescue = '%s-rescue' % instance.name - - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) if container.status != 'Stopped': container.stop(wait=True) container.delete(wait=True) - profile = self.client.profiles.get(instance.name) + profile = common.get_profile(self.client, instance) del profile.devices['rescue'] profile.save() - container = self.client.containers.get(rescue) + container = get_rescue_container(self.client, instance) container.rename(instance.name, wait=True) container.start(wait=True) @@ -971,7 +968,7 @@ def power_off(self, instance, timeout=0, retry_interval=0): See 'nova.virt.drvier.ComputeDriver.power_off` for more information. """ - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) if container.status != 'Stopped': container.stop(wait=True) @@ -982,7 +979,7 @@ def power_on(self, context, instance, network_info, See 'nova.virt.drvier.ComputeDriver.power_on` for more information. """ - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) if container.status != 'Running': container.start(wait=True) @@ -1132,17 +1129,17 @@ def finish_migration(self, context, migration, instance, disk_info, # Step 3 - Start the network and container self.plug_vifs(instance, network_info) - self.client.containers.get(instance.name).start(wait=True) + common.get_container(self.client, instance).start(wait=True) def confirm_migration(self, context, migration, instance, network_info): self.unplug_vifs(instance, network_info) - self.client.containers.get(instance.name).delete(wait=True) - self.client.profiles.get(instance.name).delete() + common.get_container(self.client, instance).delete(wait=True) + common.get_profile(self.client, instance).delete() def finish_revert_migration(self, context, instance, network_info, block_device_info=None, power_on=True): - self.client.containers.get(instance.name).start(wait=True) + common.get_container(self.client, instance).start(wait=True) def pre_live_migration(self, context, instance, block_device_info, network_info, disk_info, migrate_data=None): @@ -1166,17 +1163,17 @@ def live_migration(self, context, instance, dest, def post_live_migration(self, context, instance, block_device_info, migrate_data=None): - self.client.containers.get(instance.name).delete(wait=True) + common.get_container(self.client, instance).delete(wait=True) def post_live_migration_at_source(self, context, instance, network_info): - self.client.profiles.get(instance.name).delete() + common.get_profile(self.client, instance).delete() self.cleanup(context, instance, network_info) def check_can_live_migrate_destination( self, context, instance, src_compute_info, dst_compute_info, block_migration=False, disk_over_commit=False): try: - self.client.containers.get(instance.name) + common.get_container(self.client, instance) raise exception.InstanceExists(name=instance.name) except lxd_exceptions.LXDAPIException as e: if e.response.status_code != 404: @@ -1209,7 +1206,7 @@ def _add_configdrive(self, context, instance, raise exception.ConfigDriveUnsupportedFormat( format=CONF.config_drive_format) - container = self.client.containers.get(instance.name) + container = common.get_container(self.client, instance) storage_id = 0 """ Determine UID shift used for container uid mapping @@ -1326,7 +1323,7 @@ def _migrate(self, source_host, instance): """Migrate an instance from source.""" source_client = pylxd.Client( endpoint='https://{}:8443'.format(source_host), verify=False) - container = source_client.containers.get(instance.name) + container = common.get_container(source_client, instance) data = container.generate_migration_data() self.client.containers.create(data, wait=True) diff --git a/nova/virt/lxd/flavor.py b/nova/virt/lxd/flavor.py index 05636c0..7f5c1fd 100644 --- a/nova/virt/lxd/flavor.py +++ b/nova/virt/lxd/flavor.py @@ -227,7 +227,7 @@ def to_profile(client, instance, network_info, block_info, update=False): devices.update(new) if update is True: - profile = client.profiles.get(name) + profile = get_profile(client, instance) profile.devices = devices profile.config = config profile.save() diff --git a/nova/virt/lxd/storage.py b/nova/virt/lxd/storage.py index 17f6b5b..47dd272 100644 --- a/nova/virt/lxd/storage.py +++ b/nova/virt/lxd/storage.py @@ -32,7 +32,7 @@ def attach_ephemeral(client, block_device_info, lxd_config, instance): if ephemeral_storage: storage_driver = lxd_config['environment']['storage'] - container = client.containers.get(instance.name) + container = common.get_container(client, instance) container_id_map = container.config[ 'volatile.last_state.idmap'].split(',') storage_id = container_id_map[2].split(':')[1] @@ -64,7 +64,7 @@ def attach_ephemeral(client, block_device_info, lxd_config, instance): # before the container starts. storage_dir = os.path.join( instance_attrs.container_path, ephemeral['virtual_name']) - profile = client.profiles.get(instance.name) + profile = common.get_profile(client, instance) storage_name = ephemeral['virtual_name'] profile.devices[storage_name]['source'] = storage_dir profile.save()