Merge "Rework rescue/unrescue methods"

This commit is contained in:
Jenkins
2016-07-23 02:35:45 +00:00
committed by Gerrit Code Review
2 changed files with 150 additions and 103 deletions

View File

@@ -767,3 +767,78 @@ class LXDDriverTest(test.NoDBTestCase):
self.client.containers.get.assert_called_once_with(instance.name)
container.unfreeze.assert_called_once_with(instance.name, wait=True)
@mock.patch('nova.virt.lxd.driver.container_utils.get_container_rescue')
def test_rescue(self, get_container_rescue):
profile = mock.Mock()
profile.devices = {
'root': {
'type': 'disk',
'path': '/',
'size': '1GB'
}
}
container = mock.Mock()
self.client.profiles.get.return_value = profile
self.client.containers.get.return_value = container
get_container_rescue.return_value = '/path'
ctx = context.get_admin_context()
instance = fake_instance.fake_instance_obj(ctx, name='test')
profile.name = instance.name
network_info = [mock.Mock()]
image_meta = mock.Mock()
rescue_password = mock.Mock()
rescue = '%s-rescue' % instance.name
lxd_driver = driver.LXDDriver(None)
lxd_driver.init_host(None)
lxd_driver.rescue(
ctx, instance, network_info, image_meta, rescue_password)
lxd_driver.client.containers.get.assert_called_once_with(instance.name)
container.stop.assert_called_once_with(wait=True)
container.rename.assert_called_once_with(rescue, wait=True)
lxd_driver.client.profiles.get.assert_called_once_with(instance.name)
lxd_driver.client.container_create.assert_called_once_with(
{'name': instance.name, 'profiles': [profile.name],
'source': {'type': 'image', 'alias': None},
}, instance, wait=True)
self.assertTrue('rescue' in profile.devices)
def test_unrescue(self):
container = mock.Mock()
self.client.containers.get.return_value = container
profile = mock.Mock()
profile.devices = {
'root': {
'type': 'disk',
'path': '/',
'size': '1GB'
},
'rescue': {
'source': '/path',
'path': '/mnt',
'type': 'disk'
}
}
self.client.profiles.get.return_value = profile
ctx = context.get_admin_context()
instance = fake_instance.fake_instance_obj(ctx, name='test')
network_info = [mock.Mock()]
rescue = '%s-rescue' % instance.name
lxd_driver = driver.LXDDriver(None)
lxd_driver.init_host(None)
lxd_driver.unrescue(instance, network_info)
lxd_driver.client.profiles.get.assert_called_once_with(instance.name)
profile.save.assert_called_once_with()
lxd_driver.client.containers.get.assert_called_once_with(rescue)
container.stop.assert_called_once_with(wait=True)
container.rename.assert_called_once_with(instance.name, wait=True)
container.start.assert_called_once_with(wait=True)
self.assertTrue('rescue' not in profile.devices)

View File

