storage: add volume clone method
Clone volume without retrieving all the data. QubesOS/qubes-issues#2622
This commit is contained in:
parent
90141a1bef
commit
998a42703f
@ -93,6 +93,11 @@ class Volume(object):
|
|||||||
return self.pool == other.pool and self.vid == other.vid
|
return self.pool == other.pool and self.vid == other.vid
|
||||||
return NotImplemented
|
return NotImplemented
|
||||||
|
|
||||||
|
@property
|
||||||
|
def name(self):
|
||||||
|
'''per-VM volume name, if available'''
|
||||||
|
return self._vm_name
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def pool(self):
|
def pool(self):
|
||||||
'''Storage volume pool name.'''
|
'''Storage volume pool name.'''
|
||||||
@ -195,6 +200,26 @@ class Volume(object):
|
|||||||
'''
|
'''
|
||||||
self._qubesd_call('Import', payload_stream=stream)
|
self._qubesd_call('Import', payload_stream=stream)
|
||||||
|
|
||||||
|
def clone(self, source):
|
||||||
|
''' Clone data from sane volume of another VM.
|
||||||
|
|
||||||
|
This function override existing volume content.
|
||||||
|
This operation is implemented for VM volumes - those in vm.volumes
|
||||||
|
collection (not pool.volumes).
|
||||||
|
|
||||||
|
:param source: source volume object
|
||||||
|
'''
|
||||||
|
|
||||||
|
# pylint: disable=protected-access
|
||||||
|
if source._vm_name is None or self._vm_name is None:
|
||||||
|
raise NotImplementedError(
|
||||||
|
'Operation implemented only for VM volumes')
|
||||||
|
if source._vm_name != self._vm_name:
|
||||||
|
raise ValueError('Source and target volume must have the same type')
|
||||||
|
|
||||||
|
# this call is to *source* volume, because we extract data from there
|
||||||
|
source._qubesd_call('Clone', payload=str(self._vm).encode())
|
||||||
|
|
||||||
|
|
||||||
class Pool(object):
|
class Pool(object):
|
||||||
''' A Pool is used to manage different kind of volumes (File
|
''' A Pool is used to manage different kind of volumes (File
|
||||||
|
@ -161,6 +161,30 @@ class TestVMVolume(qubesadmin.tests.QubesTestCase):
|
|||||||
input_proc.stdout.close()
|
input_proc.stdout.close()
|
||||||
self.assertAllCalled()
|
self.assertAllCalled()
|
||||||
|
|
||||||
|
def test_050_clone(self):
|
||||||
|
self.app.expected_calls[
|
||||||
|
('source-vm', 'admin.vm.volume.Clone', 'volname', b'test-vm')] = \
|
||||||
|
b'0\x00'
|
||||||
|
self.app.expected_calls[
|
||||||
|
('dom0', 'admin.vm.List', None, None)] = \
|
||||||
|
b'0\x00source-vm class=AppVM state=Halted\n'
|
||||||
|
self.app.expected_calls[
|
||||||
|
('source-vm', 'admin.vm.volume.List', None, None)] = \
|
||||||
|
b'0\x00volname\nother\n'
|
||||||
|
self.vol.clone(self.app.domains['source-vm'].volumes['volname'])
|
||||||
|
self.assertAllCalled()
|
||||||
|
|
||||||
|
def test_050_clone_wrong_volume(self):
|
||||||
|
self.app.expected_calls[
|
||||||
|
('dom0', 'admin.vm.List', None, None)] = \
|
||||||
|
b'0\x00source-vm class=AppVM state=Halted\n'
|
||||||
|
self.app.expected_calls[
|
||||||
|
('source-vm', 'admin.vm.volume.List', None, None)] = \
|
||||||
|
b'0\x00volname\nother\n'
|
||||||
|
with self.assertRaises(ValueError):
|
||||||
|
self.vol.clone(self.app.domains['source-vm'].volumes['other'])
|
||||||
|
self.assertAllCalled()
|
||||||
|
|
||||||
|
|
||||||
class TestPoolVolume(TestVMVolume):
|
class TestPoolVolume(TestVMVolume):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -245,7 +269,21 @@ class TestPoolVolume(TestVMVolume):
|
|||||||
self.assertAllCalled()
|
self.assertAllCalled()
|
||||||
|
|
||||||
def test_040_import_data(self):
|
def test_040_import_data(self):
|
||||||
self.skipTest('admin.pool.vm.Import not supported')
|
self.skipTest('admin.pool.volume.Import not supported')
|
||||||
|
|
||||||
|
def test_050_clone(self):
|
||||||
|
self.app.expected_calls[
|
||||||
|
('dom0', 'admin.vm.List', None, None)] = \
|
||||||
|
b'0\x00source-vm class=AppVM state=Halted\n'
|
||||||
|
self.app.expected_calls[
|
||||||
|
('source-vm', 'admin.vm.volume.List', None, None)] = \
|
||||||
|
b'0\x00volname\nother\n'
|
||||||
|
with self.assertRaises(NotImplementedError):
|
||||||
|
self.vol.clone(self.app.domains['source-vm'].volumes['volname'])
|
||||||
|
self.assertAllCalled()
|
||||||
|
|
||||||
|
def test_050_clone_wrong_volume(self):
|
||||||
|
self.skipTest('admin.pool.volume.Clone not supported')
|
||||||
|
|
||||||
|
|
||||||
class TestPool(qubesadmin.tests.QubesTestCase):
|
class TestPool(qubesadmin.tests.QubesTestCase):
|
||||||
|
Loading…
Reference in New Issue
Block a user