Merge "libvirt: attach configdrive after instance XML"

This commit is contained in:
Jenkins
2016-06-28 17:08:16 +00:00
committed by Gerrit Code Review
2 changed files with 123 additions and 116 deletions

View File

@@ -13,7 +13,6 @@
# License for the specific language governing permissions and limitations
# under the License.
from collections import defaultdict
from collections import deque
import contextlib
import copy
@@ -9811,7 +9810,8 @@ class LibvirtConnTestCase(test.NoDBTestCase):
]
self.assertEqual(wantFiles, gotFiles)
def _create_image_helper(self, callback, suffix=''):
def _create_image_helper(self, callback, suffix='',
test_create_configdrive=False):
gotFiles = []
imported_files = []
@@ -9873,8 +9873,11 @@ class LibvirtConnTestCase(test.NoDBTestCase):
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
instance,
image_meta)
drvr._create_image(context, instance, disk_info['mapping'],
suffix=suffix)
if test_create_configdrive:
drvr._create_configdrive(context, instance)
else:
drvr._create_image(context, instance, disk_info['mapping'],
suffix=suffix)
drvr._get_guest_xml(self.context, instance, None,
disk_info, image_meta)
@@ -9896,61 +9899,42 @@ class LibvirtConnTestCase(test.NoDBTestCase):
]
self.assertEqual(gotFiles, wantFiles)
def test_create_image_with_configdrive(self):
def test_create_configdrive(self):
def enable_configdrive(instance_ref):
instance_ref['config_drive'] = 'true'
# Ensure that we create a config drive and then import it into the
# image backend store
_, imported_files = self._create_image_helper(enable_configdrive)
_, imported_files = self._create_image_helper(
enable_configdrive, test_create_configdrive=True)
self.assertTrue(imported_files[0][0].endswith('/disk.config'))
self.assertEqual('disk.config', imported_files[0][1])
def test_create_image_with_configdrive_rescue(self):
def enable_configdrive(instance_ref):
instance_ref['config_drive'] = 'true'
# Ensure that we create a config drive and then import it into the
# image backend store
_, imported_files = self._create_image_helper(enable_configdrive,
suffix='.rescue')
self.assertTrue(imported_files[0][0].endswith('/disk.config.rescue'))
self.assertEqual('disk.config.rescue', imported_files[0][1])
@mock.patch.object(nova.virt.configdrive.ConfigDriveBuilder, 'make_drive')
@mock.patch.object(instance_metadata, 'InstanceMetadata')
def test_create_image_with_configdrive_and_config_drive_exists(
self, mock_instance_metadata, mock_make_drive):
@mock.patch('nova.virt.libvirt.LibvirtDriver._create_configdrive')
@mock.patch('nova.virt.libvirt.LibvirtDriver.get_info')
@mock.patch('nova.virt.libvirt.LibvirtDriver._create_image')
@mock.patch('nova.virt.libvirt.LibvirtDriver._get_guest_xml')
@mock.patch('nova.virt.libvirt.LibvirtDriver._get_instance_disk_info')
@mock.patch('nova.virt.libvirt.blockinfo.get_disk_info')
@mock.patch('nova.virt.libvirt.guest.Guest')
@mock.patch.object(loopingcall, 'FixedIntervalLoopingCall')
def test_spawn_with_config_drive(self, mock_timer, mock_guest, mock_disk,
mock_idisk, mock_guest_xml,
mock_create_image, mock_info,
mock_create):
drvr = libvirt_driver.LibvirtDriver(fake.FakeVirtAPI(), False)
def mock_disk_creator():
exists = mock.Mock(return_value=True)
return mock.Mock(exists=exists)
# The disks dictionary contains Mocks whose exists() method returns
# True
disks = defaultdict(mock_disk_creator)
def fake_image(instance, disk_name, image_type=None):
return disks[disk_name]
# image_backend returns mocks from the disks dict, keyed by disk name
drvr.image_backend.image = mock.Mock(side_effect=fake_image)
instance = objects.Instance(**self.test_instance)
instance.task_state = task_states.RESIZE_FINISH
instance.config_drive = True
image_meta = objects.ImageMeta.from_dict(self.test_image_meta)
disk_info = blockinfo.get_disk_info(CONF.libvirt.virt_type,
instance,
image_meta)
drvr._create_image(self.context, instance, disk_info['mapping'])
mock_make_drive.assert_not_called()
disks['disk.config'].exists.return_value = False
drvr._create_image(self.context, instance, disk_info['mapping'])
self.assertTrue(mock_make_drive.called)
with test.nested(
mock.patch.object(drvr, '_create_images_and_backing'),
mock.patch.object(drvr, 'plug_vifs'),
mock.patch.object(drvr.firewall_driver, 'setup_basic_filtering'),
mock.patch.object(drvr.firewall_driver, 'prepare_instance_filter'),
mock.patch.object(drvr.firewall_driver, 'apply_instance_filter')):
drvr.spawn(self.context, instance,
image_meta, [], None)
self.assertTrue(mock_create.called)
@mock.patch.object(nova.virt.libvirt.imagebackend.Image, 'cache',
side_effect=exception.ImageNotFound(image_id='fake-id'))
@@ -13596,7 +13580,7 @@ class LibvirtConnTestCase(test.NoDBTestCase):
network_info)
pause = self._get_pause_flag(drvr, network_info)
create_domain.assert_called_once_with(
fake_xml, pause=pause, power_on=True)
fake_xml, pause=pause, power_on=True, post_xml_callback=None)
self.assertEqual(mock_dom, guest._domain)
def test_get_guest_storage_config(self):
@@ -14942,7 +14926,7 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
def fake_create_domain_and_network(
context, xml, instance, network_info, disk_info,
block_device_info=None, power_on=True, reboot=False,
vifs_already_plugged=False):
vifs_already_plugged=False, post_xml_callback=None):
self.fake_create_domain_called = True
self.assertEqual(powered_on, power_on)
self.assertTrue(vifs_already_plugged)
@@ -15649,7 +15633,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
).AndReturn(dummyxml)
self.drvr._destroy(instance)
self.drvr._create_domain(mox.IgnoreArg())
self.drvr._create_domain(mox.IgnoreArg(),
post_xml_callback=mox.IgnoreArg())
self.mox.ReplayAll()
@@ -15711,7 +15696,8 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
@mock.patch(
'nova.virt.configdrive.ConfigDriveBuilder.add_instance_metadata')
@mock.patch('nova.virt.configdrive.ConfigDriveBuilder.make_drive')
def test_rescue_config_drive(self, mock_make, mock_add):
@mock.patch('nova.virt.libvirt.guest.Guest')
def test_rescue_config_drive(self, mock_guest, mock_make, mock_add):
instance = self._create_instance()
uuid = instance.uuid
configdrive_path = uuid + '/disk.config.rescue'
@@ -15735,8 +15721,6 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
'__init__')
self.mox.StubOutWithMock(self.drvr, '_get_guest_xml')
self.mox.StubOutWithMock(self.drvr, '_destroy')
self.mox.StubOutWithMock(self.drvr, '_create_domain')
self.drvr._get_existing_domain_xml(mox.IgnoreArg(),
mox.IgnoreArg()).MultipleTimes().AndReturn(dummyxml)
libvirt_utils.write_to_file(mox.IgnoreArg(), mox.IgnoreArg())
@@ -15746,10 +15730,10 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
).AndReturn(mock_backend.kernel)
imagebackend.Backend.image(instance, 'ramdisk.rescue', 'raw'
).AndReturn(mock_backend.ramdisk)
imagebackend.Backend.image(instance, 'disk.config.rescue', 'raw'
).AndReturn(mock_backend.config)
imagebackend.Backend.image(instance, 'disk.rescue', 'default'
).AndReturn(mock_backend.root)
imagebackend.Backend.image(instance, 'disk.config.rescue', 'raw'
).AndReturn(mock_backend.config)
instance_metadata.InstanceMetadata.__init__(mox.IgnoreArg(),
content=mox.IgnoreArg(),
@@ -15764,7 +15748,6 @@ class LibvirtDriverTestCase(test.NoDBTestCase):
write_to_disk=mox.IgnoreArg()
).AndReturn(dummyxml)
self.drvr._destroy(instance)
self.drvr._create_domain(mox.IgnoreArg())
self.mox.ReplayAll()

