From e6d2dd0c893c30dda1c51d02f51df7ae7093d008 Mon Sep 17 00:00:00 2001 From: Rishabh Dave Date: Mon, 3 Aug 2020 19:40:08 +0530 Subject: [PATCH] ceph-volume: relocate the new LVM methods Signed-off-by: Rishabh Dave --- src/ceph-volume/ceph_volume/api/lvm.py | 353 +++++++++++++------------ 1 file changed, 178 insertions(+), 175 deletions(-) diff --git a/src/ceph-volume/ceph_volume/api/lvm.py b/src/ceph-volume/ceph_volume/api/lvm.py index 15938e8560f2a..778614f96ff52 100644 --- a/src/ceph-volume/ceph_volume/api/lvm.py +++ b/src/ceph-volume/ceph_volume/api/lvm.py @@ -14,6 +14,61 @@ from ceph_volume.exceptions import SizeAllocationError logger = logging.getLogger(__name__) +def convert_filters_to_str(filters): + """ + Convert filter args from dictionary to following format - + filters={filter_name=filter_val,...} + """ + if not filters: + return filters + + filter_arg = '' + for k, v in filters.items(): + filter_arg += k + '=' + v + ',' + # get rid of extra comma at the end + filter_arg = filter_arg[:len(filter_arg) - 1] + + return filter_arg + + +def convert_tags_to_str(tags): + """ + Convert tags from dictionary to following format - + tags={tag_name=tag_val,...} + """ + if not tags: + return tags + + tag_arg = 'tags={' + for k, v in tags.items(): + tag_arg += k + '=' + v + ',' + # get rid of extra comma at the end + tag_arg = tag_arg[:len(tag_arg) - 1] + '}' + + return tag_arg + + +def make_filters_lvmcmd_ready(filters, tags): + """ + Convert filters (including tags) from dictionary to following format - + filter_name=filter_val...,tags={tag_name=tag_val,...} + + The command will look as follows = + lvs -S filter_name=filter_val...,tags={tag_name=tag_val,...} + """ + filters = convert_filters_to_str(filters) + tags = convert_tags_to_str(tags) + + if filters and tags: + return filters + ',' + tags + if filters and not tags: + return filters + if not filters and tags: + return tags + else: + return '' + + def _output_parser(output, fields): """ Newer versions of LVM allow ``--reportformat=json``, but older versions, @@ -391,6 +446,42 @@ def remove_pv(pv_name): ) +def get_pvs(fields=PV_FIELDS, filters='', tags=None): + """ + Return a list of PVs that are available on the system and match the + filters and tags passed. Argument filters takes a dictionary containing + arguments required by -S option of LVM. Passing a list of LVM tags can be + quite tricky to pass as a dictionary within dictionary, therefore pass + dictionary of tags via tags argument and tricky part will be taken care of + by the helper methods. + + :param fields: string containing list of fields to be displayed by the + pvs command + :param sep: string containing separator to be used between two fields + :param filters: dictionary containing LVM filters + :param tags: dictionary containng LVM tags + :returns: list of class PVolume object representing pvs on the system + """ + filters = make_filters_lvmcmd_ready(filters, tags) + args = ['pvs', '--no-heading', '--readonly', '--separator=";"', '-S', + filters, '-o', fields] + + stdout, stderr, returncode = process.call(args, verbose_on_failure=False) + pvs_report = _output_parser(stdout, fields) + return [PVolume(**pv_report) for pv_report in pvs_report] + + +def get_first_pv(fields=PV_FIELDS, filters=None, tags=None): + """ + Wrapper of get_pv meant to be a convenience method to avoid the phrase:: + pvs = get_pvs() + if len(pvs) >= 1: + pv = pvs[0] + """ + pvs = get_pvs(fields=fields, filters=filters, tags=tags) + return pvs[0] if len(pvs) > 0 else [] + + ################################ # # Code for LVM Volume Groups @@ -598,6 +689,41 @@ def remove_vg(vg_name): ) +def get_vgs(fields=VG_FIELDS, filters='', tags=None): + """ + Return a list of VGs that are available on the system and match the + filters and tags passed. Argument filters takes a dictionary containing + arguments required by -S option of LVM. Passing a list of LVM tags can be + quite tricky to pass as a dictionary within dictionary, therefore pass + dictionary of tags via tags argument and tricky part will be taken care of + by the helper methods. + + :param fields: string containing list of fields to be displayed by the + vgs command + :param sep: string containing separator to be used between two fields + :param filters: dictionary containing LVM filters + :param tags: dictionary containng LVM tags + :returns: list of class VolumeGroup object representing vgs on the system + """ + filters = make_filters_lvmcmd_ready(filters, tags) + args = ['vgs'] + VG_CMD_OPTIONS + ['-S', filters, '-o', fields] + + stdout, stderr, returncode = process.call(args, verbose_on_failure=False) + vgs_report =_output_parser(stdout, fields) + return [VolumeGroup(**vg_report) for vg_report in vgs_report] + + +def get_first_vg(fields=VG_FIELDS, filters=None, tags=None): + """ + Wrapper of get_vg meant to be a convenience method to avoid the phrase:: + vgs = get_vgs() + if len(vgs) >= 1: + vg = vgs[0] + """ + vgs = get_vgs(fields=fields, filters=filters, tags=tags) + return vgs[0] if len(vgs) > 0 else [] + + def get_device_vgs(device, name_prefix=''): stdout, stderr, returncode = process.call( ['pvs'] + VG_CMD_OPTIONS + ['-o', VG_FIELDS, device], @@ -843,53 +969,6 @@ def create_lv(name_prefix, return lv -def remove_lv(lv): - """ - Removes a logical volume given it's absolute path. - - Will return True if the lv is successfully removed or - raises a RuntimeError if the removal fails. - - :param lv: A ``Volume`` object or the path for an LV - """ - if isinstance(lv, Volume): - path = lv.lv_path - else: - path = lv - - stdout, stderr, returncode = process.call( - [ - 'lvremove', - '-v', # verbose - '-f', # force it - path - ], - show_command=True, - terminal_verbose=True, - ) - if returncode != 0: - raise RuntimeError("Unable to remove %s" % path) - return True - - -def get_lv_by_name(name): - stdout, stderr, returncode = process.call( - ['lvs', '--noheadings', '-o', LV_FIELDS, '-S', - 'lv_name={}'.format(name)], - verbose_on_failure=False - ) - lvs = _output_parser(stdout, LV_FIELDS) - return [Volume(**lv) for lv in lvs] - -def get_lvs_by_tag(lv_tag): - stdout, stderr, returncode = process.call( - ['lvs', '--noheadings', '--separator=";"', '-a', '-o', LV_FIELDS, '-S', - 'lv_tags={{{}}}'.format(lv_tag)], - verbose_on_failure=False - ) - lvs = _output_parser(stdout, LV_FIELDS) - return [Volume(**lv) for lv in lvs] - def create_lvs(volume_group, parts=None, size=None, name_prefix='ceph-lv'): """ Create multiple Logical Volumes from a Volume Group by calculating the @@ -933,141 +1012,34 @@ def create_lvs(volume_group, parts=None, size=None, name_prefix='ceph-lv'): return lvs -def get_device_lvs(device, name_prefix=''): - stdout, stderr, returncode = process.call( - ['pvs'] + LV_CMD_OPTIONS + ['-o', LV_FIELDS, device], - verbose_on_failure=False - ) - lvs = _output_parser(stdout, LV_FIELDS) - return [Volume(**lv) for lv in lvs if lv['lv_name'] and - lv['lv_name'].startswith(name_prefix)] - - -############################################################# -# -# New methods to get PVs, LVs, and VGs. -# Later, these can be easily merged with get_api_* methods -# -########################################################### - -def convert_filters_to_str(filters): - """ - Convert filter args from dictionary to following format - - filters={filter_name=filter_val,...} - """ - if not filters: - return filters - - filter_arg = '' - for k, v in filters.items(): - filter_arg += k + '=' + v + ',' - # get rid of extra comma at the end - filter_arg = filter_arg[:len(filter_arg) - 1] - - return filter_arg - -def convert_tags_to_str(tags): - """ - Convert tags from dictionary to following format - - tags={tag_name=tag_val,...} +def remove_lv(lv): """ - if not tags: - return tags - - tag_arg = 'tags={' - for k, v in tags.items(): - tag_arg += k + '=' + v + ',' - # get rid of extra comma at the end - tag_arg = tag_arg[:len(tag_arg) - 1] + '}' - - return tag_arg + Removes a logical volume given it's absolute path. -def make_filters_lvmcmd_ready(filters, tags): - """ - Convert filters (including tags) from dictionary to following format - - filter_name=filter_val...,tags={tag_name=tag_val,...} + Will return True if the lv is successfully removed or + raises a RuntimeError if the removal fails. - The command will look as follows = - lvs -S filter_name=filter_val...,tags={tag_name=tag_val,...} + :param lv: A ``Volume`` object or the path for an LV """ - filters = convert_filters_to_str(filters) - tags = convert_tags_to_str(tags) - - if filters and tags: - return filters + ',' + tags - if filters and not tags: - return filters - if not filters and tags: - return tags + if isinstance(lv, Volume): + path = lv.lv_path else: - return '' - -def get_pvs(fields=PV_FIELDS, filters='', tags=None): - """ - Return a list of PVs that are available on the system and match the - filters and tags passed. Argument filters takes a dictionary containing - arguments required by -S option of LVM. Passing a list of LVM tags can be - quite tricky to pass as a dictionary within dictionary, therefore pass - dictionary of tags via tags argument and tricky part will be taken care of - by the helper methods. - - :param fields: string containing list of fields to be displayed by the - pvs command - :param sep: string containing separator to be used between two fields - :param filters: dictionary containing LVM filters - :param tags: dictionary containng LVM tags - :returns: list of class PVolume object representing pvs on the system - """ - filters = make_filters_lvmcmd_ready(filters, tags) - args = ['pvs', '--no-heading', '--readonly', '--separator=";"', '-S', - filters, '-o', fields] - - stdout, stderr, returncode = process.call(args, verbose_on_failure=False) - pvs_report = _output_parser(stdout, fields) - return [PVolume(**pv_report) for pv_report in pvs_report] - -def get_first_pv(fields=PV_FIELDS, filters=None, tags=None): - """ - Wrapper of get_pv meant to be a convenience method to avoid the phrase:: - pvs = get_pvs() - if len(pvs) >= 1: - pv = pvs[0] - """ - pvs = get_pvs(fields=fields, filters=filters, tags=tags) - return pvs[0] if len(pvs) > 0 else [] - -def get_vgs(fields=VG_FIELDS, filters='', tags=None): - """ - Return a list of VGs that are available on the system and match the - filters and tags passed. Argument filters takes a dictionary containing - arguments required by -S option of LVM. Passing a list of LVM tags can be - quite tricky to pass as a dictionary within dictionary, therefore pass - dictionary of tags via tags argument and tricky part will be taken care of - by the helper methods. - - :param fields: string containing list of fields to be displayed by the - vgs command - :param sep: string containing separator to be used between two fields - :param filters: dictionary containing LVM filters - :param tags: dictionary containng LVM tags - :returns: list of class VolumeGroup object representing vgs on the system - """ - filters = make_filters_lvmcmd_ready(filters, tags) - args = ['vgs'] + VG_CMD_OPTIONS + ['-S', filters, '-o', fields] + path = lv - stdout, stderr, returncode = process.call(args, verbose_on_failure=False) - vgs_report =_output_parser(stdout, fields) - return [VolumeGroup(**vg_report) for vg_report in vgs_report] + stdout, stderr, returncode = process.call( + [ + 'lvremove', + '-v', # verbose + '-f', # force it + path + ], + show_command=True, + terminal_verbose=True, + ) + if returncode != 0: + raise RuntimeError("Unable to remove %s" % path) + return True -def get_first_vg(fields=VG_FIELDS, filters=None, tags=None): - """ - Wrapper of get_vg meant to be a convenience method to avoid the phrase:: - vgs = get_vgs() - if len(vgs) >= 1: - vg = vgs[0] - """ - vgs = get_vgs(fields=fields, filters=filters, tags=tags) - return vgs[0] if len(vgs) > 0 else [] def get_lvs(fields=LV_FIELDS, filters='', tags=None): """ @@ -1092,6 +1064,7 @@ def get_lvs(fields=LV_FIELDS, filters='', tags=None): lvs_report = _output_parser(stdout, fields) return [Volume(**lv_report) for lv_report in lvs_report] + def get_first_lv(fields=LV_FIELDS, filters=None, tags=None): """ Wrapper of get_lv meant to be a convenience method to avoid the phrase:: @@ -1101,3 +1074,33 @@ def get_first_lv(fields=LV_FIELDS, filters=None, tags=None): """ lvs = get_lvs(fields=fields, filters=filters, tags=tags) return lvs[0] if len(lvs) > 0 else [] + + +def get_lv_by_name(name): + stdout, stderr, returncode = process.call( + ['lvs', '--noheadings', '-o', LV_FIELDS, '-S', + 'lv_name={}'.format(name)], + verbose_on_failure=False + ) + lvs = _output_parser(stdout, LV_FIELDS) + return [Volume(**lv) for lv in lvs] + + +def get_lvs_by_tag(lv_tag): + stdout, stderr, returncode = process.call( + ['lvs', '--noheadings', '--separator=";"', '-a', '-o', LV_FIELDS, '-S', + 'lv_tags={{{}}}'.format(lv_tag)], + verbose_on_failure=False + ) + lvs = _output_parser(stdout, LV_FIELDS) + return [Volume(**lv) for lv in lvs] + + +def get_device_lvs(device, name_prefix=''): + stdout, stderr, returncode = process.call( + ['pvs'] + LV_CMD_OPTIONS + ['-o', LV_FIELDS, device], + verbose_on_failure=False + ) + lvs = _output_parser(stdout, LV_FIELDS) + return [Volume(**lv) for lv in lvs if lv['lv_name'] and + lv['lv_name'].startswith(name_prefix)] -- 2.39.5