Config drive v2
This is the first cut of config drive v2. Some points to note: - implements a helper to create new-style config drives. These config drives can be VFAT or ISO9660, this is controlled by a flag. The current default is ISO9660. - the config drives contain all the injected files, as well as everything returned from the ec2 style metadata service. Only the most recent version of the ec2 metadata is used, but future versions will appear as well. - the v1 functionality of specifying an image from glance to have the files injected into is dropped. - the location for file injection is now a directory named openstack/files, not the root level of the filesystem. Filename mapping is in the openstack metadata files. - the default format for the config drive is iso9660, although the previous vfat is available with a flag change. - includes the first version of an openstack metadata format. - there are some simple unit tests which probably need more done to them. Partially implements bp config-drive-v2. Change-Id: I210fa4dd7d8d6be398a46b30a0d46b960e22d6b0
This commit is contained in:
@@ -13,10 +13,12 @@ tune2fs: CommandFilter, /sbin/tune2fs, root
|
||||
|
||||
# nova/virt/disk/mount.py: 'mount', mapped_device, mount_dir
|
||||
# nova/virt/xenapi/vm_utils.py: 'mount', '-t', 'ext2,ext3,ext4,reiserfs'..
|
||||
# nova/virt/configdrive.py: 'mount', device, mountdir
|
||||
mount: CommandFilter, /bin/mount, root
|
||||
|
||||
# nova/virt/disk/mount.py: 'umount', mapped_device
|
||||
# nova/virt/xenapi/vm_utils.py: 'umount', dev_path
|
||||
# nova/virt/configdrive.py: 'umount', mountdir
|
||||
umount: CommandFilter, /bin/umount, root
|
||||
|
||||
# nova/virt/disk/nbd.py: 'qemu-nbd', '-c', device, image
|
||||
|
@@ -19,6 +19,7 @@
|
||||
"""Instance Metadata information."""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
|
||||
from nova.api.ec2 import ec2utils
|
||||
@@ -27,9 +28,21 @@ from nova import context
|
||||
from nova import db
|
||||
from nova import flags
|
||||
from nova import network
|
||||
from nova.openstack.common import cfg
|
||||
|
||||
|
||||
metadata_opts = [
|
||||
cfg.StrOpt('config_drive_skip_versions',
|
||||
default=('1.0 2007-01-19 2007-03-01 2007-08-29 2007-10-10 '
|
||||
'2007-12-15 2008-02-01 2008-09-01'),
|
||||
help=('List of metadata versions to skip placing into the '
|
||||
'config drive')),
|
||||
]
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
flags.DECLARE('dhcp_domain', 'nova.network.manager')
|
||||
FLAGS.register_opts(metadata_opts)
|
||||
|
||||
|
||||
_DEFAULT_MAPPINGS = {'ami': 'sda1',
|
||||
'ephemeral0': 'sda2',
|
||||
@@ -205,6 +218,39 @@ class InstanceMetadata():
|
||||
|
||||
return data
|
||||
|
||||
def metadata_for_config_drive(self, injected_files):
|
||||
"""Yields (path, value) tuples for metadata elements."""
|
||||
# EC2 style metadata
|
||||
for version in VERSIONS:
|
||||
if version in FLAGS.config_drive_skip_versions.split(' '):
|
||||
continue
|
||||
|
||||
data = self.get_ec2_metadata(version)
|
||||
if 'user-data' in data:
|
||||
filepath = os.path.join('ec2', version, 'userdata.raw')
|
||||
yield (filepath, data['user-data'])
|
||||
del data['user-data']
|
||||
|
||||
try:
|
||||
del data['public-keys']['0']['_name']
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
filepath = os.path.join('ec2', version, 'metadata.json')
|
||||
yield (filepath, json.dumps(data['meta-data']))
|
||||
|
||||
filepath = os.path.join('ec2', 'latest', 'metadata.json')
|
||||
yield (filepath, json.dumps(data['meta-data']))
|
||||
|
||||
# Openstack style metadata
|
||||
# TODO(mikal): refactor this later
|
||||
files = []
|
||||
for path in injected_files:
|
||||
files.append({'path': path,
|
||||
'content': injected_files[path]})
|
||||
yield ('openstack/2012-08-10/files.json', json.dumps(files))
|
||||
yield ('openstack/latest/files.json', json.dumps(files))
|
||||
|
||||
|
||||
def get_metadata_by_address(address):
|
||||
ctxt = context.get_admin_context()
|
||||
|
@@ -1145,6 +1145,16 @@ class InstanceIsLocked(InstanceInvalidState):
|
||||
message = _("Instance %(instance_uuid)s is locked")
|
||||
|
||||
|
||||
class ConfigDriveMountFailed(NovaException):
|
||||
message = _("Could not mount vfat config drive. %(operation)s failed. "
|
||||
"Error: %(error)s")
|
||||
|
||||
|
||||
class ConfigDriveUnknownFormat(NovaException):
|
||||
message = _("Unknown config drive format %(format)s. Select one of "
|
||||
"iso9660 or vfat.")
|
||||
|
||||
|
||||
def get_context_from_function_and_args(function, args, kwargs):
|
||||
"""Find an arg of type RequestContext and return it.
|
||||
|
||||
|
103
nova/tests/test_configdrive2.py
Normal file
103
nova/tests/test_configdrive2.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 Michael Still and Canonical Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
|
||||
import mox
|
||||
import os
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
from nova import test
|
||||
|
||||
from nova import flags
|
||||
from nova.openstack.common import log
|
||||
from nova import utils
|
||||
from nova.virt import configdrive
|
||||
from nova.virt.libvirt import utils as virtutils
|
||||
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
|
||||
class ConfigDriveTestCase(test.TestCase):
|
||||
|
||||
def test_create_configdrive_iso(self):
|
||||
imagefile = None
|
||||
|
||||
try:
|
||||
self.mox.StubOutWithMock(utils, 'execute')
|
||||
|
||||
utils.execute('genisoimage', '-o', mox.IgnoreArg(), '-ldots',
|
||||
'-allow-lowercase', '-allow-multidot', '-l',
|
||||
'-publisher', mox.IgnoreArg(), '-quiet', '-J', '-r',
|
||||
'-V', 'config-2', mox.IgnoreArg(), attempts=1,
|
||||
run_as_root=False).AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
c = configdrive.ConfigDriveBuilder()
|
||||
c._add_file('this/is/a/path/hello', 'This is some content')
|
||||
(fd, imagefile) = tempfile.mkstemp(prefix='cd_iso_')
|
||||
os.close(fd)
|
||||
c._make_iso9660(imagefile)
|
||||
c.cleanup()
|
||||
|
||||
# Check cleanup
|
||||
self.assertFalse(os.path.exists(c.tempdir))
|
||||
|
||||
finally:
|
||||
if imagefile:
|
||||
utils.delete_if_exists(imagefile)
|
||||
|
||||
def test_create_configdrive_vfat(self):
|
||||
imagefile = None
|
||||
try:
|
||||
self.mox.StubOutWithMock(virtutils, 'mkfs')
|
||||
self.mox.StubOutWithMock(utils, 'execute')
|
||||
self.mox.StubOutWithMock(utils, 'trycmd')
|
||||
|
||||
virtutils.mkfs('vfat', mox.IgnoreArg(),
|
||||
label='config-2').AndReturn(None)
|
||||
utils.trycmd('mount', '-o', 'loop', mox.IgnoreArg(),
|
||||
mox.IgnoreArg(),
|
||||
run_as_root=True).AndReturn((None, None))
|
||||
utils.trycmd('chown', mox.IgnoreArg(), mox.IgnoreArg(),
|
||||
run_as_root=True).AndReturn((None, None))
|
||||
utils.execute('umount', mox.IgnoreArg(),
|
||||
run_as_root=True).AndReturn(None)
|
||||
|
||||
self.mox.ReplayAll()
|
||||
|
||||
c = configdrive.ConfigDriveBuilder()
|
||||
c._add_file('this/is/a/path/hello', 'This is some content')
|
||||
(fd, imagefile) = tempfile.mkstemp(prefix='cd_vfat_')
|
||||
os.close(fd)
|
||||
c._make_vfat(imagefile)
|
||||
c.cleanup()
|
||||
|
||||
# Check cleanup
|
||||
self.assertFalse(os.path.exists(c.tempdir))
|
||||
|
||||
# NOTE(mikal): we can't check for a VFAT output here because the
|
||||
# filesystem creation stuff has been mocked out because it
|
||||
# requires root permissions
|
||||
|
||||
finally:
|
||||
if imagefile:
|
||||
utils.delete_if_exists(imagefile)
|
@@ -88,6 +88,9 @@ class _FakeDriverBackendTestCase(test.TestCase):
|
||||
def fake_migrateToURI(*a):
|
||||
pass
|
||||
|
||||
def fake_make_drive(_self, _path):
|
||||
pass
|
||||
|
||||
self.stubs.Set(nova.virt.libvirt.driver.disk,
|
||||
'extend', fake_extend)
|
||||
|
||||
@@ -96,6 +99,11 @@ class _FakeDriverBackendTestCase(test.TestCase):
|
||||
self.stubs.Set(nova.virt.libvirt.driver.libvirt.Domain,
|
||||
'migrateToURI', fake_migrateToURI)
|
||||
|
||||
# We can't actually make a config drive v2 because ensure_tree has
|
||||
# been faked out
|
||||
self.stubs.Set(nova.virt.configdrive.ConfigDriveBuilder,
|
||||
'make_drive', fake_make_drive)
|
||||
|
||||
def _teardown_fakelibvirt(self):
|
||||
# Restore libvirt
|
||||
import nova.virt.libvirt.driver
|
||||
|
173
nova/virt/configdrive.py
Normal file
173
nova/virt/configdrive.py
Normal file
@@ -0,0 +1,173 @@
|
||||
# vim: tabstop=4 shiftwidth=4 softtabstop=4
|
||||
|
||||
# Copyright 2012 Michael Still and Canonical Inc
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
"""Config Drive v2 helper."""
|
||||
|
||||
import base64
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import tempfile
|
||||
|
||||
from nova.api.metadata import base as instance_metadata
|
||||
from nova import exception
|
||||
from nova import flags
|
||||
from nova.openstack.common import cfg
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
from nova import version
|
||||
from nova.virt.libvirt import utils as virtutils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
configdrive_opts = [
|
||||
cfg.StrOpt('config_drive_format',
|
||||
default='iso9660',
|
||||
help='Config drive format. One of iso9660 (default) or vfat'),
|
||||
cfg.StrOpt('config_drive_tempdir',
|
||||
default=tempfile.tempdir,
|
||||
help=('Where to put temporary files associated with '
|
||||
'config drive creation')),
|
||||
]
|
||||
|
||||
FLAGS = flags.FLAGS
|
||||
FLAGS.register_opts(configdrive_opts)
|
||||
|
||||
|
||||
class ConfigDriveBuilder(object):
|
||||
def __init__(self, instance=None):
|
||||
self.instance = instance
|
||||
self.imagefile = None
|
||||
self.injected = {}
|
||||
self.next_inject_id = 1
|
||||
|
||||
# TODO(mikal): I don't think I can use utils.tempdir here, because
|
||||
# I need to have the directory last longer than the scope of this
|
||||
# method call
|
||||
self.tempdir = tempfile.mkdtemp(dir=FLAGS.config_drive_tempdir,
|
||||
prefix='cd_gen_')
|
||||
|
||||
def _add_file(self, path, data, inject=False):
|
||||
if inject:
|
||||
path_id = '%03d' % self.next_inject_id
|
||||
path = 'openstack/files/%s' % path_id
|
||||
self.injected[path] = path_id
|
||||
self.next_inject_id += 1
|
||||
|
||||
filepath = os.path.join(self.tempdir, path)
|
||||
dirname = os.path.dirname(filepath)
|
||||
virtutils.ensure_tree(dirname)
|
||||
with open(filepath, 'w') as f:
|
||||
f.write(data)
|
||||
|
||||
def add_instance_metadata(self):
|
||||
inst_md = instance_metadata.InstanceMetadata(self.instance)
|
||||
for (path, value) in inst_md.metadata_for_config_drive(self.injected):
|
||||
self._add_file(path, value)
|
||||
LOG.debug(_('Added %(filepath)s to config drive'),
|
||||
{'filepath': path}, instance=self.instance)
|
||||
|
||||
def inject_data(self, key, net, metadata, admin_pass, files):
|
||||
if key:
|
||||
self._add_file('key', key, inject=True)
|
||||
if net:
|
||||
self._add_file('net', net, inject=True)
|
||||
if metadata:
|
||||
self._add_file('metadata', metadata, inject=True)
|
||||
if admin_pass:
|
||||
self._add_file('adminpass', admin_pass, inject=True)
|
||||
if files:
|
||||
files_struct = []
|
||||
for (path, contents) in files:
|
||||
files.append[{'path': path,
|
||||
'encoding': 'base64',
|
||||
'data': base64.b64encode(contents),
|
||||
}]
|
||||
self._add_file('files', json.dumps(files_struct), inject=True)
|
||||
|
||||
def _make_iso9660(self, path):
|
||||
utils.execute('genisoimage',
|
||||
'-o', path,
|
||||
'-ldots',
|
||||
'-allow-lowercase',
|
||||
'-allow-multidot',
|
||||
'-l',
|
||||
'-publisher', ('"OpenStack nova %s"'
|
||||
% version.version_string()),
|
||||
'-quiet',
|
||||
'-J',
|
||||
'-r',
|
||||
'-V', 'config-2',
|
||||
self.tempdir,
|
||||
attempts=1,
|
||||
run_as_root=False)
|
||||
|
||||
def _make_vfat(self, path):
|
||||
# NOTE(mikal): This is a little horrible, but I couldn't find an
|
||||
# equivalent to genisoimage for vfat filesystems. vfat images are
|
||||
# always 64mb.
|
||||
with open(path, 'w') as f:
|
||||
f.truncate(64 * 1024 * 1024)
|
||||
|
||||
virtutils.mkfs('vfat', path, label='config-2')
|
||||
|
||||
mounted = False
|
||||
try:
|
||||
mountdir = tempfile.mkdtemp(dir=FLAGS.config_drive_tempdir,
|
||||
prefix='cd_mnt_')
|
||||
_out, err = utils.trycmd('mount', '-o', 'loop', path, mountdir,
|
||||
run_as_root=True)
|
||||
if err:
|
||||
raise exception.ConfigDriveMountFailed(operation='mount',
|
||||
error=err)
|
||||
mounted = True
|
||||
|
||||
_out, err = utils.trycmd('chown',
|
||||
'%s.%s' % (os.getuid(), os.getgid()),
|
||||
mountdir, run_as_root=True)
|
||||
if err:
|
||||
raise exception.ConfigDriveMountFailed(operation='chown',
|
||||
error=err)
|
||||
|
||||
# NOTE(mikal): I can't just use shutils.copytree here, because the
|
||||
# destination directory already exists. This is annoying.
|
||||
for ent in os.listdir(self.tempdir):
|
||||
shutil.copytree(os.path.join(self.tempdir, ent),
|
||||
os.path.join(mountdir, ent))
|
||||
|
||||
finally:
|
||||
if mounted:
|
||||
utils.execute('umount', mountdir, run_as_root=True)
|
||||
shutil.rmtree(mountdir)
|
||||
|
||||
def make_drive(self, path):
|
||||
if FLAGS.config_drive_format == 'iso9660':
|
||||
self._make_iso9660(path)
|
||||
elif FLAGS.config_drive_format == 'vfat':
|
||||
self._make_vfat(path)
|
||||
else:
|
||||
raise exception.ConfigDriveUnknownFormat(
|
||||
format=FLAGS.config_drive_format)
|
||||
|
||||
def cleanup(self):
|
||||
if self.imagefile:
|
||||
utils.delete_if_exists(self.imagefile)
|
||||
|
||||
try:
|
||||
shutil.rmtree(self.tempdir)
|
||||
except OSError, e:
|
||||
LOG.error(_('Could not remove tmpdir: %s'), str(e))
|
@@ -71,6 +71,7 @@ from nova.openstack.common import importutils
|
||||
from nova.openstack.common import jsonutils
|
||||
from nova.openstack.common import log as logging
|
||||
from nova import utils
|
||||
from nova.virt import configdrive
|
||||
from nova.virt.disk import api as disk
|
||||
from nova.virt import driver
|
||||
from nova.virt.libvirt import config
|
||||
@@ -1207,6 +1208,13 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
if not suffix:
|
||||
suffix = ''
|
||||
|
||||
# Are we using a config drive?
|
||||
using_config_drive = False
|
||||
if (instance.get('config_drive') or
|
||||
FLAGS.force_config_drive):
|
||||
LOG.info(_('Using config drive'), instance=instance)
|
||||
using_config_drive = True
|
||||
|
||||
# syntactic nicety
|
||||
def basepath(fname='', suffix=suffix):
|
||||
return os.path.join(FLAGS.instances_path,
|
||||
@@ -1325,30 +1333,15 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
size=size,
|
||||
swap_mb=swap_mb)
|
||||
|
||||
# target partition for file injection
|
||||
target_partition = None
|
||||
if not instance['kernel_id']:
|
||||
target_partition = FLAGS.libvirt_inject_partition
|
||||
if target_partition == 0:
|
||||
target_partition = None
|
||||
|
||||
config_drive, config_drive_id = self._get_config_drive_info(instance)
|
||||
|
||||
if any((FLAGS.libvirt_type == 'lxc', config_drive, config_drive_id)):
|
||||
if FLAGS.libvirt_type == 'lxc':
|
||||
target_partition = None
|
||||
|
||||
if config_drive_id:
|
||||
fname = config_drive_id
|
||||
raw('disk.config').cache(fn=libvirt_utils.fetch_image,
|
||||
fname=fname,
|
||||
image_id=config_drive_id,
|
||||
user_id=instance['user_id'],
|
||||
project_id=instance['project_id'])
|
||||
elif config_drive:
|
||||
label = 'config'
|
||||
with utils.remove_path_on_error(basepath('disk.config')):
|
||||
self._create_local(basepath('disk.config'), 64, unit='M',
|
||||
fs_format='msdos', label=label) # 64MB
|
||||
|
||||
if FLAGS.libvirt_inject_key and instance['key_data']:
|
||||
key = str(instance['key_data'])
|
||||
else:
|
||||
@@ -1391,6 +1384,12 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
searchList=[{'interfaces': nets,
|
||||
'use_ipv6': FLAGS.use_ipv6}]))
|
||||
|
||||
# Config drive
|
||||
cdb = None
|
||||
if using_config_drive:
|
||||
cdb = configdrive.ConfigDriveBuilder(instance=instance)
|
||||
|
||||
# File injection
|
||||
metadata = instance.get('metadata')
|
||||
|
||||
if FLAGS.libvirt_inject_password:
|
||||
@@ -1401,28 +1400,45 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
files = instance.get('injected_files')
|
||||
|
||||
if any((key, net, metadata, admin_pass, files)):
|
||||
if config_drive: # Should be True or None by now.
|
||||
injection_path = raw('disk.config').path
|
||||
img_id = 'config-drive'
|
||||
else:
|
||||
if not using_config_drive:
|
||||
# If we're not using config_drive, inject into root fs
|
||||
injection_path = image('disk').path
|
||||
img_id = instance['image_ref']
|
||||
|
||||
for injection in ('metadata', 'key', 'net', 'admin_pass', 'files'):
|
||||
if locals()[injection]:
|
||||
LOG.info(_('Injecting %(injection)s into image'
|
||||
' %(img_id)s'), locals(), instance=instance)
|
||||
try:
|
||||
disk.inject_data(injection_path,
|
||||
key, net, metadata, admin_pass, files,
|
||||
partition=target_partition,
|
||||
use_cow=FLAGS.use_cow_images)
|
||||
for injection in ('metadata', 'key', 'net', 'admin_pass',
|
||||
'files'):
|
||||
if locals()[injection]:
|
||||
LOG.info(_('Injecting %(injection)s into image'
|
||||
' %(img_id)s'), locals(), instance=instance)
|
||||
try:
|
||||
disk.inject_data(injection_path,
|
||||
key, net, metadata, admin_pass, files,
|
||||
partition=target_partition,
|
||||
use_cow=FLAGS.use_cow_images)
|
||||
|
||||
except Exception as e:
|
||||
# This could be a windows image, or a vmdk format disk
|
||||
LOG.warn(_('Ignoring error injecting data into image '
|
||||
'%(img_id)s (%(e)s)') % locals(),
|
||||
instance=instance)
|
||||
except Exception as e:
|
||||
# This could be a windows image, or a vmdk format disk
|
||||
LOG.warn(_('Ignoring error injecting data into image '
|
||||
'%(img_id)s (%(e)s)') % locals(),
|
||||
instance=instance)
|
||||
|
||||
else:
|
||||
# We're using config_drive, so put the files there instead
|
||||
cdb.inject_data(key, net, metadata, admin_pass, files)
|
||||
|
||||
if using_config_drive:
|
||||
# NOTE(mikal): Render the config drive. We can't add instance
|
||||
# metadata here until after file injection, as the file injection
|
||||
# creates state the openstack metadata relies on.
|
||||
cdb.add_instance_metadata()
|
||||
|
||||
try:
|
||||
configdrive_path = basepath(fname='disk.config')
|
||||
LOG.info(_('Creating config drive at %(path)s'),
|
||||
{'path': configdrive_path}, instance=instance)
|
||||
cdb.make_drive(configdrive_path)
|
||||
finally:
|
||||
cdb.cleanup()
|
||||
|
||||
if FLAGS.libvirt_type == 'lxc':
|
||||
disk.setup_container(basepath('disk'),
|
||||
@@ -1450,18 +1466,6 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
LOG.debug(_("block_device_list %s"), block_device_list)
|
||||
return block_device.strip_dev(mount_device) in block_device_list
|
||||
|
||||
def _get_config_drive_info(self, instance):
|
||||
config_drive = instance.get('config_drive')
|
||||
config_drive_id = instance.get('config_drive_id')
|
||||
if FLAGS.force_config_drive:
|
||||
if not config_drive_id:
|
||||
config_drive = True
|
||||
return config_drive, config_drive_id
|
||||
|
||||
def _has_config_drive(self, instance):
|
||||
config_drive, config_drive_id = self._get_config_drive_info(instance)
|
||||
return any((config_drive, config_drive_id))
|
||||
|
||||
def get_host_capabilities(self):
|
||||
"""Returns an instance of config.LibvirtConfigCaps representing
|
||||
the capabilities of the host"""
|
||||
@@ -1657,7 +1661,9 @@ class LibvirtDriver(driver.ComputeDriver):
|
||||
mount_device)
|
||||
devices.append(cfg)
|
||||
|
||||
if self._has_config_drive(instance):
|
||||
if (instance.get('config_drive') or
|
||||
instance.get('config_drive_id') or
|
||||
FLAGS.force_config_drive):
|
||||
diskconfig = config.LibvirtConfigGuestDisk()
|
||||
diskconfig.source_type = "file"
|
||||
diskconfig.driver_format = "raw"
|
||||
|
Reference in New Issue
Block a user