virt: convert VFS API to use nova.virt.image.model

Convert the nova.virt.disk.vfs classes to use the
nova.virt.image.model classes instead of manually
passing a filename and image format as parameters.
This enables the libguestfs impl to now support the
configuration of RBD images.

Related-Bug: #1257674
Change-Id: I41d6af52ffed924d35ac255625488f40f7989a0a
This commit is contained in:
Daniel P. Berrange 2014-10-29 18:54:44 +00:00
parent 0ea510f556
commit 356e0651a6
10 changed files with 196 additions and 115 deletions

View File

@ -195,7 +195,8 @@ def _get_virt_name(regex, data):
driver = m.group(1)
# Ignore things we mis-detect as virt drivers in the regex
if driver in ["test_virt_drivers", "driver", "firewall",
"disk", "api", "imagecache", "cpu", "hardware"]:
"disk", "api", "imagecache", "cpu", "hardware",
"image"]:
return None
return driver

View File

@ -22,6 +22,7 @@ from nova import test
from nova.tests.unit.virt.disk.vfs import fakeguestfs
from nova.virt.disk import api as diskapi
from nova.virt.disk.vfs import guestfs as vfsguestfs
from nova.virt.image import model as imgmodel
class VirtDiskTest(test.NoDBTestCase):
@ -30,6 +31,8 @@ class VirtDiskTest(test.NoDBTestCase):
self.useFixture(
fixtures.MonkeyPatch('nova.virt.disk.vfs.guestfs.guestfs',
fakeguestfs))
self.file = imgmodel.LocalFileImage("/some/file",
imgmodel.FORMAT_QCOW2)
def test_inject_data(self):
self.assertTrue(diskapi.inject_data("/some/file", use_cow=True))
@ -54,7 +57,7 @@ class VirtDiskTest(test.NoDBTestCase):
def test_inject_data_key(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
vfs = vfsguestfs.VFSGuestFS(self.file)
vfs.setup()
diskapi._inject_key_into_fs("mysshkey", vfs)
@ -75,7 +78,7 @@ class VirtDiskTest(test.NoDBTestCase):
def test_inject_data_key_with_selinux(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
vfs = vfsguestfs.VFSGuestFS(self.file)
vfs.setup()
vfs.make_path("etc/selinux")
@ -109,7 +112,7 @@ class VirtDiskTest(test.NoDBTestCase):
def test_inject_data_key_with_selinux_append_with_newline(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
vfs = vfsguestfs.VFSGuestFS(self.file)
vfs.setup()
vfs.replace_file("/etc/rc.d/rc.local", "#!/bin/sh\necho done")
@ -131,7 +134,7 @@ class VirtDiskTest(test.NoDBTestCase):
def test_inject_net(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
vfs = vfsguestfs.VFSGuestFS(self.file)
vfs.setup()
diskapi._inject_net_into_fs("mynetconfig", vfs)
@ -146,7 +149,7 @@ class VirtDiskTest(test.NoDBTestCase):
vfs.teardown()
def test_inject_metadata(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
vfs = vfsguestfs.VFSGuestFS(self.file)
vfs.setup()
metadata = {"foo": "bar", "eek": "wizz"}
metadata = OrderedDict(sorted(metadata.items()))
@ -163,7 +166,7 @@ class VirtDiskTest(test.NoDBTestCase):
vfs.teardown()
def test_inject_admin_password(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
vfs = vfsguestfs.VFSGuestFS(self.file)
vfs.setup()
def fake_salt():
@ -219,7 +222,7 @@ class VirtDiskTest(test.NoDBTestCase):
vfs.teardown()
def test_inject_files_into_fs(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
vfs = vfsguestfs.VFSGuestFS(self.file)
vfs.setup()
diskapi._inject_files_into_fs([("/path/to/not/exists/file",
@ -244,7 +247,7 @@ class VirtDiskTest(test.NoDBTestCase):
vfs.teardown()
def test_inject_files_into_fs_dir_exists(self):
vfs = vfsguestfs.VFSGuestFS("/some/file", "qcow2")
vfs = vfsguestfs.VFSGuestFS(self.file)
vfs.setup()
called = {'make_path': False}

View File

@ -61,7 +61,7 @@ class GuestFS(object):
if file == "/some/fail/file":
raise RuntimeError("%s: No such file or directory", file)
self.drives.append((file, kwargs['format']))
self.drives.append((file, kwargs))
def add_drive(self, file, format=None, *args, **kwargs):
self.add_drive_opts(file, format=None, *args, **kwargs)

View File

@ -19,6 +19,7 @@ from nova import exception
from nova import test
from nova.tests.unit.virt.disk.vfs import fakeguestfs
from nova.virt.disk.vfs import guestfs as vfsimpl
from nova.virt.image import model as imgmodel
class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
@ -28,15 +29,25 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
fixtures.MonkeyPatch('nova.virt.disk.vfs.guestfs.guestfs',
fakeguestfs))
def _do_test_appliance_setup_inspect(self, forcetcg):
self.qcowfile = imgmodel.LocalFileImage("/dummy.qcow2",
imgmodel.FORMAT_QCOW2)
self.rawfile = imgmodel.LocalFileImage("/dummy.img",
imgmodel.FORMAT_RAW)
self.lvmfile = imgmodel.LocalBlockImage("/dev/volgroup/myvol")
self.rbdfile = imgmodel.RBDImage("myvol", "mypool",
"cthulu",
"arrrrrgh",
["server1:123", "server2:123"])
def _do_test_appliance_setup_inspect(self, image, drives, forcetcg):
if forcetcg:
vfsimpl.force_tcg()
else:
vfsimpl.force_tcg(False)
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2",
imgfmt="qcow2",
partition=-1)
vfs = vfsimpl.VFSGuestFS(
image,
partition=-1)
vfs.setup()
if forcetcg:
@ -46,6 +57,8 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
self.assertIsNone(vfs.handle.backend_settings)
self.assertTrue(vfs.handle.running)
self.assertEqual(drives,
vfs.handle.drives)
self.assertEqual(3, len(vfs.handle.mounts))
self.assertEqual("/dev/mapper/guestvgf-lv_root",
vfs.handle.mounts[0][1])
@ -66,14 +79,32 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
self.assertEqual(0, len(handle.mounts))
def test_appliance_setup_inspect_auto(self):
self._do_test_appliance_setup_inspect(False)
drives = [("/dummy.qcow2", {"format": "qcow2"})]
self._do_test_appliance_setup_inspect(self.qcowfile, drives, False)
def test_appliance_setup_inspect_tcg(self):
self._do_test_appliance_setup_inspect(True)
drives = [("/dummy.qcow2", {"format": "qcow2"})]
self._do_test_appliance_setup_inspect(self.qcowfile, drives, True)
def test_appliance_setup_inspect_raw(self):
drives = [("/dummy.img", {"format": "raw"})]
self._do_test_appliance_setup_inspect(self.rawfile, drives, True)
def test_appliance_setup_inspect_lvm(self):
drives = [("/dev/volgroup/myvol", {"format": "raw"})]
self._do_test_appliance_setup_inspect(self.lvmfile, drives, True)
def test_appliance_setup_inspect_rbd(self):
drives = [("mypool/myvol", {"format": "raw",
"protocol": "rbd",
"username": "cthulu",
"secret": "arrrrrgh",
"server": ["server1:123",
"server2:123"]})]
self._do_test_appliance_setup_inspect(self.rbdfile, drives, True)
def test_appliance_setup_inspect_no_root_raises(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2",
imgfmt="qcow2",
vfs = vfsimpl.VFSGuestFS(self.qcowfile,
partition=-1)
# call setup to init the handle so we can stub it
vfs.setup()
@ -87,8 +118,7 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
self.assertRaises(exception.NovaException, vfs.setup_os_inspect)
def test_appliance_setup_inspect_multi_boots_raises(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2",
imgfmt="qcow2",
vfs = vfsimpl.VFSGuestFS(self.qcowfile,
partition=-1)
# call setup to init the handle so we can stub it
vfs.setup()
@ -102,8 +132,7 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
self.assertRaises(exception.NovaException, vfs.setup_os_inspect)
def test_appliance_setup_static_nopart(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2",
imgfmt="qcow2",
vfs = vfsimpl.VFSGuestFS(self.qcowfile,
partition=None)
vfs.setup()
@ -122,8 +151,7 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
self.assertEqual(0, len(handle.mounts))
def test_appliance_setup_static_part(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2",
imgfmt="qcow2",
vfs = vfsimpl.VFSGuestFS(self.qcowfile,
partition=2)
vfs.setup()
@ -142,7 +170,7 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
self.assertEqual(0, len(handle.mounts))
def test_makepath(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
vfs.make_path("/some/dir")
vfs.make_path("/other/dir")
@ -155,7 +183,7 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
vfs.teardown()
def test_append_file(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
vfs.append_file("/some/file", " Goodbye")
@ -166,7 +194,7 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
vfs.teardown()
def test_replace_file(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
vfs.replace_file("/some/file", "Goodbye")
@ -177,14 +205,14 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
vfs.teardown()
def test_read_file(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
self.assertEqual("Hello World", vfs.read_file("/some/file"))
vfs.teardown()
def test_has_file(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
vfs.read_file("/some/file")
@ -194,7 +222,7 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
vfs.teardown()
def test_set_permissions(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
vfs.read_file("/some/file")
@ -206,7 +234,7 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
vfs.teardown()
def test_set_ownership(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
vfs.read_file("/some/file")
@ -228,29 +256,29 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
vfs.teardown()
def test_close_on_error(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
self.assertFalse(vfs.handle.kwargs['close_on_exit'])
vfs.teardown()
self.stubs.Set(fakeguestfs.GuestFS, 'SUPPORT_CLOSE_ON_EXIT', False)
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
self.assertNotIn('close_on_exit', vfs.handle.kwargs)
vfs.teardown()
def test_python_return_dict(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
self.assertFalse(vfs.handle.kwargs['python_return_dict'])
vfs.teardown()
self.stubs.Set(fakeguestfs.GuestFS, 'SUPPORT_RETURN_DICT', False)
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
self.assertNotIn('python_return_dict', vfs.handle.kwargs)
vfs.teardown()
def test_setup_debug_disable(self):
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
self.assertFalse(vfs.handle.trace_enabled)
self.assertFalse(vfs.handle.verbose_enabled)
@ -258,14 +286,14 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
def test_setup_debug_enabled(self):
self.flags(debug=True, group='guestfs')
vfs = vfsimpl.VFSGuestFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
self.assertTrue(vfs.handle.trace_enabled)
self.assertTrue(vfs.handle.verbose_enabled)
self.assertIsNotNone(vfs.handle.event_callback)
def test_get_format_fs(self):
vfs = vfsimpl.VFSGuestFS("dummy.img")
vfs = vfsimpl.VFSGuestFS(self.rawfile)
vfs.setup()
self.assertIsNotNone(vfs.handle)
self.assertTrue('ext3', vfs.get_image_fs())
@ -273,12 +301,12 @@ class VirtDiskVFSGuestFSTest(test.NoDBTestCase):
@mock.patch.object(vfsimpl.VFSGuestFS, 'setup_os')
def test_setup_mount(self, setup_os):
vfs = vfsimpl.VFSGuestFS("img.qcow2", imgfmt='qcow2')
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup()
self.assertTrue(setup_os.called)
@mock.patch.object(vfsimpl.VFSGuestFS, 'setup_os')
def test_setup_mount_false(self, setup_os):
vfs = vfsimpl.VFSGuestFS("img.qcow2", imgfmt='qcow2')
vfs = vfsimpl.VFSGuestFS(self.qcowfile)
vfs.setup(mount=False)
self.assertFalse(setup_os.called)

View File

@ -22,6 +22,7 @@ from nova import exception
from nova import test
from nova.tests.unit import utils as tests_utils
import nova.utils
from nova.virt.disk.mount import nbd
from nova.virt.disk.vfs import localfs as vfsimpl
from nova.virt.image import model as imgmodel
@ -134,11 +135,13 @@ class VirtDiskVFSLocalFSTestPaths(test.NoDBTestCase):
return real_execute(*cmd_parts, **kwargs)
self.stubs.Set(processutils, 'execute', nonroot_execute)
self.rawfile = imgmodel.LocalFileImage("/dummy.img",
imgmodel.FORMAT_RAW)
def test_check_safe_path(self):
if not tests_utils.coreutils_readlink_available():
self.skipTest("coreutils readlink(1) unavailable")
vfs = vfsimpl.VFSLocalFS("dummy.img")
vfs = vfsimpl.VFSLocalFS(self.rawfile)
vfs.imgdir = "/foo"
ret = vfs._canonical_path('etc/something.conf')
self.assertEqual(ret, '/foo/etc/something.conf')
@ -146,7 +149,7 @@ class VirtDiskVFSLocalFSTestPaths(test.NoDBTestCase):
def test_check_unsafe_path(self):
if not tests_utils.coreutils_readlink_available():
self.skipTest("coreutils readlink(1) unavailable")
vfs = vfsimpl.VFSLocalFS("dummy.img")
vfs = vfsimpl.VFSLocalFS(self.rawfile)
vfs.imgdir = "/foo"
self.assertRaises(exception.Invalid,
vfs._canonical_path,
@ -154,13 +157,21 @@ class VirtDiskVFSLocalFSTestPaths(test.NoDBTestCase):
class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
def setUp(self):
super(VirtDiskVFSLocalFSTest, self).setUp()
self.qcowfile = imgmodel.LocalFileImage("/dummy.qcow2",
imgmodel.FORMAT_QCOW2)
self.rawfile = imgmodel.LocalFileImage("/dummy.img",
imgmodel.FORMAT_RAW)
def test_makepath(self):
global dirs, commands
dirs = []
commands = []
self.stubs.Set(processutils, 'execute', fake_execute)
vfs = vfsimpl.VFSLocalFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
vfs.imgdir = "/scratch/dir"
vfs.make_path("/some/dir")
vfs.make_path("/other/dir")
@ -193,7 +204,7 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
commands = []
self.stubs.Set(processutils, 'execute', fake_execute)
vfs = vfsimpl.VFSLocalFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
vfs.imgdir = "/scratch/dir"
vfs.append_file("/some/file", " Goodbye")
@ -219,7 +230,7 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
commands = []
self.stubs.Set(processutils, 'execute', fake_execute)
vfs = vfsimpl.VFSLocalFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
vfs.imgdir = "/scratch/dir"
vfs.replace_file("/some/file", "Goodbye")
@ -244,7 +255,7 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
commands = []
self.stubs.Set(processutils, 'execute', fake_execute)
vfs = vfsimpl.VFSLocalFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
vfs.imgdir = "/scratch/dir"
self.assertEqual(vfs.read_file("/some/file"), "Hello World")
@ -264,7 +275,7 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
commands = []
self.stubs.Set(processutils, 'execute', fake_execute)
vfs = vfsimpl.VFSLocalFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
vfs.imgdir = "/scratch/dir"
vfs.read_file("/some/file")
@ -304,7 +315,7 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
files = {}
self.stubs.Set(processutils, 'execute', fake_execute)
vfs = vfsimpl.VFSLocalFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
vfs.imgdir = "/scratch/dir"
vfs.read_file("/some/file")
@ -335,7 +346,7 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
files = {}
self.stubs.Set(processutils, 'execute', fake_execute)
vfs = vfsimpl.VFSLocalFS(imgfile="/dummy.qcow2", imgfmt="qcow2")
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
vfs.imgdir = "/scratch/dir"
vfs.read_file("/some/file")
@ -390,7 +401,7 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
@mock.patch.object(nova.utils, 'execute')
def test_get_format_fs(self, execute):
vfs = vfsimpl.VFSLocalFS("dummy.img")
vfs = vfsimpl.VFSLocalFS(self.rawfile)
vfs.setup = mock.MagicMock()
vfs.teardown = mock.MagicMock()
@ -421,9 +432,9 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
check_exit_code=[0, 2])
@mock.patch.object(tempfile, 'mkdtemp')
@mock.patch.object(nova.virt.disk.mount.nbd, 'NbdMount')
@mock.patch.object(nbd, 'NbdMount')
def test_setup_mount(self, NbdMount, mkdtemp):
vfs = vfsimpl.VFSLocalFS("img.qcow2", imgfmt='qcow2')
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
mounter = mock.MagicMock()
mkdtemp.return_value = 'tmp/'
@ -432,16 +443,13 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
vfs.setup()
self.assertTrue(mkdtemp.called)
NbdMount.assert_called_once_with(
imgmodel.LocalFileImage('img.qcow2',
imgmodel.FORMAT_QCOW2),
'tmp/', None)
NbdMount.assert_called_once_with(self.qcowfile, "tmp/", None)
mounter.do_mount.assert_called_once_with()
@mock.patch.object(tempfile, 'mkdtemp')
@mock.patch.object(nova.virt.disk.mount.nbd, 'NbdMount')
@mock.patch.object(nbd, 'NbdMount')
def test_setup_mount_false(self, NbdMount, mkdtemp):
vfs = vfsimpl.VFSLocalFS("img.qcow2", imgfmt='qcow2')
vfs = vfsimpl.VFSLocalFS(self.qcowfile)
mounter = mock.MagicMock()
mkdtemp.return_value = 'tmp/'
@ -450,8 +458,5 @@ class VirtDiskVFSLocalFSTest(test.NoDBTestCase):
vfs.setup(mount=False)
self.assertTrue(mkdtemp.called)
NbdMount.assert_called_once_with(
imgmodel.LocalFileImage('img.qcow2',
imgmodel.FORMAT_QCOW2),
'tmp/', None)
NbdMount.assert_called_once_with(self.qcowfile, "tmp/", None)
self.assertFalse(mounter.do_mount.called)

View File

@ -240,7 +240,9 @@ def is_image_extendable(image, use_cow=False):
if use_cow:
fs = None
try:
fs = vfs.VFS.instance_for_image(image, 'qcow2', None)
fs = vfs.VFS.instance_for_image(
imgmodel.LocalFileImage(image, imgmodel.FORMAT_QCOW2),
None)
fs.setup(mount=False)
if fs.get_image_fs() in SUPPORTED_FS_TO_EXTEND:
return True
@ -384,6 +386,16 @@ def inject_data(image, key=None, net=None, metadata=None, admin_password=None,
files=None, partition=None, use_cow=False, mandatory=()):
"""Inject the specified items into a disk image.
:param image: the local file path
:param key: the SSH public key to inject
:param net: the network configuration to inject
:param metadata: the user metadata to inject
:param admin_password: the root password to set
:param files: the files to copy into the image
:param partition: the partition number to access
:param use_cow: whether the image is in qcow2 format
:param mandatory: the list of parameters which must not fail to inject
If an item name is not specified in the MANDATORY iterable, then a warning
is logged on failure to inject that item, rather than raising an exception.
@ -400,11 +412,12 @@ def inject_data(image, key=None, net=None, metadata=None, admin_password=None,
"files=%(files)s partition=%(partition)s use_cow=%(use_cow)s",
{'image': image, 'key': key, 'net': net, 'metadata': metadata,
'files': files, 'partition': partition, 'use_cow': use_cow})
fmt = "raw"
fmt = imgmodel.FORMAT_RAW
if use_cow:
fmt = "qcow2"
fmt = imgmodel.FORMAT_QCOW2
try:
fs = vfs.VFS.instance_for_image(image, fmt, partition)
fs = vfs.VFS.instance_for_image(
imgmodel.LocalFileImage(image, fmt), partition)
fs.setup()
except Exception as e:
# If a mandatory item is passed to this function,

View File

@ -39,18 +39,23 @@ class VFS(object):
guestfs_ready = False
@staticmethod
def instance_for_image(imgfile, imgfmt, partition):
LOG.debug("Instance for image imgfile=%(imgfile)s "
"imgfmt=%(imgfmt)s partition=%(partition)s",
{'imgfile': imgfile, 'imgfmt': imgfmt,
'partition': partition})
def instance_for_image(image, partition):
"""Get a VFS instance for the image
:param image: instance of nova.virt.image.model.Image
:param partition: the partition number to access
"""
LOG.debug("Instance for image image=%(image)s "
"partition=%(partition)s",
{'image': image, 'partition': partition})
vfs = None
try:
LOG.debug("Using primary VFSGuestFS")
vfs = importutils.import_object(
"nova.virt.disk.vfs.guestfs.VFSGuestFS",
imgfile, imgfmt, partition)
image, partition)
if not VFS.guestfs_ready:
# Inspect for capabilities and keep
# track of the result only if succeeded.
@ -69,11 +74,16 @@ class VFS(object):
return importutils.import_object(
"nova.virt.disk.vfs.localfs.VFSLocalFS",
imgfile, imgfmt, partition)
image, partition)
def __init__(self, imgfile, imgfmt, partition):
self.imgfile = imgfile
self.imgfmt = imgfmt
def __init__(self, image, partition):
"""Create a new local VFS instance
:param image: instance of nova.virt.image.model.Image
:param partition: the partition number to access
"""
self.image = image
self.partition = partition
def setup(self, mount=True):

View File

@ -22,6 +22,7 @@ from nova import exception
from nova.i18n import _
from nova.i18n import _LW
from nova.virt.disk.vfs import api as vfs
from nova.virt.image import model as imgmodel
LOG = logging.getLogger(__name__)
@ -57,8 +58,14 @@ class VFSGuestFS(vfs.VFS):
the host filesystem, thus avoiding any potential for symlink
attacks from the guest filesystem.
"""
def __init__(self, imgfile, imgfmt='raw', partition=None):
super(VFSGuestFS, self).__init__(imgfile, imgfmt, partition)
def __init__(self, image, partition=None):
"""Create a new local VFS instance
:param image: instance of nova.virt.image.model.Image
:param partition: the partition number of access
"""
super(VFSGuestFS, self).__init__(image, partition)
global guestfs
if guestfs is None:
@ -111,8 +118,8 @@ class VFSGuestFS(vfs.VFS):
self.setup_os_static()
def setup_os_static(self):
LOG.debug("Mount guest OS image %(imgfile)s partition %(part)s",
{'imgfile': self.imgfile, 'part': str(self.partition)})
LOG.debug("Mount guest OS image %(image)s partition %(part)s",
{'image': self.image, 'part': str(self.partition)})
if self.partition:
self.handle.mount_options("", "/dev/sda%d" % self.partition, "/")
@ -120,18 +127,18 @@ class VFSGuestFS(vfs.VFS):
self.handle.mount_options("", "/dev/sda", "/")
def setup_os_inspect(self):
LOG.debug("Inspecting guest OS image %s", self.imgfile)
LOG.debug("Inspecting guest OS image %s", self.image)
roots = self.handle.inspect_os()
if len(roots) == 0:
raise exception.NovaException(_("No operating system found in %s")
% self.imgfile)
% self.image)
if len(roots) != 1:
LOG.debug("Multi-boot OS %(roots)s", {'roots': str(roots)})
raise exception.NovaException(
_("Multi-boot operating system found in %s") %
self.imgfile)
self.image)
self.setup_os_root(roots[0])
@ -141,8 +148,8 @@ class VFSGuestFS(vfs.VFS):
if len(mounts) == 0:
raise exception.NovaException(
_("No mount points found in %(root)s of %(imgfile)s") %
{'root': root, 'imgfile': self.imgfile})
_("No mount points found in %(root)s of %(image)s") %
{'root': root, 'image': self.image})
# the root directory must be mounted first
mounts.sort(key=lambda mount: mount[0])
@ -156,8 +163,8 @@ class VFSGuestFS(vfs.VFS):
root_mounted = True
except RuntimeError as e:
msg = _("Error mounting %(device)s to %(dir)s in image"
" %(imgfile)s with libguestfs (%(e)s)") % \
{'imgfile': self.imgfile, 'device': mount[1],
" %(image)s with libguestfs (%(e)s)") % \
{'image': self.image, 'device': mount[1],
'dir': mount[0], 'e': e}
if root_mounted:
LOG.debug(msg)
@ -165,8 +172,8 @@ class VFSGuestFS(vfs.VFS):
raise exception.NovaException(msg)
def setup(self, mount=True):
LOG.debug("Setting up appliance for %(imgfile)s %(imgfmt)s",
{'imgfile': self.imgfile, 'imgfmt': self.imgfmt})
LOG.debug("Setting up appliance for %(image)s",
{'image': self.image})
try:
self.handle = tpool.Proxy(
guestfs.GuestFS(python_return_dict=False,
@ -195,7 +202,21 @@ class VFSGuestFS(vfs.VFS):
pass
try:
self.handle.add_drive_opts(self.imgfile, format=self.imgfmt)
if isinstance(self.image, imgmodel.LocalImage):
self.handle.add_drive_opts(self.image.path,
format=self.image.format)
elif isinstance(self.image, imgmodel.RBDImage):
self.handle.add_drive_opts("%s/%s" % (self.image.pool,
self.image.name),
protocol="rbd",
format=imgmodel.FORMAT_RAW,
server=self.image.servers,
username=self.image.user,
secret=self.image.password)
else:
raise exception.UnsupportedImageModel(
self.image.__class__.__name__)
self.handle.launch()
if mount:
@ -208,8 +229,8 @@ class VFSGuestFS(vfs.VFS):
# close() is not enough
self.teardown()
raise exception.NovaException(
_("Error mounting %(imgfile)s with libguestfs (%(e)s)") %
{'imgfile': self.imgfile, 'e': e})
_("Error mounting %(image)s with libguestfs (%(e)s)") %
{'image': self.image, 'e': e})
except Exception:
# explicitly teardown instead of implicit close()
# to prevent orphaned VMs in cases when an implicit

View File

@ -21,10 +21,8 @@ from oslo_utils import excutils
from nova import exception
from nova.i18n import _
from nova import utils
from nova.virt.disk.mount import loop
from nova.virt.disk.mount import nbd
from nova.virt.disk.mount import api as mount_api
from nova.virt.disk.vfs import api as vfs
from nova.virt.image import model as imgmodel
LOG = logging.getLogger(__name__)
@ -55,8 +53,15 @@ class VFSLocalFS(vfs.VFS):
raw, it will use the loopback mount impl, otherwise it will
use the qemu-nbd impl.
"""
def __init__(self, imgfile, imgfmt="raw", partition=None, imgdir=None):
super(VFSLocalFS, self).__init__(imgfile, imgfmt, partition)
def __init__(self, image, partition=None, imgdir=None):
"""Create a new local VFS instance
:param image: instance of nova.virt.image.model.Image
:param partition: the partition number of access
:param imgdir: the directory to mount the image at
"""
super(VFSLocalFS, self).__init__(image, partition)
self.imgdir = imgdir
self.mount = None
@ -64,20 +69,9 @@ class VFSLocalFS(vfs.VFS):
def setup(self, mount=True):
self.imgdir = tempfile.mkdtemp(prefix="openstack-vfs-localfs")
try:
if self.imgfmt == "raw":
LOG.debug("Using LoopMount")
mnt = loop.LoopMount(
imgmodel.LocalFileImage(self.imgfile,
imgmodel.FORMAT_RAW),
self.imgdir,
self.partition)
else:
LOG.debug("Using NbdMount")
mnt = nbd.NbdMount(
imgmodel.LocalFileImage(self.imgfile,
imgmodel.FORMAT_QCOW2),
self.imgdir,
self.partition)
mnt = mount_api.Mount.instance_for_format(self.image,
self.imgdir,
self.partition)
if mount:
if not mnt.do_mount():
raise exception.NovaException(mnt.error)

View File

@ -54,11 +54,11 @@ from nova.virt import diagnostics
from nova.virt.disk import api as disk
from nova.virt.disk.vfs import localfs as vfsimpl
from nova.virt import hardware
from nova.virt.image import model as imgmodel
from nova.virt import netutils
from nova.virt.xenapi import agent
from nova.virt.xenapi.image import utils as image_utils
LOG = logging.getLogger(__name__)
xenapi_vm_utils_opts = [
@ -2462,9 +2462,15 @@ def _mounted_processing(device, key, net, metadata):
try:
# This try block ensures that the umount occurs
if not agent.find_guest_agent(tmpdir):
vfs = vfsimpl.VFSLocalFS(imgfile=None,
imgfmt=None,
imgdir=tmpdir)
# TODO(berrange) passing in a None filename is
# rather dubious. We shouldn't be re-implementing
# the mount/unmount logic here either, when the
# VFSLocalFS impl has direct support for mount
# and unmount handling if it were passed a
# non-None filename
vfs = vfsimpl.VFSLocalFS(
imgmodel.LocalFileImage(None, imgmodel.FORMAT_RAW),
imgdir=tmpdir)
LOG.info(_LI('Manipulating interface files directly'))
# for xenapi, we don't 'inject' admin_password here,
# it's handled at instance startup time, nor do we