Skip to content

Commit

Permalink
feat: Add support for filesystem online resize
Browse files Browse the repository at this point in the history
Mounted devices are now resized without unmounting them first if
the filesystem supports online resizing.
  • Loading branch information
vojtechtrefny authored and richm committed Jun 22, 2023
1 parent 57a94ab commit 0fb30fb
Show file tree
Hide file tree
Showing 2 changed files with 137 additions and 3 deletions.
41 changes: 38 additions & 3 deletions library/blivet.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@
from blivet3.deviceaction import ActionConfigureFormat, ActionAddMember, ActionRemoveMember
from blivet3.devicefactory import DEFAULT_THPOOL_RESERVE
from blivet3.flags import flags as blivet_flags
from blivet3.formats import get_format
from blivet3.formats import fslib, get_format
from blivet3.partitioning import do_partitioning
from blivet3.size import Size
from blivet3.udev import trigger
Expand All @@ -138,7 +138,7 @@
from blivet.deviceaction import ActionConfigureFormat, ActionAddMember, ActionRemoveMember
from blivet.devicefactory import DEFAULT_THPOOL_RESERVE
from blivet.flags import flags as blivet_flags
from blivet.formats import get_format
from blivet.formats import fslib, get_format
from blivet.partitioning import do_partitioning
from blivet.size import Size
from blivet.udev import trigger
Expand All @@ -152,6 +152,7 @@

if BLIVET_PACKAGE:
blivet_flags.debug = True
blivet_flags.allow_online_fs_resize = True
set_up_logging()
log = logging.getLogger(BLIVET_PACKAGE + ".ansible")

Expand Down Expand Up @@ -512,6 +513,40 @@ def _resize(self):
if not self._device.min_size <= size <= self._device.max_size:
raise BlivetAnsibleError("volume '%s' cannot be resized to '%s'" % (self._volume['name'], size))

def _fs_unmount():
if safe_mode:
raise BlivetAnsibleError("device '%s' must be unmounted to be resized in safe mode" %
self._device.name)

log.info("unmounting device %s for offline resize", self._device.name)

try:
self._device.format.teardown()
except Exception as e:
raise BlivetAnsibleError("failed to unmount '%s' to allow offline resize: %s" % (self._device.name, str(e)))

if self._device.format.status and self._device.format.mountable:
if hasattr(self._device.format, "_resize_support") and hasattr(fslib, "FSResize"):
if size > self._device.size:
# online grow
if self._device.format._resize_support & fslib.FSResize.ONLINE_GROW:
log.debug("filesystem '%s' supports online grow, will attempt to resize '%s' while mounted",
self._device.format.type, self._device.name)
else:
# online grow not supported, try unmount the device
_fs_unmount()
elif size < self._device.size:
# online shrink
if self._device.format._resize_support & fslib.FSResize.ONLINE_SHRINK:
log.debug("filesystem '%s' supports online shrink, will attempt to resize '%s' while mounted",
self._device.format.type, self._device.name)
else:
# online shrink not supported, try unmount the device
_fs_unmount()
else:
# online resize not supported by blivet
_fs_unmount()

try:
self._blivet.resize_device(self._device, size)
except ValueError as e:
Expand Down Expand Up @@ -1908,7 +1943,7 @@ def action_dict(action):
result['packages'] = b.packages[:]

for action in scheduled:
if (action.is_destroy or action.is_resize) and action.is_format and action.format.exists and \
if action.is_destroy and action.is_format and action.format.exists and \
(action.format.mountable or action.format.type == "swap"):
action.format.teardown()

Expand Down
99 changes: 99 additions & 0 deletions tests/tests_resize.yml
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,102 @@

- name: Verify role results
include_tasks: verify-role-results.yml

- name: Gather package facts
package_facts:

- name: Set blivet package name
set_fact:
blivet_pkg_name: "{{ ansible_facts.packages |
select('search', 'blivet') | select('search', 'python') | list }}"

- name: Set blivet package version
set_fact:
blivet_pkg_version: "{{
ansible_facts.packages[blivet_pkg_name[0]][0]['version'] +
'-' + ansible_facts.packages[blivet_pkg_name[0]][0]['release'] }}"

- name: Set distribution version
set_fact:
is_rhel9: "{{ (ansible_facts.distribution == 'CentOS' or
ansible_facts.distribution == 'RedHat') and
ansible_facts.distribution_major_version == '9' }}"
is_rhel8: "{{ (ansible_facts.distribution == 'CentOS' or
ansible_facts.distribution == 'RedHat') and
ansible_facts.distribution_major_version == '8' }}"
is_fedora: "{{ ansible_facts.distribution == 'Fedora' }}"

# For ext4 FS -- online resize

- name: Run test on supported platforms
when: ((is_fedora and blivet_pkg_version is version("3.7.1-3", ">=")) or
(is_rhel8 and blivet_pkg_version is version("3.6.0-6", ">=")) or
(is_rhel9 and blivet_pkg_version is version("3.6.0-8", ">=")))
block:
- name: >-
Create one LVM logical volume under one volume group with size
{{ volume_size_before }}
include_role:
name: linux-system-roles.storage
vars:
storage_pools:
- name: foo
disks: "{{ unused_disks }}"
type: lvm
volumes:
- name: test1
fs_type: ext4
size: "{{ volume_size_before }}"
mount_point: "{{ mount_location }}"
- name: Verify role results
include_tasks: verify-role-results.yml

- name: Change volume_size to {{ volume_size_after }}
include_role:
name: linux-system-roles.storage
vars:
storage_pools:
- name: foo
type: lvm
disks: "{{ unused_disks }}"
volumes:
- name: test1
fs_type: ext4
size: "{{ volume_size_after }}"
mount_point: "{{ mount_location }}"

- name: Verify role results
include_tasks: verify-role-results.yml

- name: Test for correct handling of offline resize in safe mode
include_tasks: verify-role-failed.yml
vars:
__storage_failed_regex: must be unmounted to be resized in safe mode
__storage_failed_msg: >-
Unexpected behavior w/ resize in safe mode
__storage_failed_params:
storage_pools:
- name: foo
disks: "{{ unused_disks }}"
volumes:
- name: test1
fs_type: ext4
size: "{{ volume_size_before }}"
mount_point: "{{ mount_location }}"

- name: Clean up
include_role:
name: linux-system-roles.storage
vars:
storage_pools:
- name: foo
disks: "{{ unused_disks }}"
state: absent
volumes:
- name: test1
size: "{{ volume_size_before }}"
mount_point: "{{ mount_location }}"

- name: Verify role results
include_tasks: verify-role-results.yml

0 comments on commit 0fb30fb

Please sign in to comment.