Merge "libvirt: start lxc from block device"

This commit is contained in:
Jenkins 2014-09-11 15:09:56 +00:00 committed by Gerrit Code Review
commit d946915e06
2 changed files with 208 additions and 70 deletions

View File

@ -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 = []

View File

@ -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)