summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--nova/tests/virt/xenapi/test_vm_utils.py89
-rw-r--r--nova/virt/configdrive.py8
-rw-r--r--nova/virt/xenapi/vm_utils.py51
-rw-r--r--nova/virt/xenapi/vmops.py21
4 files changed, 159 insertions, 10 deletions
diff --git a/nova/tests/virt/xenapi/test_vm_utils.py b/nova/tests/virt/xenapi/test_vm_utils.py
new file mode 100644
index 000000000..275088af0
--- /dev/null
+++ b/nova/tests/virt/xenapi/test_vm_utils.py
@@ -0,0 +1,89 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2013 OpenStack LLC
+# 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 contextlib
+import fixtures
+import mox
+import uuid
+
+from nova import test
+from nova.tests.xenapi import stubs
+from nova import utils
+from nova.virt.xenapi import vm_utils
+
+
+@contextlib.contextmanager
+def contextified(result):
+ yield result
+
+
+def _fake_noop(*args, **kwargs):
+ return
+
+
+class GenerateConfigDriveTestCase(test.TestCase):
+ def test_no_admin_pass(self):
+ # This is here to avoid masking errors, it shouldn't be used normally
+ self.useFixture(fixtures.MonkeyPatch(
+ 'nova.virt.xenapi.vm_utils.destroy_vdi', _fake_noop))
+
+ # Mocks
+ instance = {}
+
+ self.mox.StubOutWithMock(vm_utils, 'safe_find_sr')
+ vm_utils.safe_find_sr('session').AndReturn('sr_ref')
+
+ self.mox.StubOutWithMock(vm_utils, 'create_vdi')
+ vm_utils.create_vdi('session', 'sr_ref', instance, 'config-2',
+ 'configdrive',
+ 64 * 1024 * 1024).AndReturn('vdi_ref')
+
+ self.mox.StubOutWithMock(vm_utils, 'vdi_attached_here')
+ vm_utils.vdi_attached_here(
+ 'session', 'vdi_ref', read_only=False).AndReturn(
+ contextified('mounted_dev'))
+
+ class FakeInstanceMetadata(object):
+ def __init__(self, instance, content=None, extra_md=None):
+ pass
+
+ def metadata_for_config_drive(self):
+ return []
+
+ self.useFixture(fixtures.MonkeyPatch(
+ 'nova.api.metadata.base.InstanceMetadata',
+ FakeInstanceMetadata))
+
+ 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)
+ utils.execute('dd', mox.IgnoreArg(), mox.IgnoreArg(),
+ run_as_root=True).AndReturn(None)
+
+ self.mox.StubOutWithMock(vm_utils, 'create_vbd')
+ vm_utils.create_vbd('session', 'vm_ref', 'vdi_ref', mox.IgnoreArg(),
+ bootable=False, read_only=True).AndReturn(None)
+
+ self.mox.ReplayAll()
+
+ # And the actual call we're testing
+ vm_utils.generate_configdrive('session', instance, 'vm_ref',
+ 'userdevice')
diff --git a/nova/virt/configdrive.py b/nova/virt/configdrive.py
index 321bf8389..9255ab851 100644
--- a/nova/virt/configdrive.py
+++ b/nova/virt/configdrive.py
@@ -54,6 +54,9 @@ configdrive_opts = [
CONF = cfg.CONF
CONF.register_opts(configdrive_opts)
+# Config drives are 64mb, if we can't size to the exact size of the data
+CONFIGDRIVESIZE_BYTES = 64 * 1024 * 1024
+
@contextlib.contextmanager
def config_drive_helper(instance_md=None):
@@ -116,10 +119,9 @@ class _ConfigDriveBuilder(object):
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.
+ # equivalent to genisoimage for vfat filesystems.
with open(path, 'w') as f:
- f.truncate(64 * 1024 * 1024)
+ f.truncate(CONFIGDRIVESIZE_BYTES)
utils.mkfs('vfat', path, label='config-2')
diff --git a/nova/virt/xenapi/vm_utils.py b/nova/virt/xenapi/vm_utils.py
index 40d43da8d..ac35a4f2b 100644
--- a/nova/virt/xenapi/vm_utils.py
+++ b/nova/virt/xenapi/vm_utils.py
@@ -34,6 +34,7 @@ from xml.parsers import expat
from eventlet import greenthread
+from nova.api.metadata import base as instance_metadata
from nova import block_device
from nova.compute import power_state
from nova.compute import task_states
@@ -43,6 +44,7 @@ from nova.openstack.common import cfg
from nova.openstack.common import excutils
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.disk.vfs import localfs as vfsimpl
from nova.virt import driver
@@ -153,6 +155,7 @@ class ImageType(object):
| 4 - vhd disk image (local SR, NOT inspected by XS, PV assumed for
| linux, HVM assumed for Windows)
| 5 - ISO disk image (local SR, NOT partitioned by plugin)
+ | 6 - config drive
"""
KERNEL = 0
@@ -161,7 +164,9 @@ class ImageType(object):
DISK_RAW = 3
DISK_VHD = 4
DISK_ISO = 5
- _ids = (KERNEL, RAMDISK, DISK, DISK_RAW, DISK_VHD, DISK_ISO)
+ DISK_CONFIGDRIVE = 6
+ _ids = (KERNEL, RAMDISK, DISK, DISK_RAW, DISK_VHD, DISK_ISO,
+ DISK_CONFIGDRIVE)
KERNEL_STR = "kernel"
RAMDISK_STR = "ramdisk"
@@ -169,8 +174,9 @@ class ImageType(object):
DISK_RAW_STR = "os_raw"
DISK_VHD_STR = "vhd"
DISK_ISO_STR = "iso"
+ DISK_CONFIGDRIVE_STR = "configdrive"
_strs = (KERNEL_STR, RAMDISK_STR, DISK_STR, DISK_RAW_STR, DISK_VHD_STR,
- DISK_ISO_STR)
+ DISK_ISO_STR, DISK_CONFIGDRIVE_STR)
@classmethod
def to_string(cls, image_type):
@@ -178,14 +184,15 @@ class ImageType(object):
@classmethod
def get_role(cls, image_type_id):
- " Get the role played by the image, based on its type "
+ """Get the role played by the image, based on its type."""
return {
cls.KERNEL: 'kernel',
cls.RAMDISK: 'ramdisk',
cls.DISK: 'root',
cls.DISK_RAW: 'root',
cls.DISK_VHD: 'root',
- cls.DISK_ISO: 'iso'
+ cls.DISK_ISO: 'iso',
+ cls.DISK_CONFIGDRIVE: 'configdrive'
}.get(image_type_id)
@@ -868,6 +875,42 @@ def generate_ephemeral(session, instance, vm_ref, userdevice, name_label,
CONF.default_ephemeral_format)
+def generate_configdrive(session, instance, vm_ref, userdevice,
+ admin_password=None, files=None):
+ sr_ref = safe_find_sr(session)
+ vdi_ref = create_vdi(session, sr_ref, instance, 'config-2',
+ 'configdrive', configdrive.CONFIGDRIVESIZE_BYTES)
+
+ try:
+ with vdi_attached_here(session, vdi_ref, read_only=False) as dev:
+ dev_path = utils.make_dev_path(dev)
+
+ # NOTE(mikal): libvirt supports injecting the admin password as
+ # well. This is not currently implemented for xenapi as it is not
+ # supported by the existing file injection
+ extra_md = {}
+ if admin_password:
+ extra_md['admin_pass'] = admin_password
+ inst_md = instance_metadata.InstanceMetadata(instance,
+ content=files,
+ extra_md=extra_md)
+ with configdrive.config_drive_helper(instance_md=inst_md) as cdb:
+ with utils.tempdir() as tmp_path:
+ tmp_file = os.path.join(tmp_path, 'configdrive')
+ cdb.make_drive(tmp_file)
+
+ utils.execute('dd',
+ 'if=%s' % tmp_file,
+ 'of=%s' % dev_path,
+ run_as_root=True)
+
+ create_vbd(session, vm_ref, vdi_ref, userdevice, bootable=False,
+ read_only=True)
+ except Exception:
+ with excutils.save_and_reraise_exception():
+ destroy_vdi(session, vdi_ref)
+
+
def create_kernel_image(context, session, instance, name_label, image_id,
image_type):
"""Creates kernel/ramdisk file from the image stored in the cache.
diff --git a/nova/virt/xenapi/vmops.py b/nova/virt/xenapi/vmops.py
index e8e0f3cb0..4a8372cda 100644
--- a/nova/virt/xenapi/vmops.py
+++ b/nova/virt/xenapi/vmops.py
@@ -40,6 +40,7 @@ from nova.openstack.common import jsonutils
from nova.openstack.common import log as logging
from nova.openstack.common import timeutils
from nova import utils
+from nova.virt import configdrive
from nova.virt import driver as virt_driver
from nova.virt import firewall
from nova.virt.xenapi import agent as xapi_agent
@@ -77,6 +78,7 @@ DEVICE_RESCUE = '1'
DEVICE_SWAP = '2'
DEVICE_EPHEMERAL = '3'
DEVICE_CD = '4'
+DEVICE_CONFIGDRIVE = '5'
def cmp_version(a, b):
@@ -344,7 +346,8 @@ class VMOps(object):
@step
def attach_disks_step(undo_mgr, vm_ref, vdis, disk_image_type):
self._attach_disks(instance, vm_ref, name_label, vdis,
- disk_image_type)
+ disk_image_type, admin_password,
+ injected_files)
if rescue:
# NOTE(johannes): Attach root disk to rescue VM now, before
@@ -437,7 +440,12 @@ class VMOps(object):
disk_image_type)
self._setup_vm_networking(instance, vm_ref, vdis, network_info,
rescue)
- self.inject_instance_metadata(instance, vm_ref)
+
+ # NOTE(mikal): file injection only happens if we are _not_ using a
+ # configdrive.
+ if not configdrive.required_by(instance):
+ self.inject_instance_metadata(instance, vm_ref)
+
return vm_ref
def _setup_vm_networking(self, instance, vm_ref, vdis, network_info,
@@ -491,7 +499,7 @@ class VMOps(object):
return vm_ref
def _attach_disks(self, instance, vm_ref, name_label, vdis,
- disk_image_type):
+ disk_image_type, admin_password=None, files=None):
ctx = nova_context.get_admin_context()
instance_type = instance['instance_type']
@@ -537,6 +545,13 @@ class VMOps(object):
DEVICE_EPHEMERAL, name_label,
ephemeral_gb)
+ # Attach (optional) configdrive v2 disk
+ if configdrive.required_by(instance):
+ vm_utils.generate_configdrive(self._session, instance, vm_ref,
+ DEVICE_CONFIGDRIVE,
+ admin_password=admin_password,
+ files=files)
+
def _boot_new_instance(self, instance, vm_ref, injected_files,
admin_password):
"""Boot a new instance and configure it."""