Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow volume shrinking #479

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions qubes/storage/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,14 +355,17 @@ def is_outdated(self):
'''
raise self._not_implemented("is_outdated")

async def resize(self, size):
''' Expands volume, throws
async def resize(self, size, allow_shrink=False):
''' Resizes volume, throws
:py:class:`qubes.storage.StoragePoolException` if
given size is less than current_size
given size is less than current_size and allow_shrink
is not True

This can be implemented as a coroutine.

:param int size: new size in bytes
:param bool allow_shrink: if shrinking volume is allowed. \
False by default.
'''
# pylint: disable=unused-argument
raise self._not_implemented("resize")
Expand Down
4 changes: 2 additions & 2 deletions qubes/storage/callback.py
Original file line number Diff line number Diff line change
Expand Up @@ -454,10 +454,10 @@ async def remove(self):
await self._callback('post_volume_remove')
return ret

async def resize(self, size):
async def resize(self, size, allow_shrink=False):
await self._assert_initialized()
await self._callback('pre_volume_resize', cb_args=[size])
return await coro_maybe(self._cb_impl.resize(size))
return await coro_maybe(self._cb_impl.resize(size, allow_shrink))

async def start(self):
await self._assert_initialized()
Expand Down
9 changes: 5 additions & 4 deletions qubes/storage/file.py
Original file line number Diff line number Diff line change
Expand Up @@ -324,10 +324,11 @@ def _resize_snapshot_dev(self, size):
# and make it active
subprocess.check_call(['dmsetup', 'resume', path])

def resize(self, size): # pylint: disable=invalid-overridden-method
''' Expands volume, throws
def resize(self, size, allow_shrink=False): \
# pylint: disable=invalid-overridden-method
''' Resizes volume, throws
:py:class:`qubst.storage.qubes.storage.StoragePoolException` if
given size is less than current_size
given size is less than current size and allow_shrink is not True
''' # pylint: disable=no-self-use
if not self.rw:
msg = 'Can not resize reađonly volume {!s}'.format(self)
Expand All @@ -340,7 +341,7 @@ def resize(self, size): # pylint: disable=invalid-overridden-method
'the template instead'
raise qubes.storage.StoragePoolException(msg)

if size < self.size:
if size < self.size and not allow_shrink:
raise qubes.storage.StoragePoolException(
'For your own safety, shrinking of %s is'
' disabled. If you really know what you'
Expand Down
31 changes: 20 additions & 11 deletions qubes/storage/lvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,34 +605,39 @@ async def revert(self, revision=None):
return self

@qubes.storage.Volume.locked
async def resize(self, size):
''' Expands volume, throws
async def resize(self, size, allow_shrink=False):
''' Resizes volume, throws
:py:class:`qubst.storage.qubes.storage.StoragePoolException` if
given size is less than current_size
given size is less than current size and allow_shrink is not True
'''
lvm_command = 'extend'

if not self.rw:
msg = 'Can not resize reađonly volume {!s}'.format(self)
raise qubes.storage.StoragePoolException(msg)

if size < self.size:
raise qubes.storage.StoragePoolException(
'For your own safety, shrinking of %s is'
' disabled (%d < %d). If you really know what you'
' are doing, use `lvresize` on %s manually.' %
(self.name, size, self.size, self.vid))
if allow_shrink:
lvm_command = 'resize'
else:
raise qubes.storage.StoragePoolException(
'For your own safety, shrinking of %s is'
' disabled (%d < %d). If you really know what you'
' are doing, use `lvresize` on %s manually.' %
(self.name, size, self.size, self.vid))

if size == self.size:
return

if self.is_dirty() or self.snap_on_start:
cmd = ['extend', self._vid_snap, str(size)]
cmd = [lvm_command, self._vid_snap, str(size)]
await qubes_lvm_coro(cmd, self.log)
elif hasattr(self, '_vid_import') and \
os.path.exists('/dev/' + self._vid_import):
cmd = ['extend', self._vid_import, str(size)]
cmd = [lvm_command, self._vid_import, str(size)]
await qubes_lvm_coro(cmd, self.log)
elif self.save_on_stop and not self.snap_on_start:
cmd = ['extend', self._vid_current, str(size)]
cmd = [lvm_command, self._vid_current, str(size)]
await qubes_lvm_coro(cmd, self.log)

self._size = size
Expand Down Expand Up @@ -774,6 +779,10 @@ def _get_lvm_cmdline(cmd):
elif action == 'extend':
assert len(cmd) == 3, 'wrong number of arguments for extend'
lvm_cmd = ["lvextend", "--size=" + cmd[2] + 'B', '--', cmd[1]]
elif action == 'resize':
assert len(cmd) == 3, 'wrong number of arguments for resize'
lvm_cmd = ["lvresize", "--force", "--size=" + cmd[2] + 'B',
'--', cmd[1]]
elif action == 'activate':
assert len(cmd) == 2, 'wrong number of arguments for activate'
lvm_cmd = ['lvchange', '--activate=y', '--', cmd[1]]
Expand Down
14 changes: 12 additions & 2 deletions qubes/storage/reflink.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,10 +296,20 @@ def revert(self, revision=None): # pylint: disable=invalid-overridden-method

@qubes.storage.Volume.locked
@_coroutinized
def resize(self, size): # pylint: disable=invalid-overridden-method
def resize(self, size, allow_shrink=False): \
# pylint: disable=invalid-overridden-method
''' Resize a read-write volume; notify any corresponding loop
devices of the size change.
devices of the size change; throw
:py:class:`qubst.storage.qubes.storage.StoragePoolException` if
given size is less than current size and allow_shrink is not True
'''
if size < self._size and not allow_shrink:
raise qubes.storage.StoragePoolException(
'For your own safety, shrinking of %s is'
' disabled. If you really know what you'
' are doing, use `truncate` on %s manually.' %
(self.name, self.vid))

if not self.rw:
raise qubes.storage.StoragePoolException(
'Cannot resize: {} is read-only'.format(self.vid))
Expand Down
8 changes: 8 additions & 0 deletions qubes/tests/storage_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,14 @@ def test_023_resize(self):
qubes.utils.coro_maybe(volume.resize(new_size)))
self.assertEqual(os.path.getsize(volume.path), new_size)
self.assertEqual(volume.size, new_size)
self.loop.run_until_complete(
qubes.utils.coro_maybe(volume.resize(new_size)))
self.assertEqual(os.path.getsize(volume.path), new_size/4)
self.assertEqual(volume.size, new_size)
self.loop.run_until_complete(
qubes.utils.coro_maybe(volume.resize(new_size/4, True)))
self.assertEqual(os.path.getsize(volume.path), new_size/4)
self.assertEqual(volume.size, new_size)

def test_024_import_data_with_new_size(self):
config = {
Expand Down
6 changes: 6 additions & 0 deletions qubes/tests/storage_lvm.py
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,12 @@ def test_006_resize(self):
self.loop.run_until_complete(volume.resize(new_size))
self.assertEqual(self._get_size(path), new_size)
self.assertEqual(volume.size, new_size)
self.loop.run_until_complete(volume.resize(new_size/4))
self.assertEqual(self._get_size(path), new_size)
self.assertEqual(volume.size, new_size)
self.loop.run_until_complete(volume.resize(new_size/4, True))
self.assertEqual(self._get_size(path), new_size/4)
self.assertEqual(volume.size, new_size/4)

def test_007_resize_running(self):
old_size = 32 * 1024**2
Expand Down