diff options
author | David Teigland <teigland@redhat.com> | 2016-12-08 16:22:26 -0600 |
---|---|---|
committer | David Teigland <teigland@redhat.com> | 2016-12-09 09:30:58 -0600 |
commit | c0a356561aab01cedcdd603e580162aea2b4e0de (patch) | |
tree | 0b12d637680e116050c87a7854f0cb53cd0ff916 | |
parent | 4df2157c7262d17c021d274211d1a2859a824dc5 (diff) | |
download | lvm2-c0a356561aab01cedcdd603e580162aea2b4e0de.tar.gz |
lvconvert: use command defs for pool metadata swap
-rw-r--r-- | test/shell/lvconvert-thin.sh | 18 | ||||
-rw-r--r-- | tools/args.h | 1 | ||||
-rw-r--r-- | tools/command-lines.in | 12 | ||||
-rw-r--r-- | tools/lvconvert.c | 227 | ||||
-rw-r--r-- | tools/lvmcmdline.c | 3 | ||||
-rw-r--r-- | tools/tools.h | 1 |
6 files changed, 245 insertions, 17 deletions
diff --git a/test/shell/lvconvert-thin.sh b/test/shell/lvconvert-thin.sh index 2144bd23b..f765591e1 100644 --- a/test/shell/lvconvert-thin.sh +++ b/test/shell/lvconvert-thin.sh @@ -81,9 +81,9 @@ grep "Pool zeroing and large" err UUID=$(get lv_field $vg/$lv2 uuid) # Fail is pool is active # TODO maybe detect inactive pool and deactivate -fail lvconvert --yes --thinpool $vg/$lv1 --poolmetadata $lv2 +fail lvconvert --swapmetadata --yes --poolmetadata $lv2 $vg/$lv1 lvchange -an $vg -lvconvert --yes --thinpool $vg/$lv1 --poolmetadata $lv2 +lvconvert --swapmetadata --yes --poolmetadata $lv2 $vg/$lv1 check lv_field $vg/${lv1}_tmeta uuid "$UUID" lvremove -f $vg @@ -96,20 +96,20 @@ lvcreate -L1M -n $lv3 $vg # chunk size is bigger then size of thin pool data fail lvconvert --yes -c 1G --thinpool $vg/$lv3 # stripes can't be used with poolmetadata -invalid lvconvert --stripes 2 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 +not lvconvert --stripes 2 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 # too small metadata (<2M) fail lvconvert --yes -c 64 --thinpool $vg/$lv1 --poolmetadata $vg/$lv3 # too small chunk size fails -invalid lvconvert -c 4 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 +not lvconvert -c 4 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 # too big chunk size fails -invalid lvconvert -c 2G --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 +not lvconvert -c 2G --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 # negative chunk size fails -invalid lvconvert -c -256 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 +not lvconvert -c -256 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 # non multiple of 64KiB fails -invalid lvconvert -c 88 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 +not lvconvert -c 88 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 # cannot use same LV for pool and convertion -invalid lvconvert --yes --thinpool $vg/$lv3 -T $vg/$lv3 +not lvconvert --yes --thinpool $vg/$lv3 -T $vg/$lv3 # Warning about smaller then suggested lvconvert --yes -c 256 --thinpool $vg/$lv1 --poolmetadata $vg/$lv2 2>&1 | tee err @@ -129,7 +129,7 @@ if test "$TSIZE" = 64T; then lvcreate -L24T -n $lv1 $vg # Warning about bigger then needed (24T data and 16G -> 128K chunk) lvconvert --yes -c 64 --thinpool $vg/$lv1 2>&1 | tee err -grep "WARNING: Chunk size is too small" err +grep "too small" err lvremove -f $vg fi diff --git a/tools/args.h b/tools/args.h index 3560c9c93..654f7189e 100644 --- a/tools/args.h +++ b/tools/args.h @@ -120,6 +120,7 @@ arg(showdeprecated_ARG, '\0', "showdeprecated", 0, 0, 0) arg(showunsupported_ARG, '\0', "showunsupported", 0, 0, 0) arg(startpoll_ARG, '\0', "startpoll", 0, 0, 0) arg(stripes_long_ARG, '\0', "stripes", number_VAL, 0, 0) +arg(swapmetadata_ARG, '\0', "swapmetadata", 0, 0, 0) arg(syncaction_ARG, '\0', "syncaction", syncaction_VAL, 0, 0) arg(sysinit_ARG, '\0', "sysinit", 0, 0, 0) arg(systemid_ARG, '\0', "systemid", string_VAL, 0, 0) diff --git a/tools/command-lines.in b/tools/command-lines.in index 5768c9709..521dd8ded 100644 --- a/tools/command-lines.in +++ b/tools/command-lines.in @@ -481,10 +481,16 @@ DESC: Separate and delete the cache pool from a cache LV. # FIXME: add a new option defining this operation, e.g. --swapmetadata -lvconvert --poolmetadata LV LV_thinpool_cachepool -OO: OO_LVCONVERT +lvconvert --swapmetadata --poolmetadata LV LV_thinpool_cachepool +OO: --chunksize SizeKB, OO_LVCONVERT ID: lvconvert_swap_pool_metadata -DESC: Swap metadata LV in a thin pool or cache pool (temporary command). +DESC: Swap metadata LV in a thin pool or cache pool (for repair only). + +# alternate form, does not use explicit command option +lvconvert --poolmetadata LV LV_thinpool_cachepool +OO: --chunksize SizeKB, OO_LVCONVERT +ID: lvconvert_swap_pool_metadata_deprecated +DESC: Swap metadata LV in a thin pool or cache pool (variant, use --swapmetadata). FLAGS: SECONDARY_SYNTAX --- diff --git a/tools/lvconvert.c b/tools/lvconvert.c index 629bd600b..0c0fbd9f5 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -3373,6 +3373,143 @@ revert_new_lv: #endif } +static int _lvconvert_swap_pool_metadata(struct cmd_context *cmd, + struct logical_volume *lv, + struct logical_volume *metadata_lv) +{ + struct volume_group *vg = lv->vg; + struct logical_volume *prev_metadata_lv; + struct lv_segment *seg; + struct lv_types *lvtype; + char meta_name[NAME_LEN]; + const char *swap_name; + uint32_t chunk_size; + int is_thinpool; + int is_cachepool; + int lvt_enum; + + is_thinpool = lv_is_thin_pool(lv); + is_cachepool = lv_is_cache_pool(lv); + lvt_enum = get_lvt_enum(metadata_lv); + lvtype = get_lv_type(lvt_enum); + + if (lvt_enum != striped_LVT && lvt_enum != linear_LVT && lvt_enum != raid_LVT) { + log_error("LV %s with type %s cannot be used as a metadata LV.", + display_lvname(metadata_lv), lvtype ? lvtype->name : "unknown"); + return 0; + } + + if (!lv_is_visible(metadata_lv)) { + log_error("Can't convert internal LV %s.", + display_lvname(metadata_lv)); + return 0; + } + + if (lv_is_locked(metadata_lv)) { + log_error("Can't convert locked LV %s.", + display_lvname(metadata_lv)); + return 0; + } + + if (lv_is_origin(metadata_lv) || + lv_is_merging_origin(metadata_lv) || + lv_is_external_origin(metadata_lv) || + lv_is_virtual(metadata_lv)) { + log_error("Pool metadata LV %s is of an unsupported type.", + display_lvname(metadata_lv)); + return 0; + } + + /* FIXME cache pool */ + if (is_thinpool && pool_is_active(lv)) { + /* If any volume referencing pool active - abort here */ + log_error("Cannot convert pool %s with active volumes.", + display_lvname(lv)); + return 0; + } + + if ((dm_snprintf(meta_name, sizeof(meta_name), "%s%s", lv->name, is_cachepool ? "_cmeta" : "_tmeta") < 0)) { + log_error("Failed to create internal lv names, pool name is too long."); + return 0; + } + + seg = first_seg(lv); + + /* Normally do NOT change chunk size when swapping */ + + if (arg_is_set(cmd, chunksize_ARG)) { + chunk_size = arg_uint_value(cmd, chunksize_ARG, 0); + + if ((chunk_size != seg->chunk_size) && !dm_list_empty(&lv->segs_using_this_lv)) { + if (arg_count(cmd, force_ARG) == PROMPT) { + log_error("Chunk size can be only changed with --force. Conversion aborted."); + return 0; + } + + log_warn("WARNING: Changing chunk size %s to %s for %s pool volume.", + display_size(cmd, seg->chunk_size), + display_size(cmd, chunk_size), + display_lvname(lv)); + + /* Ok, user has likely some serious reason for this */ + if (!arg_count(cmd, yes_ARG) && + yes_no_prompt("Do you really want to change chunk size for %s pool volume? [y/n]: ", + display_lvname(lv)) == 'n') { + log_error("Conversion aborted."); + return 0; + } + } + + seg->chunk_size = chunk_size; + } + + if (!arg_count(cmd, yes_ARG) && + yes_no_prompt("Do you want to swap metadata of %s pool with metadata volume %s? [y/n]: ", + display_lvname(lv), + display_lvname(metadata_lv)) == 'n') { + log_error("Conversion aborted."); + return 0; + } + + if (!deactivate_lv(cmd, metadata_lv)) { + log_error("Aborting. Failed to deactivate %s.", + display_lvname(metadata_lv)); + return 0; + } + + if (!archive(vg)) + return_0; + + /* Swap names between old and new metadata LV */ + + if (!detach_pool_metadata_lv(seg, &prev_metadata_lv)) + return_0; + + swap_name = metadata_lv->name; + + if (!lv_rename_update(cmd, metadata_lv, "pvmove_tmeta", 0)) + return_0; + + /* Give the previous metadata LV the name of the LV replacing it. */ + + if (!lv_rename_update(cmd, prev_metadata_lv, swap_name, 0)) + return_0; + + /* Rename deactivated metadata LV to have _tmeta suffix */ + + if (!lv_rename_update(cmd, metadata_lv, meta_name, 0)) + return_0; + + if (!attach_pool_metadata_lv(seg, metadata_lv)) + return_0; + + if (!vg_write(vg) || !vg_commit(vg)) + return_0; + + backup(vg); + return 1; +} + /* * Create a new pool LV, using the lv arg as the data sub LV. * The metadata sub LV is either a new LV created here, or an @@ -3552,12 +3689,21 @@ static int _lvconvert_to_pool(struct cmd_context *cmd, return_0; } + if ((uint64_t)chunk_size > ((uint64_t)lv->le_count * vg->extent_size)) { + log_error("Pool data LV %s is too small (%s) for specified chunk size (%s).", + display_lvname(lv), + display_size(cmd, (uint64_t)lv->le_count * vg->extent_size), + display_size(cmd, chunk_size)); + return 0; + } + if (metadata_lv && (meta_extents > metadata_lv->le_count)) { - log_error("Logical volume %s is too small for metadata.", - display_lvname(metadata_lv)); + log_error("Pool metadata LV %s is too small (%s extents) for required metadata (%s extents).", + display_lvname(metadata_lv), metadata_lv->le_count, meta_extents); + return 0; } - log_warn("Pool metadata extents %u chunk_size %u", meta_extents, chunk_size); + log_verbose("Pool metadata extents %u chunk_size %u", meta_extents, chunk_size); /* @@ -5784,7 +5930,7 @@ static int _lvconvert_to_cache_vol_single(struct cmd_context *cmd, return ECMD_PROCESSED; -out: + out: return ECMD_FAILED; } @@ -5859,3 +6005,76 @@ int lvconvert_to_thin_with_external_cmd(struct cmd_context *cmd, int argc, char NULL, NULL, &_lvconvert_to_thin_with_external_single); } +static int _lvconvert_swap_pool_metadata_single(struct cmd_context *cmd, + struct logical_volume *lv, + struct processing_handle *handle) +{ + struct volume_group *vg = lv->vg; + struct logical_volume *metadata_lv; + const char *metadata_name; + + if (!(metadata_name = arg_str_value(cmd, poolmetadata_ARG, NULL))) + goto_out; + + if (!validate_lvname_param(cmd, &vg->name, &metadata_name)) + goto_out; + + if (!(metadata_lv = find_lv(vg, metadata_name))) { + log_error("Metadata LV %s not found.", metadata_name); + goto out; + } + + if (metadata_lv == lv) { + log_error("Can't use same LV for pool data and metadata LV %s.", + display_lvname(metadata_lv)); + goto out; + } + + if (!_lvconvert_swap_pool_metadata(cmd, lv, metadata_lv)) + goto_out; + + return ECMD_PROCESSED; + + out: + return ECMD_FAILED; +} + +int lvconvert_swap_pool_metadata_cmd(struct cmd_context *cmd, int argc, char **argv) +{ + return process_each_lv(cmd, 1, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, + NULL, NULL, &_lvconvert_swap_pool_metadata_single); +} + +#if 0 +int lvconvert_swap_pool_metadata_noarg_cmd(struct cmd_context *cmd, int argc, char **argv) +{ + struct command *new_command; + char *pool_name; + + switch (cmd->command->command_line_enum) { + case lvconvert_swap_thinpool_metadata_CMD: + pool_name = (char *)arg_str_value(cmd, thinpool_ARG, NULL); + break; + case lvconvert_swap_cachepool_metadata_CMD: + pool_name = (char *)arg_str_value(cmd, cachepool_ARG, NULL); + break; + default: + log_error(INTERNAL_ERROR "Unknown pool conversion."); + return 0; + }; + + new_command = get_command(lvconvert_swap_pool_metadata_CMD); + + log_debug("Changing command line id %s %d to standard form %s %d", + cmd->command->command_line_id, cmd->command->command_line_enum, + new_command->command_line_id, new_command->command_line_enum); + + /* Make the LV the first position arg. */ + + cmd->position_argv[0] = pool_name; + cmd->position_argc++; + cmd->command = new_command; + + return lvconvert_swap_pool_metadata_cmd(cmd, argc, argv); +} +#endif diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c index 390263c34..857147768 100644 --- a/tools/lvmcmdline.c +++ b/tools/lvmcmdline.c @@ -142,6 +142,8 @@ struct command_function command_functions[COMMAND_ID_COUNT] = { { lvconvert_to_cachepool_noarg_CMD, lvconvert_to_pool_noarg_cmd }, { lvconvert_to_thin_with_external_CMD, lvconvert_to_thin_with_external_cmd }, { lvconvert_to_cache_vol_CMD, lvconvert_to_cache_vol_cmd }, + { lvconvert_swap_pool_metadata_CMD, lvconvert_swap_pool_metadata_cmd }, + { lvconvert_swap_pool_metadata_deprecated_CMD, lvconvert_swap_pool_metadata_cmd }, }; #if 0 @@ -155,7 +157,6 @@ struct command_function command_functions[COMMAND_ID_COUNT] = { /* utilities for creating/maintaining thin and cache objects. */ { lvconvert_split_and_keep_cachepool_CMD, lvconvert_split_and_keep_cachepool_fn }, { lvconvert_split_and_delete_cachepool_CMD, lvconvert_split_and_delete_cachepool_fn }, - { lvconvert_swap_pool_metadata_CMD, lvconvert_swap_pool_metadata_fn }, /* directed to one of the other merges (snap,thin,mirror) when all are implemented */ { lvconvert_merge_CMD, lvconvert_merge_fn }, diff --git a/tools/tools.h b/tools/tools.h index 2ed3bb0bc..de672b68a 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -264,5 +264,6 @@ int lvconvert_to_pool_cmd(struct cmd_context *cmd, int argc, char **argv); int lvconvert_to_pool_noarg_cmd(struct cmd_context *cmd, int argc, char **argv); int lvconvert_to_cache_vol_cmd(struct cmd_context *cmd, int argc, char **argv); int lvconvert_to_thin_with_external_cmd(struct cmd_context *cmd, int argc, char **argv); +int lvconvert_swap_pool_metadata_cmd(struct cmd_context *cmd, int argc, char **argv); #endif |