-
Notifications
You must be signed in to change notification settings - Fork 59
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
Add support for creating and managing LVM cache volumes #235
Merged
richm
merged 3 commits into
linux-system-roles:master
from
vojtechtrefny:master_lvm-cache
Jan 10, 2022
Merged
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -474,6 +474,9 @@ def _destroy(self): | |
def _manage_encryption(self): | ||
self._device = self._manage_one_encryption(self._device) | ||
|
||
def _manage_cache(self): | ||
pass | ||
|
||
def _resize(self): | ||
""" Schedule actions as needed to ensure the device has the desired size. """ | ||
size = self._get_size() | ||
|
@@ -552,6 +555,7 @@ def manage(self): | |
raise BlivetAnsibleError("failed to look up or create device '%s'" % self._volume['name']) | ||
|
||
self._manage_encryption() | ||
self._manage_cache() | ||
|
||
# schedule reformat if appropriate | ||
if self._device.raw_device.exists: | ||
|
@@ -622,6 +626,10 @@ def _get_device_id(self): | |
def _resize(self): | ||
pass | ||
|
||
def _manage_cache(self): | ||
if self._volume['cached']: | ||
raise BlivetAnsibleError("caching is not supported for partition volumes") | ||
|
||
def _create(self): | ||
if self._device: | ||
return | ||
|
@@ -734,6 +742,55 @@ def _get_params_lvmraid(self): | |
|
||
return dict(seg_type=self._volume['raid_level'], pvs=pvs) | ||
|
||
def _detach_cache(self): | ||
""" Detach cache from the volume and remove the unused cache pool """ | ||
try: | ||
cpool_name = self._device.cache.detach() | ||
except Exception as e: | ||
raise BlivetAnsibleError("failed to detach cache from volume '%s': %s" % (self._device.name, str(e))) | ||
|
||
# full reset is needed for the cache pool to be added to the devicetree so we can remove it | ||
self._blivet.reset() | ||
|
||
cpool_name = cpool_name.rstrip("_cpool") | ||
cpool_device = self._blivet.devicetree.resolve_device("%s-%s" % (self._device.vg.name, cpool_name)) | ||
|
||
self._blivet.destroy_device(cpool_device) | ||
|
||
def _attach_cache(self): | ||
""" Create a new cache pool and attach it to the volume """ | ||
raise BlivetAnsibleError("adding cache to an existing volume is currently not supported") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Blivet unfortunately doesn't support creating a new cache pool. We can attach an existing cache pool to the LV, but we can't create it. I plan to add support for creating cache pools to blivet and I'll update this when the support is available. |
||
|
||
def _manage_cache(self): | ||
if not self._device: | ||
# cache for newly created LVs is managed in _create | ||
return | ||
|
||
if self._volume['cached'] and not self._device.raw_device.cached: | ||
self._attach_cache() | ||
if not self._volume['cached'] and self._device.raw_device.cached: | ||
self._detach_cache() | ||
|
||
def _get_params_lvmcache(self): | ||
parent = self._blivet_pool._device | ||
fast_pvs = [] | ||
for cache_spec in self._volume['cache_devices']: | ||
cache_device = self._blivet.devicetree.resolve_device(cache_spec) | ||
if cache_device is None: | ||
raise BlivetAnsibleError("cache device '%s' not found" % cache_spec) | ||
|
||
pv_device = next((pv for pv in parent.pvs if cache_device.name in [an.name for an in pv.ancestors]), | ||
None) | ||
if pv_device is None: | ||
raise BlivetAnsibleError("cache device '%s' doesn't seems to be a physical volume or its parent" % cache_spec) | ||
fast_pvs.append(pv_device) | ||
|
||
cache_request = devices.lvm.LVMCacheRequest(size=Size(self._volume['cache_size']), | ||
mode=self._volume['cache_mode'], | ||
pvs=fast_pvs) | ||
|
||
return dict(cache_request=cache_request) | ||
|
||
def _create(self): | ||
if self._device: | ||
return | ||
|
@@ -765,6 +822,10 @@ def _create(self): | |
lvmraid_arguments = self._get_params_lvmraid() | ||
newlv_arguments.update(lvmraid_arguments) | ||
|
||
if self._volume['cached']: | ||
lvmcache_arguments = self._get_params_lvmcache() | ||
newlv_arguments.update(lvmcache_arguments) | ||
|
||
try: | ||
device = self._blivet.new_lv(**newlv_arguments) | ||
except Exception as e: | ||
|
@@ -1456,7 +1517,11 @@ def run_module(): | |
state=dict(type='str', default='present', choices=['present', 'absent']), | ||
type=dict(type='str'), | ||
volumes=dict(type='list', elements='dict', default=list(), | ||
options=dict(compression=dict(type='bool'), | ||
options=dict(cached=dict(type='bool'), | ||
cache_devices=dict(type='list', elements='str', default=list()), | ||
cache_mode=dict(type='str'), | ||
cache_size=dict(type='str'), | ||
compression=dict(type='bool'), | ||
deduplication=dict(type='bool'), | ||
encryption=dict(type='bool'), | ||
encryption_cipher=dict(type='str'), | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
--- | ||
|
||
- name: check cache options | ||
block: | ||
|
||
- name: Get information about the LV | ||
command: > | ||
lvs --noheadings --nameprefixes --units=b --nosuffix --unquoted | ||
-o name,attr,cache_total_blocks,chunk_size,segtype | ||
{{ storage_test_pool.name }}/{{ storage_test_volume.name }} | ||
register: lvs | ||
changed_when: false | ||
|
||
- set_fact: | ||
storage_test_lv_segtype: "{{ lvs.stdout|regex_search('LVM2_SEGTYPE=(\\S+)', '\\1') }}" | ||
|
||
- name: check segment type | ||
assert: | ||
that: storage_test_volume.cached | ternary(storage_test_lv_segtype[0] == 'cache', storage_test_lv_segtype[0] != 'cache') | ||
msg: Unexpected segtype {{ storage_test_lv_segtype }} for {{ storage_test_volume.name }} | ||
|
||
- set_fact: | ||
storage_test_lv_cache_size: "{{ lvs.stdout|regex_search('LVM2_CACHE_TOTAL_BLOCKS=([0-9]*) LVM2_CHUNK_SIZE=([0-9]+)', '\\1', '\\2') }}" | ||
when: storage_test_volume.cached|bool | ||
|
||
- name: parse the requested cache size | ||
bsize: | ||
size: "{{ storage_test_volume.cache_size }}" | ||
register: storage_test_requested_cache_size | ||
when: storage_test_volume.cached|bool | ||
|
||
- set_fact: | ||
storage_test_expected_cache_size: "{{ storage_test_requested_cache_size.bytes }}" | ||
when: storage_test_volume.cached|bool | ||
|
||
- name: Check cache size | ||
assert: | ||
that: (storage_test_expected_cache_size|int - | ||
storage_test_lv_cache_size[0]|int * storage_test_lv_cache_size[1]|int)|abs / storage_test_expected_cache_size|int < 0.01 | ||
msg: > | ||
Unexpected cache size, expected {{ storage_test_expected_cache_size }}, | ||
got {{ storage_test_lv_cache_size[0]|int * storage_test_lv_cache_size[1]|int }} | ||
when: storage_test_volume.cached|bool | ||
|
||
when: storage_test_volume.type == 'lvm' and _storage_test_volume_present |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
--- | ||
- hosts: all | ||
become: true | ||
vars: | ||
storage_safe_mode: false | ||
storage_use_partitions: true | ||
volume_group_size: '10g' | ||
volume_size: '5g' | ||
cache_size: '4g' | ||
vojtechtrefny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
tags: | ||
- tests::lvm | ||
|
||
tasks: | ||
- include_role: | ||
name: linux-system-roles.storage | ||
|
||
- name: Mark tasks to be skipped | ||
set_fact: | ||
storage_skip_checks: | ||
- blivet_available | ||
- packages_installed | ||
- service_facts | ||
|
||
- include_tasks: get_unused_disk.yml | ||
vars: | ||
min_size: "{{ volume_group_size }}" | ||
max_return: 2 | ||
disks_needed: 2 | ||
|
||
- name: Create a cached LVM logical volume under volume group 'foo' | ||
include_role: | ||
name: linux-system-roles.storage | ||
vars: | ||
storage_pools: | ||
- name: foo | ||
disks: "{{ unused_disks }}" | ||
volumes: | ||
- name: test | ||
size: "{{ volume_size }}" | ||
cached: true | ||
cache_size: "{{ cache_size }}" | ||
cache_devices: "{{ [unused_disks[1]] }}" | ||
|
||
- include_tasks: verify-role-results.yml | ||
|
||
- name: Remove (detach) cache from the 'test' LV created above | ||
include_role: | ||
name: linux-system-roles.storage | ||
vars: | ||
storage_pools: | ||
- name: foo | ||
disks: "{{ unused_disks }}" | ||
volumes: | ||
- name: test | ||
size: "{{ volume_size }}" | ||
cached: false | ||
|
||
- include_tasks: verify-role-results.yml | ||
|
||
- name: Clean up | ||
include_role: | ||
name: linux-system-roles.storage | ||
vars: | ||
storage_pools: | ||
- name: foo | ||
disks: "{{ unused_disks }}" | ||
state: "absent" | ||
volumes: | ||
- name: test | ||
size: "{{ volume_size }}" | ||
|
||
- include_tasks: verify-role-results.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
--- | ||
- hosts: all | ||
become: true | ||
vars: | ||
storage_safe_mode: false | ||
volume_group_size: '10g' | ||
volume_size: '5g' | ||
cache_size: '4g' | ||
vojtechtrefny marked this conversation as resolved.
Show resolved
Hide resolved
|
||
tags: | ||
- tests::lvm | ||
|
||
tasks: | ||
- include_role: | ||
name: linux-system-roles.storage | ||
|
||
- name: Mark tasks to be skipped | ||
set_fact: | ||
storage_skip_checks: | ||
- blivet_available | ||
- packages_installed | ||
- service_facts | ||
|
||
- include_tasks: get_unused_disk.yml | ||
vars: | ||
max_return: 2 | ||
disks_needed: 2 | ||
|
||
- name: Verify that creating a cached partition volume fails | ||
block: | ||
- name: Create cached partition | ||
include_role: | ||
name: linux-system-roles.storage | ||
vars: | ||
storage_pools: | ||
- name: "{{ unused_disks[0] }}" | ||
type: partition | ||
disks: "{{ unused_disks }}" | ||
volumes: | ||
- name: test1 | ||
type: partition | ||
cached: true | ||
|
||
- name: unreachable task | ||
fail: | ||
msg: UNREACH | ||
|
||
rescue: | ||
- name: Check that we failed in the role | ||
assert: | ||
that: | ||
- ansible_failed_result.msg != 'UNREACH' | ||
msg: "Role has not failed when it should have" | ||
|
||
- name: Verify that creating cache on unused disk fails | ||
block: | ||
- name: Create cached volume | ||
include_role: | ||
name: linux-system-roles.storage | ||
vars: | ||
storage_pools: | ||
- name: "foo" | ||
disks: "{{ [unused_disks[0]] }}" | ||
volumes: | ||
- name: test1 | ||
size: "{{ volume_size }}" | ||
cached: true | ||
cache_size: "{{ cache_size }}" | ||
cache_devices: "{{ [unused_disks[1]] }}" | ||
|
||
- name: unreachable task | ||
fail: | ||
msg: UNREACH | ||
|
||
rescue: | ||
- name: Check that we failed in the role | ||
assert: | ||
that: | ||
- ansible_failed_result.msg != 'UNREACH' | ||
msg: "Role has not failed when it should have" |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PVs could be partitions or md as well as whole disks. Their names may be difficult to predict. Is it the case that, as of now, we cannot operate on non-existent PVs/VGs because blivet cannot yet create cache pools? If it is possible, is there a way to specify the cache devices in terms of which disks they are on in these cases?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point. I think we can allow specifying list of disks and look for PVs in their children. I'll update the PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added new commit that adds support for using disks for
cache_devices
instead of PVs.