Merge "libvirt: virtuozzo instance resize support"
This commit is contained in:
commit
1cb9195aa8
@ -385,8 +385,10 @@ driver-impl-vmware=complete
|
|||||||
driver-impl-hyperv=complete
|
driver-impl-hyperv=complete
|
||||||
driver-impl-ironic=partial
|
driver-impl-ironic=partial
|
||||||
driver-notes-ironic=Only certain ironic drivers support this
|
driver-notes-ironic=Only certain ironic drivers support this
|
||||||
driver-impl-libvirt-vz-vm=missing
|
driver-impl-libvirt-vz-vm=complete
|
||||||
driver-impl-libvirt-vz-ct=missing
|
driver-notes-vz-vm=Resizing Virtuozzo instances implies guest filesystem resize also
|
||||||
|
driver-impl-libvirt-vz-ct=complete
|
||||||
|
driver-notes-vz-ct=Resizing Virtuozzo instances implies guest filesystem resize also
|
||||||
|
|
||||||
[operation.resume]
|
[operation.resume]
|
||||||
title=Restore instance
|
title=Restore instance
|
||||||
|
@ -246,7 +246,8 @@ cp: CommandFilter, cp, root
|
|||||||
sync: CommandFilter, sync, root
|
sync: CommandFilter, sync, root
|
||||||
|
|
||||||
# nova/virt/libvirt/imagebackend.py:
|
# nova/virt/libvirt/imagebackend.py:
|
||||||
ploop: CommandFilter, ploop, root
|
ploop: RegExpFilter, ploop, root, ploop, restore-descriptor, .*
|
||||||
|
prl_disk_tool: RegExpFilter, prl_disk_tool, root, prl_disk_tool, resize, --size, .*M$, --resize_partition, --hdd, .*
|
||||||
|
|
||||||
# nova/virt/libvirt/utils.py: 'xend', 'status'
|
# nova/virt/libvirt/utils.py: 'xend', 'status'
|
||||||
xend: CommandFilter, xend, root
|
xend: CommandFilter, xend, root
|
||||||
|
@ -18,6 +18,7 @@ import tempfile
|
|||||||
import fixtures
|
import fixtures
|
||||||
import mock
|
import mock
|
||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
|
from oslo_utils import units
|
||||||
|
|
||||||
from nova import test
|
from nova import test
|
||||||
from nova import utils
|
from nova import utils
|
||||||
@ -176,6 +177,24 @@ class APITestCase(test.NoDBTestCase):
|
|||||||
imgsize)
|
imgsize)
|
||||||
self.assertFalse(mock_extendable.called)
|
self.assertFalse(mock_extendable.called)
|
||||||
|
|
||||||
|
@mock.patch.object(api, 'can_resize_image', return_value=True)
|
||||||
|
@mock.patch.object(utils, 'execute')
|
||||||
|
def test_extend_ploop(self, mock_execute, mock_can_resize_image):
|
||||||
|
imgfile = tempfile.NamedTemporaryFile()
|
||||||
|
self.addCleanup(imgfile.close)
|
||||||
|
imgsize = 10 * units.Gi
|
||||||
|
imgsize_mb = str(imgsize // units.Mi) + 'M'
|
||||||
|
image = imgmodel.LocalFileImage(imgfile, imgmodel.FORMAT_PLOOP)
|
||||||
|
|
||||||
|
api.extend(image, imgsize)
|
||||||
|
mock_can_resize_image.assert_called_once_with(image.path,
|
||||||
|
imgsize)
|
||||||
|
mock_execute.assert_called_once_with('prl_disk_tool', 'resize',
|
||||||
|
'--size', imgsize_mb,
|
||||||
|
'--resize_partition',
|
||||||
|
'--hdd', imgfile,
|
||||||
|
run_as_root=True)
|
||||||
|
|
||||||
def test_extend_raw_success(self):
|
def test_extend_raw_success(self):
|
||||||
imgfile = tempfile.NamedTemporaryFile()
|
imgfile = tempfile.NamedTemporaryFile()
|
||||||
self.addCleanup(imgfile.close)
|
self.addCleanup(imgfile.close)
|
||||||
|
@ -1590,6 +1590,7 @@ class PloopTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
'__call__')
|
'__call__')
|
||||||
self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'copy_image')
|
self.mox.StubOutWithMock(imagebackend.libvirt_utils, 'copy_image')
|
||||||
self.mox.StubOutWithMock(self.utils, 'execute')
|
self.mox.StubOutWithMock(self.utils, 'execute')
|
||||||
|
self.mox.StubOutWithMock(imagebackend.disk, 'extend')
|
||||||
return fn
|
return fn
|
||||||
|
|
||||||
def test_cache(self):
|
def test_cache(self):
|
||||||
@ -1619,9 +1620,9 @@ class PloopTestCase(_ImageTestCase, test.NoDBTestCase):
|
|||||||
imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, img_path)
|
imagebackend.libvirt_utils.copy_image(self.TEMPLATE_PATH, img_path)
|
||||||
self.utils.execute("ploop", "restore-descriptor", "-f", "raw",
|
self.utils.execute("ploop", "restore-descriptor", "-f", "raw",
|
||||||
self.PATH, img_path)
|
self.PATH, img_path)
|
||||||
self.utils.execute("ploop", "grow", '-s', "2K",
|
image = imgmodel.LocalFileImage(self.PATH, imgmodel.FORMAT_PLOOP)
|
||||||
os.path.join(self.PATH, "DiskDescriptor.xml"),
|
imagebackend.disk.extend(image, 2048)
|
||||||
run_as_root=True)
|
|
||||||
self.mox.ReplayAll()
|
self.mox.ReplayAll()
|
||||||
|
|
||||||
image = self.image_class(self.INSTANCE, self.NAME)
|
image = self.image_class(self.INSTANCE, self.NAME)
|
||||||
|
@ -44,7 +44,7 @@ class LibvirtUtilsTestCase(test.NoDBTestCase):
|
|||||||
@mock.patch('nova.utils.execute')
|
@mock.patch('nova.utils.execute')
|
||||||
def test_copy_image_local(self, mock_execute):
|
def test_copy_image_local(self, mock_execute):
|
||||||
libvirt_utils.copy_image('src', 'dest')
|
libvirt_utils.copy_image('src', 'dest')
|
||||||
mock_execute.assert_called_once_with('cp', 'src', 'dest')
|
mock_execute.assert_called_once_with('cp', '-r', 'src', 'dest')
|
||||||
|
|
||||||
@mock.patch('nova.virt.libvirt.volume.remotefs.SshDriver.copy_file')
|
@mock.patch('nova.virt.libvirt.volume.remotefs.SshDriver.copy_file')
|
||||||
def test_copy_image_remote_ssh(self, mock_rem_fs_remove):
|
def test_copy_image_remote_ssh(self, mock_rem_fs_remove):
|
||||||
|
@ -166,7 +166,7 @@ class RemoteFSTestCase(test.NoDBTestCase):
|
|||||||
remotefs.RsyncDriver().copy_file('1.2.3.4:/home/star_wars',
|
remotefs.RsyncDriver().copy_file('1.2.3.4:/home/star_wars',
|
||||||
'/home/favourite', None, None,
|
'/home/favourite', None, None,
|
||||||
compression=True)
|
compression=True)
|
||||||
mock_execute.assert_called_once_with('rsync', '--sparse',
|
mock_execute.assert_called_once_with('rsync', '-r', '--sparse',
|
||||||
'1.2.3.4:/home/star_wars',
|
'1.2.3.4:/home/star_wars',
|
||||||
'/home/favourite',
|
'/home/favourite',
|
||||||
'--compress',
|
'--compress',
|
||||||
@ -178,7 +178,7 @@ class RemoteFSTestCase(test.NoDBTestCase):
|
|||||||
remotefs.RsyncDriver().copy_file('1.2.3.4:/home/star_wars',
|
remotefs.RsyncDriver().copy_file('1.2.3.4:/home/star_wars',
|
||||||
'/home/favourite', None, None,
|
'/home/favourite', None, None,
|
||||||
compression=False)
|
compression=False)
|
||||||
mock_execute.assert_called_once_with('rsync', '--sparse',
|
mock_execute.assert_called_once_with('rsync', '-r', '--sparse',
|
||||||
'1.2.3.4:/home/star_wars',
|
'1.2.3.4:/home/star_wars',
|
||||||
'/home/favourite',
|
'/home/favourite',
|
||||||
on_completion=None,
|
on_completion=None,
|
||||||
@ -188,7 +188,7 @@ class RemoteFSTestCase(test.NoDBTestCase):
|
|||||||
def test_remote_copy_file_ssh(self, mock_execute):
|
def test_remote_copy_file_ssh(self, mock_execute):
|
||||||
remotefs.SshDriver().copy_file('1.2.3.4:/home/SpaceOdyssey',
|
remotefs.SshDriver().copy_file('1.2.3.4:/home/SpaceOdyssey',
|
||||||
'/home/favourite', None, None, True)
|
'/home/favourite', None, None, True)
|
||||||
mock_execute.assert_called_once_with('scp',
|
mock_execute.assert_called_once_with('scp', '-r',
|
||||||
'1.2.3.4:/home/SpaceOdyssey',
|
'1.2.3.4:/home/SpaceOdyssey',
|
||||||
'/home/favourite',
|
'/home/favourite',
|
||||||
on_completion=None,
|
on_completion=None,
|
||||||
|
@ -33,6 +33,7 @@ if os.name != 'nt':
|
|||||||
from oslo_concurrency import processutils
|
from oslo_concurrency import processutils
|
||||||
from oslo_log import log as logging
|
from oslo_log import log as logging
|
||||||
from oslo_serialization import jsonutils
|
from oslo_serialization import jsonutils
|
||||||
|
from oslo_utils import units
|
||||||
|
|
||||||
import nova.conf
|
import nova.conf
|
||||||
from nova import exception
|
from nova import exception
|
||||||
@ -158,6 +159,16 @@ def extend(image, size):
|
|||||||
if not isinstance(image, imgmodel.LocalImage):
|
if not isinstance(image, imgmodel.LocalImage):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if (image.format == imgmodel.FORMAT_PLOOP):
|
||||||
|
if not can_resize_image(image.path, size):
|
||||||
|
return
|
||||||
|
|
||||||
|
utils.execute('prl_disk_tool', 'resize',
|
||||||
|
'--size', '%dM' % (size // units.Mi),
|
||||||
|
'--resize_partition',
|
||||||
|
'--hdd', image.path, run_as_root=True)
|
||||||
|
return
|
||||||
|
|
||||||
if not can_resize_image(image.path, size):
|
if not can_resize_image(image.path, size):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -20,10 +20,12 @@ from nova import exception
|
|||||||
|
|
||||||
FORMAT_RAW = "raw"
|
FORMAT_RAW = "raw"
|
||||||
FORMAT_QCOW2 = "qcow2"
|
FORMAT_QCOW2 = "qcow2"
|
||||||
|
FORMAT_PLOOP = "ploop"
|
||||||
|
|
||||||
ALL_FORMATS = [
|
ALL_FORMATS = [
|
||||||
FORMAT_RAW,
|
FORMAT_RAW,
|
||||||
FORMAT_QCOW2,
|
FORMAT_QCOW2,
|
||||||
|
FORMAT_PLOOP,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
@ -1024,9 +1024,7 @@ class Ploop(Image):
|
|||||||
utils.execute('ploop', 'restore-descriptor', '-f', self.pcs_format,
|
utils.execute('ploop', 'restore-descriptor', '-f', self.pcs_format,
|
||||||
target, image_path)
|
target, image_path)
|
||||||
if size:
|
if size:
|
||||||
dd_path = os.path.join(self.path, "DiskDescriptor.xml")
|
self.resize_image(size)
|
||||||
utils.execute('ploop', 'grow', '-s', '%dK' % (size >> 10),
|
|
||||||
dd_path, run_as_root=True)
|
|
||||||
|
|
||||||
if not os.path.exists(self.path):
|
if not os.path.exists(self.path):
|
||||||
if CONF.force_raw_images:
|
if CONF.force_raw_images:
|
||||||
@ -1063,9 +1061,8 @@ class Ploop(Image):
|
|||||||
create_ploop_image(base, self.path, size)
|
create_ploop_image(base, self.path, size)
|
||||||
|
|
||||||
def resize_image(self, size):
|
def resize_image(self, size):
|
||||||
dd_path = os.path.join(self.path, "DiskDescriptor.xml")
|
image = imgmodel.LocalFileImage(self.path, imgmodel.FORMAT_PLOOP)
|
||||||
utils.execute('ploop', 'grow', '-s', '%dK' % (size >> 10), dd_path,
|
disk.extend(image, size)
|
||||||
run_as_root=True)
|
|
||||||
|
|
||||||
def snapshot_extract(self, target, out_format):
|
def snapshot_extract(self, target, out_format):
|
||||||
img_path = os.path.join(self.path, "root.hds")
|
img_path = os.path.join(self.path, "root.hds")
|
||||||
|
@ -199,7 +199,8 @@ def copy_image(src, dest, host=None, receive=False,
|
|||||||
# sparse files. I.E. holes will not be written to DEST,
|
# sparse files. I.E. holes will not be written to DEST,
|
||||||
# rather recreated efficiently. In addition, since
|
# rather recreated efficiently. In addition, since
|
||||||
# coreutils 8.11, holes can be read efficiently too.
|
# coreutils 8.11, holes can be read efficiently too.
|
||||||
execute('cp', src, dest)
|
# we add '-r' argument because ploop disks are directories
|
||||||
|
execute('cp', '-r', src, dest)
|
||||||
else:
|
else:
|
||||||
if receive:
|
if receive:
|
||||||
src = "%s:%s" % (utils.safe_ip_format(host), src)
|
src = "%s:%s" % (utils.safe_ip_format(host), src)
|
||||||
|
@ -196,7 +196,8 @@ class SshDriver(RemoteFilesystemDriver):
|
|||||||
on_execute=on_execute, on_completion=on_completion)
|
on_execute=on_execute, on_completion=on_completion)
|
||||||
|
|
||||||
def copy_file(self, src, dst, on_execute, on_completion, compression):
|
def copy_file(self, src, dst, on_execute, on_completion, compression):
|
||||||
utils.execute('scp', src, dst,
|
# As far as ploop disks are in fact directories we add '-r' argument
|
||||||
|
utils.execute('scp', '-r', src, dst,
|
||||||
on_execute=on_execute, on_completion=on_completion)
|
on_execute=on_execute, on_completion=on_completion)
|
||||||
|
|
||||||
|
|
||||||
@ -324,7 +325,8 @@ class RsyncDriver(RemoteFilesystemDriver):
|
|||||||
on_execute=on_execute, on_completion=on_completion)
|
on_execute=on_execute, on_completion=on_completion)
|
||||||
|
|
||||||
def copy_file(self, src, dst, on_execute, on_completion, compression):
|
def copy_file(self, src, dst, on_execute, on_completion, compression):
|
||||||
args = ['rsync', '--sparse', src, dst]
|
# As far as ploop disks are in fact directories we add '-r' argument
|
||||||
|
args = ['rsync', '-r', '--sparse', src, dst]
|
||||||
if compression:
|
if compression:
|
||||||
args.append('--compress')
|
args.append('--compress')
|
||||||
utils.execute(*args,
|
utils.execute(*args,
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
features:
|
||||||
|
- Virtuozzo ploop disks can be resized now during "nova resize".
|
||||||
|
upgrade:
|
||||||
|
- You must update the rootwrap configuration for the compute service
|
||||||
|
if you use ploop images, so that "ploop grow" filter is changed
|
||||||
|
to "prl_disk_tool resize".
|
Loading…
Reference in New Issue
Block a user