diff options
| -rwxr-xr-x | nova/rootwrap/compute.py | 8 | ||||
| -rw-r--r-- | nova/virt/disk/api.py | 5 | ||||
| -rw-r--r-- | nova/virt/disk/guestfs.py | 88 |
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 |
