Bug#898257 support handling images with libguestfs
http://libguestfs.org/ provides both utilities and libraries to manipulate image files containing various operating systems. It supports various image file formats and so will expand the formats and guest types supported by openstack. It does have extra overhead in that it starts a VM to access the image. This has both advantages and disadvantages. Also qemu-nbd is not supported on some systems like RHEL 6. * nova/virt/disk/api.py (img_handlers): Add guestfs to the default list of access methods to try, to act as a fallback. * nova/virt/disk/guestfs.py: A new plugin class to provide support for libguestfs mounting. Note we use the guestmount utility, as a non root user, so the user will need the ability to use fusermount, which is often provided by being a member of the 'fuser' group. In future we might use the guestfs python module to give greater granularity of control over the image. Change-Id: I2e22c9d149fff7a73cd8cebaa280d68d3fb9096c
This commit is contained in:
parent
5335b4ab0e
commit
dd56fd39d5
@ -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
|
||||
|
@ -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))
|
||||
|
88
nova/virt/disk/guestfs.py
Normal file
88
nova/virt/disk/guestfs.py
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user