@@ -489,6 +489,81 @@ class LXDDriver(driver.ComputeDriver):
"""
self.unpause(instance)
# XXX: rockstar (20 Jul 2016) - nova-lxd does not support
# `resume_state_on_host_boot`
def rescue(self, context, instance, network_info, image_meta,
rescue_password):
"""Rescue a LXD container
Rescuing a instance requires a number of steps. First,
the failed container is stopped. Next, '-rescue', is
appended to the failed container's name, this is done
so the container can be unrescued. The container's
profile is updated with the rootfs of the
failed container. Finally, a new container
is created and started.
See 'nova.virt.driver.ComputeDriver.rescue` for more
information.
"""
rescue = '%s-rescue' % instance.name
container = self.client.containers.get(instance.name)
container.stop(wait=True)
container.rename(rescue, wait=True)
profile = self.client.profiles.get(instance.name)
rescue_dir = {
'rescue': {
'source': container_utils.get_container_rescue(instance.name),
'path': '/mnt',
'type': 'disk'
}
}
profile.devices.update(rescue_dir)
profile.save()
container_config = {
'name': instance.name,
'profiles': [profile.name],
'source': {
'type': 'image',
'alias': instance.image_ref,
}
}
container = self.client.container_create(
container_config, instance, wait=True)
container.start(wait=True)
# XXX: rockstar (20 Jul 2016) - nova-lxd does not support
# `set_bootable`
def unrescue(self, instance, network_info):
"""Unrescue an instance
Unrescue a container that has previously been rescued.
First, the rescue rootfs is removed from the profile.
Next, the container is restored to the original container
name and then started again.
See 'nova.virt.drvier.ComputeDriver.unrescue` for more
information.
"""
rescue = '%s-rescue' % instance.name
container = self.client.containers.get(rescue)
container.stop(wait=True)
profile = self.client.profiles.get(instance.name)
del profile.devices['rescue']
profile.save()
container.rename(instance.name, wait=True)
container.start(wait=True)
# XXX: rockstar (5 July 2016) - The methods and code below this line
# have not been through the cleanup process. We know the cleanup process
# is complete when there is no more code below this comment, and the
@@ -553,62 +628,9 @@ class LXDDriver(driver.ComputeDriver):
# XXX: rockstar (20 Jul 2016) - nova-lxd does not support
# `resume_state_on_host_boot`
def rescue(self, context, instance, network_info, image_meta,
rescue_password):
LOG.debug('rescue called for instance', instance=instance)
try:
# Step 1 - Stop the old container
self.session.container_stop(instance.name, instance)
# Step 2 - Rename the broken container to be rescued
self.session.container_move(instance.name,
{'name': '%s-backup' % instance.name},
instance)
# Step 3 - Re use the old instance object and confiugre
# the disk mount point and create a new container.
container_config = self.create_container(instance)
rescue_dir = container_utils.get_container_rescue(
instance.name + '-backup')
config = self.configure_disk_path(
rescue_dir, 'mnt', 'rescue', instance)
container_config['devices'].update(config)
self.session.container_init(container_config, instance)
# Step 4 - Start the rescue instance
self.session.container_start(instance.name, instance)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Container rescue failed for '
'%(instance)s: %(ex)s'),
{'instance': instance.name,
'ex': ex}, instance=instance)
# XXX: rockstar (20 Jul 2016) - nova-lxd does not support
# `set_bootable`
def unrescue(self, instance, network_info):
LOG.debug('unrescue called for instance', instance=instance)
try:
# Step 1 - Destory the rescue instance.
self.session.container_destroy(instance.name,
instance)
# Step 2 - Rename the backup container that
# the user was working on.
self.session.container_move(instance.name + '-backup',
{'name': instance.name},
instance)
# Step 3 - Start the old container
self.session.container_start(instance.name, instance)
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.exception(_LE('Container unrescue failed for '
'%(instance)s: %(ex)s'),
{'instance': instance.name,
'ex': ex}, instance=instance)
def power_off(self, instance, timeout=0, retry_interval=0):
LOG.debug('power_off called for instance', instance=instance)
try:
@@ -1245,32 +1267,6 @@ class LXDDriver(driver.ComputeDriver):
if os.path.exists(container_manifest):
os.unlink(container_manifest)
def create_container(self, instance):
"""Create a LXD container dictionary so that we can
use it to initialize a container
:param instance: nova instance object
"""
LOG.debug('create_container called for instance', instance=instance)
instance_name = instance.name
try:
# Fetch the container configuration from the current nova
# instance object
return {
'name': instance_name,
'profiles': [str(instance.name)],
'source': self.get_container_source(instance),
'devices': {}
}
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error('Failed to get container configuration'
' %(instance)s: %(ex)s',
{'instance': instance_name, 'ex': ex},
instance=instance)
def create_profile(self, instance, network_info, block_device_info):
"""Create a LXD container profile configuration
@@ -1538,30 +1534,6 @@ class LXDDriver(driver.ComputeDriver):
return network_config
def get_container_source(self, instance):
"""Set the LXD container image for the instance.
:param instance: nova instance object
:return: the container source
"""
LOG.debug('get_container_source called for instance',
instance=instance)
try:
container_source = {'type': 'image',
'alias': str(instance.image_ref)}
if container_source is None:
msg = _('Failed to determine container source for %s') \
% instance.name
raise exception.NovaException(msg)
return container_source
except Exception as ex:
with excutils.save_and_reraise_exception():
LOG.error(
_LE('Failed to configure container source '
'%(instance)s: %(ex)s'),
{'instance': instance.name, 'ex': ex},
instance=instance)
def get_container_migrate(self, container_migrate, host, instance):
"""Create the image source for a migrating container