virt: allow for direct mounting of LocalBlockImages
Block backed images, are already presented as block devices, so LocalBlockImages can be mounted directly instead of being linked with loopback or nbd to another block device. Change-Id: I5aae1e293832f7cff7bc00d793bcc86ededf5eec Closes-bug: #1287754
This commit is contained in:
parent
240df42859
commit
0e16afa588
|
@ -16,6 +16,10 @@ import mock
|
|||
|
||||
from nova import test
|
||||
from nova.virt.disk.mount import api
|
||||
from nova.virt.disk.mount import block
|
||||
from nova.virt.disk.mount import loop
|
||||
from nova.virt.disk.mount import nbd
|
||||
from nova.virt.image import model as imgmodel
|
||||
|
||||
|
||||
PARTITION = 77
|
||||
|
@ -114,3 +118,81 @@ class MountTestCase(test.NoDBTestCase):
|
|||
self.assertEqual(ORIG_DEVICE, mount.mapped_device)
|
||||
self.assertFalse(mount.automapped)
|
||||
self.assertTrue(mount.mapped)
|
||||
|
||||
def test_instance_for_format_raw(self):
|
||||
image = imgmodel.LocalFileImage("/some/file.raw",
|
||||
imgmodel.FORMAT_RAW)
|
||||
mount_dir = '/mount/dir'
|
||||
partition = -1
|
||||
inst = api.Mount.instance_for_format(image, mount_dir, partition)
|
||||
self.assertIsInstance(inst, loop.LoopMount)
|
||||
|
||||
def test_instance_for_format_qcow2(self):
|
||||
image = imgmodel.LocalFileImage("/some/file.qcows",
|
||||
imgmodel.FORMAT_QCOW2)
|
||||
mount_dir = '/mount/dir'
|
||||
partition = -1
|
||||
inst = api.Mount.instance_for_format(image, mount_dir, partition)
|
||||
self.assertIsInstance(inst, nbd.NbdMount)
|
||||
|
||||
def test_instance_for_format_block(self):
|
||||
image = imgmodel.LocalBlockImage(
|
||||
"/dev/mapper/instances--instance-0000001_disk",)
|
||||
mount_dir = '/mount/dir'
|
||||
partition = -1
|
||||
inst = api.Mount.instance_for_format(image, mount_dir, partition)
|
||||
self.assertIsInstance(inst, block.BlockMount)
|
||||
|
||||
def test_instance_for_device_loop(self):
|
||||
image = mock.MagicMock()
|
||||
mount_dir = '/mount/dir'
|
||||
partition = -1
|
||||
device = '/dev/loop0'
|
||||
inst = api.Mount.instance_for_device(image, mount_dir, partition,
|
||||
device)
|
||||
self.assertIsInstance(inst, loop.LoopMount)
|
||||
|
||||
def test_instance_for_device_loop_partition(self):
|
||||
image = mock.MagicMock()
|
||||
mount_dir = '/mount/dir'
|
||||
partition = 1
|
||||
device = '/dev/mapper/loop0p1'
|
||||
inst = api.Mount.instance_for_device(image, mount_dir, partition,
|
||||
device)
|
||||
self.assertIsInstance(inst, loop.LoopMount)
|
||||
|
||||
def test_instance_for_device_nbd(self):
|
||||
image = mock.MagicMock()
|
||||
mount_dir = '/mount/dir'
|
||||
partition = -1
|
||||
device = '/dev/nbd0'
|
||||
inst = api.Mount.instance_for_device(image, mount_dir, partition,
|
||||
device)
|
||||
self.assertIsInstance(inst, nbd.NbdMount)
|
||||
|
||||
def test_instance_for_device_nbd_partition(self):
|
||||
image = mock.MagicMock()
|
||||
mount_dir = '/mount/dir'
|
||||
partition = 1
|
||||
device = '/dev/mapper/nbd0p1'
|
||||
inst = api.Mount.instance_for_device(image, mount_dir, partition,
|
||||
device)
|
||||
self.assertIsInstance(inst, nbd.NbdMount)
|
||||
|
||||
def test_instance_for_device_block(self):
|
||||
image = mock.MagicMock()
|
||||
mount_dir = '/mount/dir'
|
||||
partition = -1
|
||||
device = '/dev/mapper/instances--instance-0000001_disk'
|
||||
inst = api.Mount.instance_for_device(image, mount_dir, partition,
|
||||
device)
|
||||
self.assertIsInstance(inst, block.BlockMount)
|
||||
|
||||
def test_instance_for_device_block_partiton(self,):
|
||||
image = mock.MagicMock()
|
||||
mount_dir = '/mount/dir'
|
||||
partition = 1
|
||||
device = '/dev/mapper/instances--instance-0000001_diskp1'
|
||||
inst = api.Mount.instance_for_device(image, mount_dir, partition,
|
||||
device)
|
||||
self.assertIsInstance(inst, block.BlockMount)
|
||||
|
|
|
@ -0,0 +1,46 @@
|
|||
# Copyright 2015 Rackspace Hosting, Inc.
|
||||
# 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 fixtures
|
||||
|
||||
from nova import test
|
||||
from nova.virt.disk.mount import block
|
||||
from nova.virt.image import model as imgmodel
|
||||
|
||||
|
||||
class LoopTestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(LoopTestCase, self).setUp()
|
||||
|
||||
device_path = '/dev/mapper/instances--instance-0000001_disk'
|
||||
self.image = imgmodel.LocalBlockImage(device_path)
|
||||
|
||||
def test_get_dev(self):
|
||||
tempdir = self.useFixture(fixtures.TempDir()).path
|
||||
b = block.BlockMount(self.image, tempdir)
|
||||
|
||||
self.assertTrue(b.get_dev())
|
||||
self.assertTrue(b.linked)
|
||||
self.assertEqual(self.image.path, b.device)
|
||||
|
||||
def test_unget_dev(self):
|
||||
tempdir = self.useFixture(fixtures.TempDir()).path
|
||||
b = block.BlockMount(self.image, tempdir)
|
||||
|
||||
b.unget_dev()
|
||||
|
||||
self.assertIsNone(b.device)
|
||||
self.assertFalse(b.linked)
|
|
@ -254,6 +254,11 @@ class TestVirtDisk(test.NoDBTestCase):
|
|||
('qemu-nbd', '-d', '/dev/nbd15'),
|
||||
]
|
||||
|
||||
# NOTE(thomasem): Not adding any commands in this case, because we're
|
||||
# not expecting an additional umount for LocalBlockImages. This is to
|
||||
# assert that no additional commands are run in this case.
|
||||
disk_api.teardown_container('/dev/volume-group/uuid_disk')
|
||||
|
||||
self.assertEqual(self.executes, expected_commands)
|
||||
|
||||
def test_lxc_teardown_container_with_namespace_cleaned(self):
|
||||
|
|
|
@ -472,10 +472,13 @@ def teardown_container(container_dir, container_root_device=None):
|
|||
LOG.debug("Release loop device %s", container_root_device)
|
||||
utils.execute('losetup', '--detach', container_root_device,
|
||||
run_as_root=True, attempts=3)
|
||||
else:
|
||||
elif 'nbd' in container_root_device:
|
||||
LOG.debug('Release nbd device %s', container_root_device)
|
||||
utils.execute('qemu-nbd', '-d', container_root_device,
|
||||
run_as_root=True)
|
||||
else:
|
||||
LOG.debug('No release necessary for block device %s' %
|
||||
container_root_device)
|
||||
except Exception:
|
||||
LOG.exception(_LE('Failed to teardown container filesystem'))
|
||||
|
||||
|
|
|
@ -62,12 +62,13 @@ class Mount(object):
|
|||
return importutils.import_object(
|
||||
"nova.virt.disk.mount.nbd.NbdMount",
|
||||
image, mountdir, partition)
|
||||
elif isinstance(image, imgmodel.LocalBlockImage):
|
||||
LOG.debug("Using BlockMount")
|
||||
return importutils.import_object(
|
||||
"nova.virt.disk.mount.block.BlockMount",
|
||||
image, mountdir, partition)
|
||||
else:
|
||||
# TODO(berrange) we could mount images of
|
||||
# type LocalBlockImage directly without
|
||||
# involving loop or nbd devices
|
||||
#
|
||||
# We could also mount RBDImage directly
|
||||
# TODO(berrange) We could mount RBDImage directly
|
||||
# using kernel RBD block dev support.
|
||||
#
|
||||
# This is left as an enhancement for future
|
||||
|
@ -98,11 +99,16 @@ class Mount(object):
|
|||
return importutils.import_object(
|
||||
"nova.virt.disk.mount.loop.LoopMount",
|
||||
image, mountdir, partition, device)
|
||||
else:
|
||||
elif "nbd" in device:
|
||||
LOG.debug("Using NbdMount")
|
||||
return importutils.import_object(
|
||||
"nova.virt.disk.mount.nbd.NbdMount",
|
||||
image, mountdir, partition, device)
|
||||
else:
|
||||
LOG.debug("Using BlockMount")
|
||||
return importutils.import_object(
|
||||
"nova.virt.disk.mount.block.BlockMount",
|
||||
image, mountdir, partition, device)
|
||||
|
||||
def __init__(self, image, mount_dir, partition=None, device=None):
|
||||
"""Create a new Mount instance
|
||||
|
@ -139,8 +145,9 @@ class Mount(object):
|
|||
if os.path.isabs(device) and os.path.exists(device):
|
||||
if device.startswith('/dev/mapper/'):
|
||||
device = os.path.basename(device)
|
||||
device, self.partition = device.rsplit('p', 1)
|
||||
self.device = os.path.join('/dev', device)
|
||||
if 'p' in device:
|
||||
device, self.partition = device.rsplit('p', 1)
|
||||
self.device = os.path.join('/dev', device)
|
||||
|
||||
def get_dev(self):
|
||||
"""Make the image available as a block device in the file system."""
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
# Copyright 2015 Rackspace Hosting, 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 block device based images directly."""
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
from nova.virt.disk.mount import api
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class BlockMount(api.Mount):
|
||||
"""Block device backed images do not need to be linked because
|
||||
they are already exposed as block devices and can be mounted
|
||||
directly.
|
||||
"""
|
||||
mode = 'block'
|
||||
|
||||
def get_dev(self):
|
||||
self.device = self.image.path
|
||||
self.linked = True
|
||||
return True
|
||||
|
||||
def unget_dev(self):
|
||||
self.linked = False
|
||||
self.device = None
|
Loading…
Reference in New Issue