diff --git a/test/shell/lvmetad-pvscan-autoactivation-polling.sh b/test/shell/lvmetad-pvscan-autoactivation-polling.sh index 34139dfcd..58afbedfc 100644 --- a/test/shell/lvmetad-pvscan-autoactivation-polling.sh +++ b/test/shell/lvmetad-pvscan-autoactivation-polling.sh @@ -54,7 +54,7 @@ mkdir test_mnt setup_merge_ $vg1 $lv1 mount "$(lvdev_ $vg1 $lv1)" test_mnt -lvconvert --merge $vg1/$(snap_lv_name_ $lv1) +lvconvert --mergesnapshot $vg1/$(snap_lv_name_ $lv1) umount test_mnt vgchange -an $vg1 diff --git a/test/shell/snapshot-merge-stack.sh b/test/shell/snapshot-merge-stack.sh index 3ee9eaa4e..55477f6a3 100644 --- a/test/shell/snapshot-merge-stack.sh +++ b/test/shell/snapshot-merge-stack.sh @@ -34,7 +34,7 @@ snap_and_merge() { SLEEP_PID=$! # initiate background merge - lvconvert -b --merge $vg/$lv2 + lvconvert -b --mergesnapshot $vg/$lv2 lvs -a -o+lv_merging,lv_merge_failed $vg kill $SLEEP_PID diff --git a/test/shell/snapshot-merge.sh b/test/shell/snapshot-merge.sh index a301d229e..0d3e1cb75 100644 --- a/test/shell/snapshot-merge.sh +++ b/test/shell/snapshot-merge.sh @@ -51,15 +51,15 @@ mkdir test_mnt # test full merge of a single LV setup_merge_ $vg $lv1 -# make sure lvconvert --merge requires explicit LV listing -not lvconvert --merge -lvconvert --merge $vg/$(snap_lv_name_ $lv1) +# make sure lvconvert --mergesnapshot requires explicit LV listing +not lvconvert --mergesnapshot +lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1) lvremove -f $vg/$lv1 # test that an actively merging snapshot may not be removed setup_merge_ $vg $lv1 -lvconvert -i+100 --merge --background $vg/$(snap_lv_name_ $lv1) +lvconvert -i+100 --mergesnapshot --background $vg/$(snap_lv_name_ $lv1) not lvremove -f $vg/$(snap_lv_name_ $lv1) lvremove -f $vg/$lv1 @@ -67,7 +67,7 @@ lvremove -f $vg/$lv1 # "onactivate merge" test setup_merge_ $vg $lv1 mount "$(lvdev_ $vg $lv1)" test_mnt -lvconvert --merge $vg/$(snap_lv_name_ $lv1) +lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1) # -- refresh LV while FS is still mounted (merge must not start), # verify 'snapshot-origin' target is still being used lvchange --refresh $vg/$lv1 @@ -88,7 +88,7 @@ lvremove -f $vg/$lv1 # to make sure preload of origin's metadata is _not_ performed setup_merge_ $vg $lv1 mount "$(lvdev_ $vg $lv1)" test_mnt -lvconvert --merge $vg/$(snap_lv_name_ $lv1) +lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1) # -- refresh LV while FS is still mounted (merge must not start), # verify 'snapshot-origin' target is still being used lvchange --refresh $vg/$lv1 @@ -99,7 +99,7 @@ lvremove -f $vg/$lv1 # test multiple snapshot merge; tests copy out that is driven by merge setup_merge_ $vg $lv1 1 -lvconvert --merge $vg/$(snap_lv_name_ $lv1) +lvconvert --mergesnapshot $vg/$(snap_lv_name_ $lv1) lvremove -f $vg/$lv1 @@ -108,7 +108,7 @@ setup_merge_ $vg $lv1 setup_merge_ $vg $lv2 lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv1) lvchange --addtag this_is_a_test $vg/$(snap_lv_name_ $lv2) -lvconvert --merge @this_is_a_test +lvconvert --mergesnapshot @this_is_a_test lvs $vg | tee out not grep $(snap_lv_name_ $lv1) out not grep $(snap_lv_name_ $lv2) out diff --git a/test/shell/thin-merge.sh b/test/shell/thin-merge.sh index f044f574a..b3ad00760 100644 --- a/test/shell/thin-merge.sh +++ b/test/shell/thin-merge.sh @@ -102,7 +102,7 @@ lvcreate -s -n snap $vg/$lv1 lvcreate -s -L10 -n oldsnapof_${lv1} $vg/$lv1 not lvconvert --merge $vg/snap $MKFS "$DM_DEV_DIR/$vg/oldsnapof_${lv1}" -lvconvert --merge $vg/oldsnapof_${lv1} +lvconvert --mergesnapshot $vg/oldsnapof_${lv1} fsck -n "$DM_DEV_DIR/$vg/$lv1" check lv_not_exists $vg oldsnapof_${lv1} # Add old snapshot to thin snapshot diff --git a/tools/args.h b/tools/args.h index 8f5f0ec5a..a01531888 100644 --- a/tools/args.h +++ b/tools/args.h @@ -60,6 +60,7 @@ arg(locktype_ARG, '\0', "locktype", locktype_VAL, 0, 0) arg(logonly_ARG, '\0', "logonly", 0, 0, 0) arg(maxrecoveryrate_ARG, '\0', "maxrecoveryrate", sizekb_VAL, 0, 0) arg(merge_ARG, '\0', "merge", 0, 0, 0) +arg(mergesnapshot_ARG, '\0', "mergesnapshot", 0, 0, 0) arg(mergedconfig_ARG, '\0', "mergedconfig", 0, 0, 0) arg(metadatacopies_ARG, '\0', "metadatacopies", metadatacopies_VAL, 0, 0) arg(metadataignore_ARG, '\0', "metadataignore", bool_VAL, 0, 0) diff --git a/tools/command-lines.in b/tools/command-lines.in index d077cc6d6..defe45102 100644 --- a/tools/command-lines.in +++ b/tools/command-lines.in @@ -476,9 +476,6 @@ FLAGS: SECONDARY_SYNTAX --- -# lvconvert utilities related to snapshots and repair. -# Create a new command set for these and migrate them out of lvconvert? - # FIXME: lvconvert --merge is an extremely ambiguous command. # It can do very different operations, but which one depends # on knowing the LV type. So, the command doesn't know what @@ -509,29 +506,58 @@ ID: lvconvert_merge DESC: Merge LV that was previously split from a mirror. DESC: Merge thin LV into its origin LV. DESC: Merge COW snapshot LV into its origin. -RULE: all not lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow +RULE: all not lv_is_locked lv_is_pvmove lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow --- -# FIXME: by using two different positional args, this is the -# single violation of the standard method of using process_each_lv(). -# Before calling process_each, it steals the first positional arg -# and adjusts argv/argc so it's not seen by process_each. +# lvconvert snapshot-related utilities +# Create a new command set for these and migrate them out of lvconvert? + +lvconvert --mergesnapshot LV_snapshot ... +OO: --background, --interval Number, OO_LVCONVERT +ID: lvconvert_merge_snapshot +DESC: Merge LV that was previously split from a mirror. +RULE: all not lv_is_locked lv_is_pvmove lv_is_merging_origin lv_is_virtual_origin lv_is_external_origin lv_is_merging_cow + +--- + +lvconvert --splitsnapshot LV_snapshot +OO: OO_LVCONVERT +ID: lvconvert_split_cow_snapshot +DESC: Separate a COW snapshot from its origin LV. +RULE: all not lv_is_locked lv_is_pvmove lv_is_origin lv_is_external_origin lv_is_merging_cow + +--- + +# NB: an unsual use of position args here, the first pos arg +# (will become origin LV) is not passed to process_each, +# the second pos arg (will become cow LV) is given to +# process_each. Because the first pos LV is not handled +# by process_each_lv, it cannot be checked against this +# command def, so a specific LV type in the first pos +# will not be checked. # alternate form of lvconvert --snapshot -lvconvert --type snapshot LV_linear_striped_raid LV_snapshot +lvconvert --type snapshot LV LV_linear OO: --snapshot, --chunksize SizeKB, --zero Bool, OO_LVCONVERT ID: lvconvert_combine_split_snapshot -DESC: Combine LV with a previously split snapshot LV. +DESC: Combine a former COW snapshot (second arg) with a former +DESC: origin LV (first arg) to reverse a splitsnapshot command. FLAGS: SECONDARY_SYNTAX +RULE: all not lv_is_locked lv_is_pvmove -lvconvert --snapshot LV_linear_striped_raid LV_snapshot +lvconvert --snapshot LV LV_linear OO: --type snapshot, --chunksize SizeKB, --zero Bool, OO_LVCONVERT ID: lvconvert_combine_split_snapshot -DESC: Combine LV with a previously split snapshot LV. +DESC: Combine a former COW snapshot (second arg) with a former +DESC: origin LV (first arg) to reverse a splitsnapshot command. +RULE: all not lv_is_locked lv_is_pvmove --- +# lvconvert repair/replace utilitiles +# Create a new command set for these and migrate them out of lvconvert? + # FIXME: use specific option names to distinguish these two # very different commands, e.g. # @@ -552,21 +578,14 @@ OP: PV ... ID: lvconvert_repair_pvs_or_thinpool DESC: Replace failed PVs in a raid or mirror LV. DESC: Repair a thin pool. - ---- +RULE: all not lv_is_locked lv_is_pvmove lvconvert --replace PV LV_raid OO: OO_LVCONVERT OP: PV ... ID: lvconvert_replace_pv DESC: Replace specific PV(s) in a raid* LV with another PV. - ---- - -lvconvert --splitsnapshot LV_snapshot -OO: OO_LVCONVERT -ID: lvconvert_split_cow_snapshot -DESC: Separate a COW snapshot from its origin LV. +RULE: all not lv_is_locked lv_is_pvmove --- diff --git a/tools/lvconvert.c b/tools/lvconvert.c index c207b02a3..1680f4ad8 100644 --- a/tools/lvconvert.c +++ b/tools/lvconvert.c @@ -1992,37 +1992,16 @@ try_new_takeover_or_reshape: return 0; } -static int _lvconvert_splitsnapshot(struct cmd_context *cmd, struct logical_volume *cow, - struct lvconvert_params *lp) +static int _lvconvert_splitsnapshot(struct cmd_context *cmd, struct logical_volume *cow) { struct volume_group *vg = cow->vg; const char *cow_name = display_lvname(cow); - if (!lv_is_cow(cow)) { - log_error("%s is not a snapshot.", cow_name); - return 0; - } - - if (lv_is_origin(cow) || lv_is_external_origin(cow)) { - log_error("Unable to split LV %s that is a snapshot origin.", cow_name); - return 0; - } - - if (lv_is_merging_cow(cow)) { - log_error("Unable to split off snapshot %s being merged into its origin.", cow_name); - return 0; - } - if (lv_is_virtual_origin(origin_from_cow(cow))) { log_error("Unable to split off snapshot %s with virtual origin.", cow_name); return 0; } - if (lv_is_thin_pool(cow) || lv_is_pool_metadata_spare(cow)) { - log_error("Unable to split off LV %s needed by thin volume(s).", cow_name); - return 0; - } - if (!(vg->fid->fmt->features & FMT_MDAS)) { log_error("Unable to split off snapshot %s using old LVM1-style metadata.", cow_name); return 0; @@ -2034,19 +2013,12 @@ static int _lvconvert_splitsnapshot(struct cmd_context *cmd, struct logical_volu return 0; } - if (!vg_check_status(vg, LVM_WRITE)) - return_0; - - if (lv_is_pvmove(cow) || lv_is_mirror_type(cow) || lv_is_raid_type(cow) || lv_is_thin_type(cow)) { - log_error("LV %s type is unsupported with --splitsnapshot.", cow_name); - return 0; - } - if (lv_is_active_locally(cow)) { if (!lv_check_not_in_use(cow, 1)) return_0; - if ((lp->force == PROMPT) && !lp->yes && + if ((arg_count(cmd, force_ARG) == PROMPT) && + !arg_count(cmd, yes_ARG) && lv_is_visible(cow) && lv_is_active(cow)) { if (yes_no_prompt("Do you really want to split off active " @@ -2165,36 +2137,16 @@ static int _lvconvert_uncache(struct cmd_context *cmd, static int _lvconvert_snapshot(struct cmd_context *cmd, struct logical_volume *lv, - struct lvconvert_params *lp) + const char *origin_name) { struct logical_volume *org; const char *snap_name = display_lvname(lv); + uint32_t chunk_size; + int zero; - if (lv_is_cache_type(lv)) { - log_error("Snapshots are not yet supported with cache type LVs %s.", - snap_name); - return 0; - } - - if (lv_is_mirrored(lv)) { - log_error("Unable to convert mirrored LV %s into a snapshot.", snap_name); - return 0; - } - - if (lv_is_origin(lv)) { - /* Unsupported stack */ - log_error("Unable to convert origin %s into a snapshot.", snap_name); - return 0; - } - - if (lv_is_pool(lv)) { - log_error("Unable to convert pool LVs %s into a snapshot.", snap_name); - return 0; - } - - if (!(org = find_lv(lv->vg, lp->origin_name))) { + if (!(org = find_lv(lv->vg, origin_name))) { log_error("Couldn't find origin volume %s in Volume group %s.", - lp->origin_name, lv->vg->name); + origin_name, lv->vg->name); return 0; } @@ -2203,22 +2155,36 @@ static int _lvconvert_snapshot(struct cmd_context *cmd, return 0; } - if (!cow_has_min_chunks(lv->vg, lv->le_count, lp->chunk_size)) + chunk_size = arg_uint_value(cmd, chunksize_ARG, 8); + if (chunk_size < 8 || chunk_size > 1024 || !is_power_of_2(chunk_size)) { + log_error("Chunk size must be a power of 2 in the range 4K to 512K."); + return 0; + } + log_verbose("Setting chunk size to %s.", display_size(cmd, chunk_size)); + + if (!cow_has_min_chunks(lv->vg, lv->le_count, chunk_size)) return_0; - if (lv_is_locked(org) || - lv_is_cache_type(org) || + /* + * check_lv_rules() checks cannot be done via command definition + * rules because this LV is not processed by process_each_lv. + */ + if (lv_is_locked(org) || lv_is_pvmove(org)) { + log_error("Unable to use LV %s as snapshot origin: LV is %s.", + display_lvname(lv), lv_is_locked(org) ? "locked" : "pvmove"); + return 0; + } + + /* + * check_lv_types() checks cannot be done via command definition + * LV_foo specification because this LV is not processed by process_each_lv. + */ + if (lv_is_cache_type(org) || lv_is_thin_type(org) || - lv_is_pvmove(org) || lv_is_mirrored(org) || lv_is_cow(org)) { - log_error("Unable to convert an LV into a snapshot of a %s LV.", - lv_is_locked(org) ? "locked" : - lv_is_cache_type(org) ? "cache type" : - lv_is_thin_type(org) ? "thin type" : - lv_is_pvmove(org) ? "pvmove" : - lv_is_mirrored(org) ? "mirrored" : - "snapshot"); + log_error("Unable to use LV %s as snapshot origin: invald LV type.", + display_lvname(lv)); return 0; } @@ -2226,7 +2192,7 @@ static int _lvconvert_snapshot(struct cmd_context *cmd, snap_name); log_warn("THIS WILL DESTROY CONTENT OF LOGICAL VOLUME (filesystem etc.)"); - if (!lp->yes && + if (!arg_count(cmd, yes_ARG) && yes_no_prompt("Do you really want to convert %s? [y/n]: ", snap_name) == 'n') { log_error("Conversion aborted."); @@ -2238,7 +2204,12 @@ static int _lvconvert_snapshot(struct cmd_context *cmd, return 0; } - if (!lp->zero || !(lv->status & LVM_WRITE)) + if (first_seg(lv)->segtype->flags & SEG_CANNOT_BE_ZEROED) + zero = 0; + else + zero = arg_int_value(cmd, zero_ARG, 1); + + if (!zero || !(lv->status & LVM_WRITE)) log_warn("WARNING: %s not zeroed.", snap_name); else { lv->status |= LV_TEMPORARY; @@ -2258,7 +2229,7 @@ static int _lvconvert_snapshot(struct cmd_context *cmd, if (!archive(lv->vg)) return_0; - if (!vg_add_snapshot(org, lv, NULL, org->le_count, lp->chunk_size)) { + if (!vg_add_snapshot(org, lv, NULL, org->le_count, chunk_size)) { log_error("Couldn't create snapshot."); return 0; } @@ -2274,7 +2245,7 @@ static int _lvconvert_snapshot(struct cmd_context *cmd, static int _lvconvert_merge_old_snapshot(struct cmd_context *cmd, struct logical_volume *lv, - struct lvconvert_params *lp) + struct logical_volume **lv_to_poll) { int merge_on_activate = 0; struct logical_volume *origin = origin_from_cow(lv); @@ -2282,29 +2253,6 @@ static int _lvconvert_merge_old_snapshot(struct cmd_context *cmd, struct lvinfo info; dm_percent_t snap_percent; - /* Check if merge is possible */ - if (!lv_is_cow(lv)) { - log_error("\"%s\" is not a mergeable logical volume.", - lv->name); - return 0; - } - - if (lv_is_merging_cow(lv)) { - log_error("Snapshot %s is already merging.", lv->name); - return 0; - } - - if (lv_is_merging_origin(origin)) { - log_error("Snapshot %s is already merging into the origin.", - find_snapshot(origin)->cow->name); - return 0; - } - - if (lv_is_virtual_origin(origin)) { - log_error("Snapshot %s has virtual origin.", lv->name); - return 0; - } - if (lv_is_external_origin(origin_from_cow(lv))) { log_error("Cannot merge snapshot \"%s\" into " "the read-only external origin \"%s\".", @@ -2368,8 +2316,7 @@ static int _lvconvert_merge_old_snapshot(struct cmd_context *cmd, if (!lv_update_and_reload(origin)) return_0; - lp->need_polling = 1; - lp->lv_to_poll = origin; + *lv_to_poll = origin; } if (merge_on_activate) @@ -3436,7 +3383,7 @@ static int _lvconvert_cache(struct cmd_context *cmd, static int _convert_cow_snapshot_splitsnapshot(struct cmd_context *cmd, struct logical_volume *lv, struct lvconvert_params *lp) { - return _lvconvert_splitsnapshot(cmd, lv, lp); + return _lvconvert_splitsnapshot(cmd, lv); } /* @@ -3446,7 +3393,7 @@ static int _convert_cow_snapshot_splitsnapshot(struct cmd_context *cmd, struct l static int _convert_cow_snapshot_merge(struct cmd_context *cmd, struct logical_volume *lv, struct lvconvert_params *lp) { - return _lvconvert_merge_old_snapshot(cmd, lv, lp); + /* return _lvconvert_merge_old_snapshot(cmd, lv, lp); */ } /* @@ -3740,7 +3687,7 @@ static int _convert_raid_merge(struct cmd_context *cmd, struct logical_volume *l static int _convert_raid_snapshot(struct cmd_context *cmd, struct logical_volume *lv, struct lvconvert_params *lp) { - return _lvconvert_snapshot(cmd, lv, lp); + return _lvconvert_snapshot(cmd, lv, lp->origin_name); } /* @@ -3879,7 +3826,7 @@ static int _convert_striped_merge(struct cmd_context *cmd, struct logical_volume static int _convert_striped_snapshot(struct cmd_context *cmd, struct logical_volume *lv, struct lvconvert_params *lp) { - return _lvconvert_snapshot(cmd, lv, lp); + return _lvconvert_snapshot(cmd, lv, lp->origin_name); } /* @@ -4620,6 +4567,11 @@ struct lvconvert_result { struct dm_list poll_idls; }; + +/* + * repair-related lvconvert utilities + */ + static int _lvconvert_repair_pvs_mirror(struct cmd_context *cmd, struct logical_volume *lv, struct processing_handle *handle, struct dm_list *use_pvh) @@ -4817,11 +4769,12 @@ int lvconvert_repair_pvs_or_thinpool_cmd(struct cmd_context *cmd, int argc, char init_ignore_suspended_devices(saved_ignore_suspended_devices); if (lr.need_polling) { - dm_list_iterate_items(idl, &lr.poll_idls) + dm_list_iterate_items(idl, &lr.poll_idls) { poll_ret = _lvconvert_poll_by_id(cmd, idl->id, arg_is_set(cmd, background_ARG), 0, 0); - if (poll_ret > ret) - ret = poll_ret; + if (poll_ret > ret) + ret = poll_ret; + } } destroy_processing_handle(cmd, handle); @@ -4895,3 +4848,152 @@ int lvconvert_replace_pv_cmd(struct cmd_context *cmd, int argc, char **argv) return ret; } + +/* + * snapshot-related lvconvert utilities + */ + + +/* + * Merge a COW snapshot LV into its origin. + */ + +static int _lvconvert_merge_snapshot_single(struct cmd_context *cmd, + struct logical_volume *lv, + struct processing_handle *handle) +{ + struct lvconvert_result *lr = (struct lvconvert_result *) handle->custom_handle; + struct logical_volume *lv_to_poll = NULL; + struct convert_poll_id_list *idl; + + if (!_lvconvert_merge_old_snapshot(cmd, lv, &lv_to_poll)) + return_ECMD_FAILED; + + if (lv_to_poll) { + if (!(idl = _convert_poll_id_list_create(cmd, lv_to_poll))) + return_ECMD_FAILED; + dm_list_add(&lr->poll_idls, &idl->list); + lr->need_polling = 1; + } + + return ECMD_PROCESSED; +} + +int lvconvert_merge_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv) +{ + struct processing_handle *handle; + struct lvconvert_result lr = { 0 }; + struct convert_poll_id_list *idl; + int ret, poll_ret; + + dm_list_init(&lr.poll_idls); + + if (!(handle = init_processing_handle(cmd, NULL))) { + log_error("Failed to initialize processing handle."); + return ECMD_FAILED; + } + + handle->custom_handle = &lr; + + ret = process_each_lv(cmd, cmd->position_argc, cmd->position_argv, NULL, NULL, READ_FOR_UPDATE, + handle, NULL, &_lvconvert_merge_snapshot_single); + + if (lr.need_polling) { + dm_list_iterate_items(idl, &lr.poll_idls) { + poll_ret = _lvconvert_poll_by_id(cmd, idl->id, + arg_is_set(cmd, background_ARG), 1, 0); + if (poll_ret > ret) + ret = poll_ret; + } + } + + destroy_processing_handle(cmd, handle); + + return ret; +} + +/* + * Separate a COW snapshot from its origin. + * + * lvconvert --splitsnapshot LV_snapshot + * lvconvert_split_cow_snapshot + */ + +static int _lvconvert_split_snapshot_single(struct cmd_context *cmd, + struct logical_volume *lv, + struct processing_handle *handle) +{ + if (!_lvconvert_splitsnapshot(cmd, lv)) + return_ECMD_FAILED; + + return ECMD_PROCESSED; +} + +int lvconvert_split_snapshot_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_split_snapshot_single); +} + +/* + * Combine two LVs that were once an origin/cow pair of LVs, were then + * separated with --splitsnapshot, and now with this command are combined again + * into the origin/cow pair. + * + * This is an obscure command that has little to no real uses. + * + * The command has unusual handling of position args. The first position arg + * will become the origin LV, and is not processed by process_each_lv. The + * second position arg will become the cow LV and is processed by + * process_each_lv. + * + * The single function can grab the origin LV from position_argv[0]. + * + * begin with an ordinary LV foo: + * lvcreate -n foo -L 1 vg + * + * create a cow snapshot of foo named foosnap: + * lvcreate -s -L 1 -n foosnap vg/foo + * + * now, foo is an "origin LV" and foosnap is a "cow LV" + * (foosnap matches LV_snapshot aka lv_is_cow) + * + * split the two LVs apart: + * lvconvert --splitsnapshot vg/foosnap + * + * now, foo is *not* an origin LV and foosnap is *not* a cow LV + * (foosnap does not match LV_snapshot) + * + * now, combine the two LVs again: + * lvconvert --snapshot vg/foo vg/foosnap + * + * after this, foosnap will match LV_snapshot again. + * + * FIXME: when splitsnapshot is run, the previous cow LV should be + * flagged in the metadata somehow, and then that flag should be + * required here. As it is now, the first and second args + * (origin and cow) can be swapped and nothing catches it. + */ + +static int _lvconvert_combine_split_snapshot_single(struct cmd_context *cmd, + struct logical_volume *lv, + struct processing_handle *handle) +{ + const char *origin_name = cmd->position_argv[0]; + + /* If origin_name includes VG name, the VG name is removed. */ + if (!validate_lvname_param(cmd, &lv->vg->name, &origin_name)) + return_ECMD_FAILED; + + if (!_lvconvert_snapshot(cmd, lv, origin_name)) + return_ECMD_FAILED; + + return ECMD_PROCESSED; +} + +int lvconvert_combine_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv) +{ + return process_each_lv(cmd, 1, cmd->position_argv + 1, NULL, NULL, READ_FOR_UPDATE, + NULL, NULL, &_lvconvert_combine_split_snapshot_single); +} + diff --git a/tools/lvmcmdline.c b/tools/lvmcmdline.c index d1c42a13c..e00a13880 100644 --- a/tools/lvmcmdline.c +++ b/tools/lvmcmdline.c @@ -123,9 +123,14 @@ struct command_function command_functions[COMMAND_ID_COUNT] = { { lvchange_poll_CMD, lvchange_monitor_poll_cmd }, { lvchange_persistent_CMD, lvchange_persistent_cmd }, - /* lvconvert utilities related to snapshots and repair. */ + /* lvconvert utilities related to repair. */ { lvconvert_repair_pvs_or_thinpool_CMD, lvconvert_repair_pvs_or_thinpool_cmd }, { lvconvert_replace_pv_CMD, lvconvert_replace_pv_cmd }, + + /* lvconvert utilities related to snapshots. */ + { lvconvert_split_cow_snapshot_CMD, lvconvert_split_snapshot_cmd }, + { lvconvert_merge_snapshot_CMD, lvconvert_merge_snapshot_cmd }, + { lvconvert_combine_split_snapshot_CMD, lvconvert_combine_split_snapshot_cmd }, }; #if 0 /* all raid-related type conversions */ @@ -147,11 +152,9 @@ struct command_function command_functions[COMMAND_ID_COUNT] = { { lvconvert_split_and_delete_cachepool_CMD, lvconvert_split_and_delete_cachepool_fn }, { lvconvert_swap_pool_metadata_CMD, lvconvert_swap_pool_metadata_fn }, - /* utilities related to snapshots and repair. */ + /* other misc. */ { lvconvert_merge_CMD, lvconvert_merge_fn }, - { lvconvert_combine_split_snapshot_CMD, lvconvert_combine_split_snapshot_fn }, - { lvconvert_split_cow_snapshot_CMD, lvconvert_split_cow_snapshot_fn }, { lvconvert_poll_start_CMD, lvconvert_poll_start_fn }, #endif diff --git a/tools/tools.h b/tools/tools.h index ec4789d22..bff562b17 100644 --- a/tools/tools.h +++ b/tools/tools.h @@ -253,4 +253,8 @@ int lvchange_persistent_cmd(struct cmd_context *cmd, int argc, char **argv); int lvconvert_repair_pvs_or_thinpool_cmd(struct cmd_context *cmd, int argc, char **argv); int lvconvert_replace_pv_cmd(struct cmd_context *cmd, int argc, char **argv); +int lvconvert_merge_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv); +int lvconvert_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv); +int lvconvert_combine_split_snapshot_cmd(struct cmd_context *cmd, int argc, char **argv); + #endif