summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rwxr-xr-xnova/rootwrap/compute.py8
-rw-r--r--nova/virt/disk/api.py5
-rw-r--r--nova/virt/disk/guestfs.py88
3 files changed, 99 insertions, 2 deletions
diff --git a/nova/rootwrap/compute.py b/nova/rootwrap/compute.py
index cd971e0d1..cee4cca2c 100755
--- a/nova/rootwrap/compute.py
+++ b/nova/rootwrap/compute.py
@@ -44,6 +44,14 @@ filters = [
# nova/virt/disk/loop.py: 'losetup', '--detach', device
CommandFilter("/sbin/losetup", "root"),
+ # nova/virt/disk/guestfs.py: 'guestmount', '--rw', '-a', image, '-i'
+ # nova/virt/disk/guestfs.py: 'guestmount', '--rw', '-a', image, '-m' dev
+ Commandfilter("/usr/bin/guestmount", "root"),
+
+ # nova/virt/disk/guestfs.py: 'fusermount', 'u', mount_dir
+ Commandfilter("/bin/fusermount", "root"),
+ Commandfilter("/usr/bin/fusermount", "root"),
+
# nova/virt/disk/api.py: 'tee', metadata_path
# nova/virt/disk/api.py: 'tee', '-a', keyfile
# nova/virt/disk/api.py: 'tee', netfile
diff --git a/nova/virt/disk/api.py b/nova/virt/disk/api.py
index 12e66cf0d..47d56c954 100644
--- a/nova/virt/disk/api.py
+++ b/nova/virt/disk/api.py
@@ -33,6 +33,7 @@ from nova import exception
from nova import flags
from nova import log as logging
from nova import utils
+from nova.virt.disk import guestfs
from nova.virt.disk import loop
from nova.virt.disk import nbd
@@ -43,7 +44,7 @@ flags.DEFINE_integer('minimum_root_size', 1024 * 1024 * 1024 * 10,
flags.DEFINE_string('injected_network_template',
utils.abspath('virt/interfaces.template'),
'Template file for injected network')
-flags.DEFINE_list('img_handlers', ['loop', 'nbd'],
+flags.DEFINE_list('img_handlers', ['loop', 'nbd', 'guestfs'],
'Order of methods used to mount disk images')
@@ -130,7 +131,7 @@ class _DiskImage(object):
@staticmethod
def _handler_class(mode):
"""Look up the appropriate class to use based on MODE."""
- for cls in (loop.Mount, nbd.Mount):
+ for cls in (loop.Mount, nbd.Mount, guestfs.Mount):
if cls.mode == mode:
return cls
raise exception.Error(_("unknown disk image handler: %s" % mode))
diff --git a/nova/virt/disk/guestfs.py b/nova/virt/disk/guestfs.py
new file mode 100644
index 000000000..6323dc8e3
--- /dev/null
+++ b/nova/virt/disk/guestfs.py
@@ -0,0 +1,88 @@
+# vim: tabstop=4 shiftwidth=4 softtabstop=4
+
+# Copyright 2011 Red Hat, Inc.
+#
+# 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.
+"""Support for mounting images with libguestfs"""
+
+import os
+
+from nova import utils
+from nova.virt.disk import mount
+
+
+class Mount(mount.Mount):
+ """libguestfs support for arbitrary images."""
+ mode = 'guestfs'
+
+ def map_dev(self):
+ self.mapped = True
+ return True
+
+ def unmap_dev(self):
+ self.mapped = False
+
+ def mnt_dev(self):
+ args = ('guestmount', '--rw', '-a', self.image)
+ if self.partition == -1:
+ args += ('-i',) # find the OS partition
+ elif self.partition:
+ args += ('-m', '/dev/sda%d' % self.partition)
+ else:
+ # We don't resort to -i for this case yet,
+ # as some older versions of libguestfs
+ # have problems identifying ttylinux images for example
+ args += ('-m', '/dev/sda')
+ args += (self.mount_dir,)
+ # root access should not required for guestfs (if the user
+ # has permissions to fusermount (by being part of the fuse
+ # group for example)). Also note the image and mount_dir
+ # have appropriate creditials at this point for read/write
+ # mounting by the nova user. However currently there are
+ # subsequent access issues by both the nova and root users
+ # if the nova user mounts the image, as detailed here:
+ # https://bugzilla.redhat.com/show_bug.cgi?id=765814
+ _out, err = utils.trycmd(*args, discard_warnings=True,
+ run_as_root=True)
+ if err:
+ self.error = _('Failed to mount filesystem: %s') % err
+ # Be defensive and ensure this is unmounted,
+ # as I'm not sure guestmount will never have
+ # mounted when it returns EXIT_FAILURE.
+ # This is required if discard_warnings=False above
+ utils.trycmd('fusermount', '-u', self.mount_dir, run_as_root=True)
+ return False
+
+ # More defensiveness as there are edge cases where
+ # guestmount can return success while not mounting
+ try:
+ if not os.listdir(self.mount_dir):
+ # Assume we've just got the original empty temp dir
+ err = _('unknown guestmount error')
+ self.error = _('Failed to mount filesystem: %s') % err
+ return False
+ except OSError:
+ # This is the usual path and means root has
+ # probably mounted fine
+ pass
+
+ self.mounted = True
+ return True
+
+ def unmnt_dev(self):
+ if not self.mounted:
+ return
+ # root users don't need a specific unmnt_dev()
+ # but ordinary users do
+ utils.execute('fusermount', '-u', self.mount_dir, run_as_root=True)
+ self.mounted = False