View File

@@ -2536,6 +2536,11 @@ class LibvirtDriver(driver.ComputeDriver):
instance,
image_meta,
rescue=True)
gen_confdrive = functools.partial(self._create_configdrive,
context, instance,
admin_pass=rescue_password,
network_info=network_info,
suffix='.rescue')
self._create_image(context, instance, disk_info['mapping'],
suffix='.rescue', disk_images=rescue_images,
network_info=network_info,
@@ -2544,7 +2549,7 @@ class LibvirtDriver(driver.ComputeDriver):
image_meta, rescue=rescue_images,
write_to_disk=True)
self._destroy(instance)
self._create_domain(xml)
self._create_domain(xml, post_xml_callback=gen_confdrive)
def unrescue(self, instance, network_info):
"""Reboot the VM which is being rescued back into primary images.
@@ -2581,6 +2586,11 @@ class LibvirtDriver(driver.ComputeDriver):
instance,
image_meta,
block_device_info)
gen_confdrive = functools.partial(self._create_configdrive,
context, instance,
admin_pass=admin_password,
files=injected_files,
network_info=network_info)
self._create_image(context, instance,
disk_info['mapping'],
network_info=network_info,
@@ -2591,9 +2601,10 @@ 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,
disk_info,
block_device_info=block_device_info)
self._create_domain_and_network(
context, xml, instance, network_info, disk_info,
block_device_info=block_device_info,
post_xml_callback=gen_confdrive)
LOG.debug("Instance is running", instance=instance)
def _wait_for_boot():
@@ -2954,54 +2965,11 @@ class LibvirtDriver(driver.ComputeDriver):
image_id=disk_images['ramdisk_id'])
inst_type = instance.get_flavor()
if CONF.libvirt.virt_type == 'uml':
libvirt_utils.chown(image('disk').path, 'root')
# Config drive
config_drive_image = None
if configdrive.required_by(instance):
LOG.info(_LI('Using config drive'), instance=instance)
config_drive_image = self.image_backend.image(
instance, 'disk.config' + suffix,
self._get_disk_config_image_type())
# Don't overwrite an existing config drive
if not config_drive_image.exists():
extra_md = {}
if admin_pass:
extra_md['admin_pass'] = admin_pass
inst_md = instance_metadata.InstanceMetadata(
instance, content=files, extra_md=extra_md,
network_info=network_info)
cdb = configdrive.ConfigDriveBuilder(instance_md=inst_md)
with cdb:
config_drive_local_path = self._get_disk_config_path(
instance, suffix)
LOG.info(_LI('Creating config drive at %(path)s'),
{'path': config_drive_local_path},
instance=instance)
try:
cdb.make_drive(config_drive_local_path)
except processutils.ProcessExecutionError as e:
with excutils.save_and_reraise_exception():
LOG.error(_LE('Creating config drive failed '
'with error: %s'),
e, instance=instance)
try:
config_drive_image.import_file(
instance, config_drive_local_path,
'disk.config' + suffix)
finally:
# NOTE(mikal): if the config drive was imported into RBD,
# then we no longer need the local copy
if CONF.libvirt.images_type == 'rbd':
os.unlink(config_drive_local_path)
need_inject = (config_drive_image is None and inject_files and
CONF.libvirt.inject_partition != -2)
# File injection only if needed
need_inject = inject_files and CONF.libvirt.inject_partition != -2
# NOTE(ndipanov): Even if disk_mapping was passed in, which
# currently happens only on rescue - we still don't want to
@@ -3101,8 +3069,51 @@ class LibvirtDriver(driver.ComputeDriver):
size=size,
swap_mb=swap_mb)
if CONF.libvirt.virt_type == 'uml':
libvirt_utils.chown(image('disk').path, 'root')
def _create_configdrive(self, context, instance, admin_pass=None,
files=None, network_info=None, suffix=''):
config_drive_image = None
if configdrive.required_by(instance):
LOG.info(_LI('Using config drive'), instance=instance)
config_drive_image = self.image_backend.image(
instance, 'disk.config' + suffix,
self._get_disk_config_image_type())
# Don't overwrite an existing config drive
if not config_drive_image.exists():
extra_md = {}
if admin_pass:
extra_md['admin_pass'] = admin_pass
inst_md = instance_metadata.InstanceMetadata(
instance, content=files, extra_md=extra_md,
network_info=network_info)
cdb = configdrive.ConfigDriveBuilder(instance_md=inst_md)
with cdb:
config_drive_local_path = self._get_disk_config_path(
instance, suffix)
LOG.info(_LI('Creating config drive at %(path)s'),
{'path': config_drive_local_path},
instance=instance)
try:
cdb.make_drive(config_drive_local_path)
except processutils.ProcessExecutionError as e:
with excutils.save_and_reraise_exception():
LOG.error(_LE('Creating config drive failed '
'with error: %s'),
e, instance=instance)
try:
config_drive_image.import_file(
instance, config_drive_local_path,
'disk.config' + suffix)
finally:
# NOTE(mikal): if the config drive was imported into RBD,
# then we no longer need the local copy
if CONF.libvirt.images_type == 'rbd':
os.unlink(config_drive_local_path)
def _prepare_pci_devices_for_use(self, pci_devices):
# kvm , qemu support managed mode
@@ -3348,9 +3359,13 @@ class LibvirtDriver(driver.ComputeDriver):
# to rbd yet. Try to fall back on 'flat' image type.
# TODO(melwitt): Add online migration of some sort so we can
# remove this fall back once we know all config drives are in rbd.
image = self.image_backend.image(instance, name, 'flat')
LOG.debug('Config drive not found in RBD, falling back to the '
'instance directory', instance=instance)
# NOTE(vladikr): make sure that the flat image exist, otherwise
# the image will be created after the domain definition.
flat_image = self.image_backend.image(instance, name, 'flat')
if flat_image.exists():
image = flat_image
LOG.debug('Config drive not found in RBD, falling back to the '
'instance directory', instance=instance)
disk_info = disk_mapping[name]
return image.libvirt_info(disk_info['bus'],
disk_info['dev'],
@@ -4691,7 +4706,7 @@ class LibvirtDriver(driver.ComputeDriver):
# TODO(sahid): Consider renaming this to _create_guest.
def _create_domain(self, xml=None, domain=None,
power_on=True, pause=False):
power_on=True, pause=False, post_xml_callback=None):
"""Create a domain.
Either domain or xml must be passed in. If both are passed, then
@@ -4701,6 +4716,8 @@ class LibvirtDriver(driver.ComputeDriver):
"""
if xml:
guest = libvirt_guest.Guest.create(xml, self._host)
if post_xml_callback is not None:
post_xml_callback()
else:
guest = libvirt_guest.Guest(domain)
@@ -4732,7 +4749,8 @@ class LibvirtDriver(driver.ComputeDriver):
def _create_domain_and_network(self, context, xml, instance, network_info,
disk_info, block_device_info=None,
power_on=True, reboot=False,
vifs_already_plugged=False):
vifs_already_plugged=False,
post_xml_callback=None):
"""Do required network setup and create domain."""
block_device_mapping = driver.block_device_info_get_mapping(
@@ -4774,7 +4792,8 @@ class LibvirtDriver(driver.ComputeDriver):
with self._lxc_disk_handler(instance, instance.image_meta,
block_device_info, disk_info):
guest = self._create_domain(
xml, pause=pause, power_on=power_on)
xml, pause=pause, power_on=power_on,
post_xml_callback=post_xml_callback)
self.firewall_driver.apply_instance_filter(instance,
network_info)
@@ -7141,6 +7160,10 @@ class LibvirtDriver(driver.ComputeDriver):
block_device_info=None, inject_files=False,
fallback_from_host=migration.source_compute)
gen_confdrive = functools.partial(self._create_configdrive,
context, instance,
network_info=network_info)
# Resize root disk and a single ephemeral disk called disk.local
# Also convert raw disks to qcow2 if migrating to host which uses
# qcow2 from host which uses raw.
@@ -7208,7 +7231,8 @@ class LibvirtDriver(driver.ComputeDriver):
block_disk_info,
block_device_info=block_device_info,
power_on=power_on,
vifs_already_plugged=True)
vifs_already_plugged=True,
post_xml_callback=gen_confdrive)
if power_on:
timer = loopingcall.FixedIntervalLoopingCall(
self._wait_for_running,