Merge "libvirt: start lxc from block device"
This commit is contained in:
commit
d946915e06
|
@ -2175,7 +2175,6 @@ class LibvirtConnTestCase(test.TestCase,
|
|||
"""
|
||||
self.flags(virt_type='lxc', group='libvirt')
|
||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
mock_domain = mock.MagicMock()
|
||||
mock_instance = mock.MagicMock()
|
||||
mock_get_inst_path.return_value = '/tmp/'
|
||||
mock_image_backend = mock.MagicMock()
|
||||
|
@ -2185,14 +2184,22 @@ class LibvirtConnTestCase(test.TestCase,
|
|||
conn.image_backend.image.return_value = mock_image
|
||||
mock_setup_container.return_value = '/dev/nbd0'
|
||||
mock_get_info.side_effect = exception.InstanceNotFound(
|
||||
instance_id='foo')
|
||||
instance_id='foo')
|
||||
conn._conn.defineXML = mock.Mock()
|
||||
conn._conn.defineXML.side_effect = ValueError('somethingbad')
|
||||
with contextlib.nested(
|
||||
mock.patch.object(conn, '_is_booted_from_volume',
|
||||
return_value=False),
|
||||
mock.patch.object(conn, 'plug_vifs'),
|
||||
mock.patch.object(conn, 'firewall_driver'),
|
||||
mock.patch.object(conn, 'cleanup')):
|
||||
self.assertRaises(ValueError,
|
||||
conn._create_domain_and_network,
|
||||
self.context,
|
||||
'xml',
|
||||
mock_instance, None)
|
||||
|
||||
mock_domain.createWithFlags.side_effect = ValueError('somethingbad')
|
||||
|
||||
self.assertRaises(ValueError, conn._create_domain, domain=mock_domain,
|
||||
instance=mock_instance)
|
||||
|
||||
mock_teardown.assert_called_with(container_dir='/tmp/rootfs')
|
||||
mock_teardown.assert_called_with(container_dir='/tmp/rootfs')
|
||||
|
||||
def test_video_driver_flavor_limit_not_set(self):
|
||||
self.flags(virt_type='kvm', group='libvirt')
|
||||
|
@ -6000,6 +6007,95 @@ class LibvirtConnTestCase(test.TestCase,
|
|||
self.assertTrue(self.cache_called_for_disk)
|
||||
db.instance_destroy(self.context, instance['uuid'])
|
||||
|
||||
def test_start_lxc_from_volume(self):
|
||||
self.flags(virt_type="lxc",
|
||||
group='libvirt')
|
||||
|
||||
def check_setup_container(path, container_dir=None, use_cow=False):
|
||||
self.assertEqual(path, '/dev/path/to/dev')
|
||||
self.assertTrue(use_cow)
|
||||
return '/dev/nbd1'
|
||||
|
||||
bdm = {
|
||||
'guest_format': None,
|
||||
'boot_index': 0,
|
||||
'mount_device': '/dev/sda',
|
||||
'connection_info': {
|
||||
'driver_volume_type': 'iscsi',
|
||||
'serial': 'afc1',
|
||||
'data': {
|
||||
'access_mode': 'rw',
|
||||
'target_discovered': False,
|
||||
'encrypted': False,
|
||||
'qos_specs': None,
|
||||
'target_iqn': 'iqn: volume-afc1',
|
||||
'target_portal': 'ip: 3260',
|
||||
'volume_id': 'afc1',
|
||||
'target_lun': 1,
|
||||
'auth_password': 'uj',
|
||||
'auth_username': '47',
|
||||
'auth_method': 'CHAP'
|
||||
}
|
||||
},
|
||||
'disk_bus': 'scsi',
|
||||
'device_type': 'disk',
|
||||
'delete_on_termination': False
|
||||
}
|
||||
|
||||
def _get(key, opt=None):
|
||||
return bdm.get(key, opt)
|
||||
|
||||
def getitem(key):
|
||||
return bdm[key]
|
||||
|
||||
def setitem(key, val):
|
||||
bdm[key] = val
|
||||
|
||||
bdm_mock = mock.MagicMock()
|
||||
bdm_mock.__getitem__.side_effect = getitem
|
||||
bdm_mock.__setitem__.side_effect = setitem
|
||||
bdm_mock.get = _get
|
||||
|
||||
disk_mock = mock.MagicMock()
|
||||
disk_mock.source_path = '/dev/path/to/dev'
|
||||
|
||||
block_device_info = {'block_device_mapping': [bdm_mock],
|
||||
'root_device_name': '/dev/sda'}
|
||||
|
||||
# Volume-backed instance created without image
|
||||
instance_ref = self.test_instance
|
||||
instance_ref['image_ref'] = ''
|
||||
instance_ref['root_device_name'] = '/dev/sda'
|
||||
instance_ref['ephemeral_gb'] = 0
|
||||
instance_ref['uuid'] = uuidutils.generate_uuid()
|
||||
instance_ref['system_metadata']['image_disk_format'] = 'qcow2'
|
||||
instance = db.instance_create(self.context, instance_ref)
|
||||
self.addCleanup(db.instance_destroy, self.context, instance['uuid'])
|
||||
inst_obj = objects.Instance.get_by_uuid(self.context, instance['uuid'])
|
||||
|
||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
with contextlib.nested(
|
||||
mock.patch.object(conn, '_create_images_and_backing'),
|
||||
mock.patch.object(conn, 'plug_vifs'),
|
||||
mock.patch.object(conn.firewall_driver, 'setup_basic_filtering'),
|
||||
mock.patch.object(conn.firewall_driver, 'prepare_instance_filter'),
|
||||
mock.patch.object(conn.firewall_driver, 'apply_instance_filter'),
|
||||
mock.patch.object(conn, '_create_domain'),
|
||||
mock.patch.object(conn, '_connect_volume',
|
||||
return_value=disk_mock),
|
||||
mock.patch.object(conn, 'get_info',
|
||||
return_value={'state': power_state.RUNNING}),
|
||||
mock.patch('nova.virt.disk.api.setup_container',
|
||||
side_effect=check_setup_container),
|
||||
mock.patch('nova.virt.disk.api.teardown_container'),):
|
||||
|
||||
conn.spawn(self.context, inst_obj, None, [], None,
|
||||
network_info=[],
|
||||
block_device_info=block_device_info)
|
||||
self.assertEqual('/dev/nbd1',
|
||||
inst_obj.system_metadata.get(
|
||||
'rootfs_device_name'))
|
||||
|
||||
def test_spawn_with_pci_devices(self):
|
||||
def fake_none(*args, **kwargs):
|
||||
return None
|
||||
|
@ -8487,7 +8583,6 @@ Active: 8381604 kB
|
|||
mock_setup_container, mock_get_info, mock_clean):
|
||||
self.flags(virt_type='lxc', group='libvirt')
|
||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
mock_domain = mock.MagicMock()
|
||||
mock_instance = mock.MagicMock()
|
||||
inst_sys_meta = dict()
|
||||
mock_instance.system_metadata = inst_sys_meta
|
||||
|
@ -8500,13 +8595,20 @@ Active: 8381604 kB
|
|||
mock_setup_container.return_value = '/dev/nbd0'
|
||||
mock_get_info.return_value = {'state': power_state.RUNNING}
|
||||
|
||||
domain = conn._create_domain(domain=mock_domain,
|
||||
instance=mock_instance)
|
||||
with contextlib.nested(
|
||||
mock.patch.object(conn, '_create_images_and_backing'),
|
||||
mock.patch.object(conn, '_is_booted_from_volume',
|
||||
return_value=False),
|
||||
mock.patch.object(conn, '_create_domain'),
|
||||
mock.patch.object(conn, 'plug_vifs'),
|
||||
mock.patch.object(conn.firewall_driver, 'setup_basic_filtering'),
|
||||
mock.patch.object(conn.firewall_driver, 'prepare_instance_filter'),
|
||||
mock.patch.object(conn.firewall_driver, 'apply_instance_filter')):
|
||||
conn._create_domain_and_network(self.context, 'xml',
|
||||
mock_instance, [])
|
||||
|
||||
self.assertEqual(mock_domain, domain)
|
||||
self.assertEqual('/dev/nbd0', inst_sys_meta['rootfs_device_name'])
|
||||
mock_instance.save.assert_has_calls([mock.call()])
|
||||
mock_domain.createWithFlags.assert_has_calls([mock.call(0)])
|
||||
mock_get_inst_path.assert_has_calls([mock.call(mock_instance)])
|
||||
mock_ensure_tree.assert_has_calls([mock.call('/tmp/rootfs')])
|
||||
conn.image_backend.image.assert_has_calls([mock.call(mock_instance,
|
||||
|
@ -8542,7 +8644,6 @@ Active: 8381604 kB
|
|||
self.assertEqual(100, id_maps[1].count)
|
||||
|
||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
mock_domain = mock.MagicMock()
|
||||
mock_instance = mock.MagicMock()
|
||||
inst_sys_meta = dict()
|
||||
mock_instance.system_metadata = inst_sys_meta
|
||||
|
@ -8556,13 +8657,20 @@ Active: 8381604 kB
|
|||
mock_chown.side_effect = chown_side_effect
|
||||
mock_get_info.return_value = {'state': power_state.RUNNING}
|
||||
|
||||
domain = conn._create_domain(domain=mock_domain,
|
||||
instance=mock_instance)
|
||||
with contextlib.nested(
|
||||
mock.patch.object(conn, '_create_images_and_backing'),
|
||||
mock.patch.object(conn, '_is_booted_from_volume',
|
||||
return_value=False),
|
||||
mock.patch.object(conn, '_create_domain'),
|
||||
mock.patch.object(conn, 'plug_vifs'),
|
||||
mock.patch.object(conn.firewall_driver, 'setup_basic_filtering'),
|
||||
mock.patch.object(conn.firewall_driver, 'prepare_instance_filter'),
|
||||
mock.patch.object(conn.firewall_driver, 'apply_instance_filter')):
|
||||
conn._create_domain_and_network(self.context, 'xml',
|
||||
mock_instance, [])
|
||||
|
||||
self.assertEqual(mock_domain, domain)
|
||||
self.assertEqual('/dev/nbd0', inst_sys_meta['rootfs_device_name'])
|
||||
mock_instance.save.assert_has_calls([mock.call()])
|
||||
mock_domain.createWithFlags.assert_has_calls([mock.call(0)])
|
||||
mock_get_inst_path.assert_has_calls([mock.call(mock_instance)])
|
||||
mock_ensure_tree.assert_has_calls([mock.call('/tmp/rootfs')])
|
||||
conn.image_backend.image.assert_has_calls([mock.call(mock_instance,
|
||||
|
@ -8585,7 +8693,6 @@ Active: 8381604 kB
|
|||
mock_get_info, mock_teardown):
|
||||
self.flags(virt_type='lxc', group='libvirt')
|
||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), True)
|
||||
mock_domain = mock.MagicMock()
|
||||
mock_instance = mock.MagicMock()
|
||||
inst_sys_meta = dict()
|
||||
mock_instance.system_metadata = inst_sys_meta
|
||||
|
@ -8598,13 +8705,20 @@ Active: 8381604 kB
|
|||
mock_setup_container.return_value = '/dev/nbd0'
|
||||
mock_get_info.return_value = {'state': power_state.SHUTDOWN}
|
||||
|
||||
domain = conn._create_domain(domain=mock_domain,
|
||||
instance=mock_instance)
|
||||
with contextlib.nested(
|
||||
mock.patch.object(conn, '_create_images_and_backing'),
|
||||
mock.patch.object(conn, '_is_booted_from_volume',
|
||||
return_value=False),
|
||||
mock.patch.object(conn, '_create_domain'),
|
||||
mock.patch.object(conn, 'plug_vifs'),
|
||||
mock.patch.object(conn.firewall_driver, 'setup_basic_filtering'),
|
||||
mock.patch.object(conn.firewall_driver, 'prepare_instance_filter'),
|
||||
mock.patch.object(conn.firewall_driver, 'apply_instance_filter')):
|
||||
conn._create_domain_and_network(self.context, 'xml',
|
||||
mock_instance, [])
|
||||
|
||||
self.assertEqual(mock_domain, domain)
|
||||
self.assertEqual('/dev/nbd0', inst_sys_meta['rootfs_device_name'])
|
||||
mock_instance.save.assert_has_calls([mock.call()])
|
||||
mock_domain.createWithFlags.assert_has_calls([mock.call(0)])
|
||||
mock_get_inst_path.assert_has_calls([mock.call(mock_instance)])
|
||||
mock_ensure_tree.assert_has_calls([mock.call('/tmp/rootfs')])
|
||||
conn.image_backend.image.assert_has_calls([mock.call(mock_instance,
|
||||
|
@ -9098,15 +9212,18 @@ Active: 8381604 kB
|
|||
self.flags(virt_type='lxc', group='libvirt')
|
||||
|
||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
instance = objects.Instance(id=1, uuid='fake-uuid')
|
||||
instance = objects.Instance(id=1, uuid='fake-uuid',
|
||||
image_ref='my_fake_image')
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(conn, '_is_booted_from_volume',
|
||||
return_value=False),
|
||||
mock.patch.object(conn, 'plug_vifs'),
|
||||
mock.patch.object(conn, 'firewall_driver'),
|
||||
mock.patch.object(conn, '_create_domain',
|
||||
side_effect=exception.NovaException),
|
||||
mock.patch.object(conn, 'cleanup')) as (
|
||||
cleanup, firewall_driver, create, plug_vifs):
|
||||
cleanup, firewall_driver, create, plug_vifs, boot):
|
||||
self.assertRaises(exception.NovaException,
|
||||
conn._create_domain_and_network,
|
||||
self.context,
|
||||
|
@ -9116,43 +9233,26 @@ Active: 8381604 kB
|
|||
def test_create_without_pause(self):
|
||||
self.flags(virt_type='lxc', group='libvirt')
|
||||
|
||||
@contextlib.contextmanager
|
||||
def fake_lxc_disk_handler(*args, **kwargs):
|
||||
yield
|
||||
|
||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
instance = objects.Instance(id=1, uuid='fake-uuid')
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch.object(conn, '_lxc_disk_handler',
|
||||
side_effect=fake_lxc_disk_handler),
|
||||
mock.patch.object(conn, 'plug_vifs'),
|
||||
mock.patch.object(conn, 'firewall_driver'),
|
||||
mock.patch.object(conn, '_create_domain'),
|
||||
mock.patch.object(conn, 'cleanup')) as (
|
||||
cleanup, firewall_driver, create, plug_vifs):
|
||||
_handler, cleanup, firewall_driver, create, plug_vifs):
|
||||
domain = conn._create_domain_and_network(self.context, 'xml',
|
||||
instance, None)
|
||||
self.assertEqual(0, create.call_args_list[0][1]['launch_flags'])
|
||||
self.assertEqual(0, domain.resume.call_count)
|
||||
|
||||
def test_lxc_create_and_rootfs_saved(self):
|
||||
self.flags(virt_type='lxc', group='libvirt')
|
||||
|
||||
conn = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
|
||||
instance = db.instance_create(self.context, self.test_instance)
|
||||
inst_obj = objects.Instance.get_by_uuid(self.context, instance['uuid'])
|
||||
|
||||
with contextlib.nested(
|
||||
mock.patch('nova.virt.disk.api.setup_container',
|
||||
return_value='/dev/nbd1'),
|
||||
mock.patch('nova.virt.disk.api.clean_lxc_namespace'),
|
||||
mock.patch('nova.openstack.common.fileutils.ensure_tree'),
|
||||
mock.patch.object(conn.image_backend, 'image'),
|
||||
mock.patch.object(conn, '_enable_hairpin'),
|
||||
mock.patch.object(conn, 'get_info',
|
||||
return_value={'state': power_state.RUNNING})
|
||||
):
|
||||
conn._conn.defineXML = mock.Mock()
|
||||
conn._create_domain('xml', instance=inst_obj)
|
||||
self.assertEqual('/dev/nbd1',
|
||||
inst_obj.system_metadata.get(
|
||||
'rootfs_device_name'))
|
||||
|
||||
def _test_create_with_network_events(self, neutron_failure=None,
|
||||
power_on=True):
|
||||
generated_events = []
|
||||
|
|
|
@ -25,6 +25,7 @@ Supports KVM, LXC, QEMU, UML, and XEN.
|
|||
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
import errno
|
||||
import functools
|
||||
import glob
|
||||
|
@ -2589,9 +2590,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
disk_info, image_meta,
|
||||
block_device_info=block_device_info,
|
||||
write_to_disk=True)
|
||||
|
||||
self._create_domain_and_network(context, xml, instance, network_info,
|
||||
block_device_info)
|
||||
block_device_info, disk_info=disk_info)
|
||||
LOG.debug("Instance is running", instance=instance)
|
||||
|
||||
def _wait_for_boot():
|
||||
|
@ -4031,15 +4031,33 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
'cpu_time': dom_info[4],
|
||||
'id': virt_dom.ID()}
|
||||
|
||||
def _create_domain_setup_lxc(self, instance):
|
||||
def _create_domain_setup_lxc(self, instance, block_device_info, disk_info):
|
||||
inst_path = libvirt_utils.get_instance_path(instance)
|
||||
block_device_mapping = driver.block_device_info_get_mapping(
|
||||
block_device_info)
|
||||
disk_info = disk_info or {}
|
||||
disk_mapping = disk_info.get('mapping', [])
|
||||
|
||||
if self._is_booted_from_volume(instance, disk_mapping):
|
||||
root_disk = block_device.get_root_bdm(block_device_mapping)
|
||||
disk_path = root_disk['connection_info']['data']['device_path']
|
||||
disk_info = blockinfo.get_info_from_bdm(
|
||||
CONF.libvirt.virt_type, root_disk)
|
||||
self._connect_volume(root_disk['connection_info'], disk_info)
|
||||
|
||||
# Get the system metadata from the instance
|
||||
system_meta = utils.instance_sys_meta(instance)
|
||||
use_cow = system_meta['image_disk_format'] == 'qcow2'
|
||||
else:
|
||||
image = self.image_backend.image(instance, 'disk')
|
||||
disk_path = image.path
|
||||
use_cow = CONF.use_cow_images
|
||||
|
||||
container_dir = os.path.join(inst_path, 'rootfs')
|
||||
fileutils.ensure_tree(container_dir)
|
||||
|
||||
image = self.image_backend.image(instance, 'disk')
|
||||
rootfs_dev = disk.setup_container(image.path,
|
||||
container_dir=container_dir,
|
||||
use_cow=CONF.use_cow_images)
|
||||
rootfs_dev = disk.setup_container(disk_path,
|
||||
container_dir=container_dir,
|
||||
use_cow=use_cow)
|
||||
|
||||
try:
|
||||
# Save rootfs device to disconnect it when deleting the instance
|
||||
|
@ -4072,6 +4090,28 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
else:
|
||||
disk.teardown_container(container_dir=container_dir)
|
||||
|
||||
@contextlib.contextmanager
|
||||
def _lxc_disk_handler(self, instance, block_device_info, disk_info):
|
||||
"""Context manager to handle the pre and post instance boot,
|
||||
LXC specific disk operations.
|
||||
|
||||
An image or a volume path will be prepared and setup to be
|
||||
used by the container, prior to starting it.
|
||||
The disk will be disconnected and unmounted if a container has
|
||||
failed to start.
|
||||
"""
|
||||
|
||||
if CONF.libvirt.virt_type != 'lxc':
|
||||
yield
|
||||
return
|
||||
|
||||
self._create_domain_setup_lxc(instance, block_device_info, disk_info)
|
||||
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
self._create_domain_cleanup_lxc(instance)
|
||||
|
||||
def _create_domain(self, xml=None, domain=None,
|
||||
instance=None, launch_flags=0, power_on=True):
|
||||
"""Create a domain.
|
||||
|
@ -4080,8 +4120,6 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
the domain definition is overwritten from the xml.
|
||||
"""
|
||||
err = None
|
||||
if instance and CONF.libvirt.virt_type == 'lxc':
|
||||
self._create_domain_setup_lxc(instance)
|
||||
try:
|
||||
if xml:
|
||||
err = _LE('Error defining a domain with XML: %s') % xml
|
||||
|
@ -4100,9 +4138,6 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
with excutils.save_and_reraise_exception():
|
||||
if err:
|
||||
LOG.error(err)
|
||||
finally:
|
||||
if instance and CONF.libvirt.virt_type == 'lxc':
|
||||
self._create_domain_cleanup_lxc(instance)
|
||||
|
||||
return domain
|
||||
|
||||
|
@ -4124,7 +4159,8 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
|
||||
def _create_domain_and_network(self, context, xml, instance, network_info,
|
||||
block_device_info=None, power_on=True,
|
||||
reboot=False, vifs_already_plugged=False):
|
||||
reboot=False, vifs_already_plugged=False,
|
||||
disk_info=None):
|
||||
|
||||
"""Do required network setup and create domain."""
|
||||
block_device_mapping = driver.block_device_info_get_mapping(
|
||||
|
@ -4132,9 +4168,9 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
|
||||
for vol in block_device_mapping:
|
||||
connection_info = vol['connection_info']
|
||||
disk_info = blockinfo.get_info_from_bdm(
|
||||
CONF.libvirt.virt_type, vol)
|
||||
conf = self._connect_volume(connection_info, disk_info)
|
||||
info = blockinfo.get_info_from_bdm(
|
||||
CONF.libvirt.virt_type, vol)
|
||||
conf = self._connect_volume(connection_info, info)
|
||||
|
||||
# cache device_path in connection_info -- required by encryptors
|
||||
if 'data' in connection_info:
|
||||
|
@ -4172,10 +4208,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
|||
network_info)
|
||||
self.firewall_driver.prepare_instance_filter(instance,
|
||||
network_info)
|
||||
domain = self._create_domain(
|
||||
xml, instance=instance,
|
||||
launch_flags=launch_flags,
|
||||
power_on=power_on)
|
||||
with self._lxc_disk_handler(instance, block_device_info,
|
||||
disk_info):
|
||||
domain = self._create_domain(
|
||||
xml, instance=instance,
|
||||
launch_flags=launch_flags,
|
||||
power_on=power_on)
|
||||
|
||||
self.firewall_driver.apply_instance_filter(instance,
|
||||
network_info)
|
||||
|
|
Loading…
Reference in New Issue