From: Mykola Golub Date: Wed, 22 Nov 2017 13:50:19 +0000 (+0200) Subject: rbd: CLI to migrate images X-Git-Tag: v14.0.1~590^2~20 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=7f27f1975320f532882289db352465f8a5cb0220;p=ceph.git rbd: CLI to migrate images Signed-off-by: Mykola Golub --- diff --git a/doc/man/8/rbd.rst b/doc/man/8/rbd.rst index 40f986ab9d50..012825489358 100644 --- a/doc/man/8/rbd.rst +++ b/doc/man/8/rbd.rst @@ -396,6 +396,29 @@ Commands 'rbd merge-diff first second - | rbd merge-diff - third result'. Note this command currently only support the source incremental diff with stripe_count == 1 +:command:`migration abort` *image-spec* + Cancel image migration. This step may be run after successful or + failed migration prepare or migration execute steps and returns the + image to its initial (before migration) state. All modifications to + the destination image are lost. + +:command:`migration commit` *image-spec* + Commit image migration. This step is run after a successful migration + prepare and migration execute steps and removes the source image data. + +:command:`migration execute` *image-spec* + Execute image migration. This step is run after a successful migration + prepare step and copies image data to the destination. + +:command:`migration prepare` [--order *order*] [--object-size *object-size*] [--image-feature *image-feature*] [--image-shared] [--stripe-unit *stripe-unit*] [--stripe-count *stripe-count*] [--data-pool *data-pool*] *src-image-spec* [*dest-image-spec*] + Prepare image migration. This is the first step when migrating an + image, i.e. changing the image location, format or other + parameters that can't be changed dynamically. The destination can + match the source, and in this case *dest-image-spec* can be omitted. + After this step the source image is set as a parent of the + destination image, and the image is accessible in copy-on-write mode + by its destination spec. + :command:`mirror image demote` *image-spec* Demote a primary image to non-primary for RBD mirroring. diff --git a/qa/workunits/rbd/cli_generic.sh b/qa/workunits/rbd/cli_generic.sh index 0ecb75186bd6..95adbbbacf10 100755 --- a/qa/workunits/rbd/cli_generic.sh +++ b/qa/workunits/rbd/cli_generic.sh @@ -1,4 +1,7 @@ -#!/bin/sh -ex +#!/usr/bin/env bash +set -ex + +. $(dirname $0)/../../standalone/ceph-helpers.sh export RBD_FORCE_ALLOW_V1=1 @@ -655,10 +658,105 @@ test_namespace() { rbd namespace remove rbd test2 } +get_migration_state() { + local image=$1 + + rbd --format xml status $image | + $XMLSTARLET sel -t -v '//status/migration/state' +} + +test_migration() { + echo "testing migration..." + remove_images + rados mkpool rbd2 + rbd pool init rbd2 + + # Convert to new format + rbd create --image-format 1 -s 128M test1 + rbd info test1 | grep 'format: 1' + rbd migration prepare test1 --image-format 2 + test "$(get_migration_state test1)" = prepared + rbd info test1 | grep 'format: 2' + rbd rm test1 && exit 1 || true + rbd migration execute test1 + test "$(get_migration_state test1)" = executed + rbd migration commit test1 + get_migration_state test1 && exit 1 || true + + # Enable layering (and some other features) + rbd info test1 | grep 'features: .*layering' && exit 1 || true + rbd migration prepare test1 --image-feature \ + layering,exclusive-lock,object-map,fast-diff,deep-flatten + rbd info test1 | grep 'features: .*layering' + rbd migration execute test1 + rbd migration commit test1 + + # Migration to other pool + rbd migration prepare test1 rbd2/test1 + test "$(get_migration_state rbd2/test1)" = prepared + rbd ls | wc -l | grep '^0$' + rbd -p rbd2 ls | grep test1 + rbd migration execute test1 + test "$(get_migration_state rbd2/test1)" = executed + rbd rm rbd2/test1 && exit 1 || true + rbd migration commit test1 + + # Enable data pool + rbd create -s 128M test1 + rbd migration prepare test1 --data-pool rbd2 + rbd info test1 | grep 'data_pool: rbd2' + rbd migration execute test1 + rbd migration commit test1 + + for format in 1 2; do + # Abort migration after successful prepare + rbd create -s 128M --image-format ${format} test2 + rbd migration prepare test2 --data-pool rbd2 + rbd bench --io-type write --io-size 1024 --io-total 1024 test2 + rbd migration abort test2 + rbd bench --io-type write --io-size 1024 --io-total 1024 test2 + rbd rm test2 + + # Abort migration after successful execute + rbd create -s 128M --image-format ${format} test2 + rbd migration prepare test2 --data-pool rbd2 + rbd bench --io-type write --io-size 1024 --io-total 1024 test2 + rbd migration execute test2 + rbd migration abort test2 + rbd bench --io-type write --io-size 1024 --io-total 1024 test2 + rbd rm test2 + + # Migration is automatically aborted if prepare failed + rbd create -s 128M --image-format ${format} test2 + rbd migration prepare test2 --data-pool INVALID_DATA_POOL && exit 1 || true + rbd bench --io-type write --io-size 1024 --io-total 1024 test2 + rbd rm test2 + + # Abort migration to other pool + rbd create -s 128M --image-format ${format} test2 + rbd migration prepare test2 rbd2/test2 + rbd bench --io-type write --io-size 1024 --io-total 1024 rbd2/test2 + rbd migration abort test2 + rbd bench --io-type write --io-size 1024 --io-total 1024 test2 + rbd rm test2 + + # The same but abort using destination image + rbd create -s 128M --image-format ${format} test2 + rbd migration prepare test2 rbd2/test2 + rbd migration abort rbd2/test2 + rbd bench --io-type write --io-size 1024 --io-total 1024 test2 + rbd rm test2 + done + + remove_images + rados rmpool rbd2 rbd2 --yes-i-really-really-mean-it +} + test_pool_image_args test_rename test_ls test_remove +test_migration RBD_CREATE_ARGS="" test_others test_locking diff --git a/src/test/cli/rbd/help.t b/src/test/cli/rbd/help.t index e7d6aff7b621..bbbbcaf6751f 100644 --- a/src/test/cli/rbd/help.t +++ b/src/test/cli/rbd/help.t @@ -59,6 +59,10 @@ lock list (lock ls) Show locks held on an image. lock remove (lock rm) Release a lock on an image. merge-diff Merge two diff exports together. + migration abort Cancel interrupted image migration. + migration commit Commit image migration. + migration execute Execute image migration. + migration prepare Prepare image migration. mirror image demote Demote an image to non-primary for RBD mirroring. mirror image disable Disable RBD mirroring for an image. @@ -1209,6 +1213,112 @@ --path arg path to merged diff (or '-' for stdout) --no-progress disable progress output + rbd help migration abort + usage: rbd migration abort [--pool ] [--namespace ] + [--image ] [--no-progress] + + + Cancel interrupted image migration. + + Positional arguments + image specification + (example: [/[/]]) + + Optional arguments + -p [ --pool ] arg pool name + --namespace arg namespace name + --image arg image name + --no-progress disable progress output + + rbd help migration commit + usage: rbd migration commit [--pool ] [--namespace ] + [--image ] [--no-progress] + + + Commit image migration. + + Positional arguments + image specification + (example: [/[/]]) + + Optional arguments + -p [ --pool ] arg pool name + --namespace arg namespace name + --image arg image name + --no-progress disable progress output + + rbd help migration execute + usage: rbd migration execute [--pool ] [--namespace ] + [--image ] [--no-progress] + + + Execute image migration. + + Positional arguments + image specification + (example: [/[/]]) + + Optional arguments + -p [ --pool ] arg pool name + --namespace arg namespace name + --image arg image name + --no-progress disable progress output + + rbd help migration prepare + usage: rbd migration prepare [--pool ] [--namespace ] + [--image ] [--dest-pool ] + [--dest-namespace ] + [--dest ] [--image-format ] + [--new-format] [--order ] + [--object-size ] + [--image-feature ] + [--image-shared] [--stripe-unit ] + [--stripe-count ] + [--data-pool ] + [--journal-splay-width ] + [--journal-object-size ] + [--journal-pool ] [--flatten] + + + Prepare image migration. + + Positional arguments + source image specification + (example: + [/[/]]) + destination image specification + (example: + [/[/]]) + + Optional arguments + -p [ --pool ] arg source pool name + --namespace arg source namespace name + --image arg source image name + --dest-pool arg destination pool name + --dest-namespace arg destination namespace name + --dest arg destination image name + --image-format arg image format [1 (deprecated) or 2] + --new-format use image format 2 + (deprecated) + --order arg object order [12 <= order <= 25] + --object-size arg object size in B/K/M [4K <= object size <= 32M] + --image-feature arg image features + [layering(+), exclusive-lock(+*), object-map(+*), + deep-flatten(+-), journaling(*)] + --image-shared shared image + --stripe-unit arg stripe unit in B/K/M + --stripe-count arg stripe count + --data-pool arg data pool + --journal-splay-width arg number of active journal objects + --journal-object-size arg size of journal objects + --journal-pool arg pool for journal objects + --flatten fill clone with parent data (make it independent) + + Image Features: + (*) supports enabling/disabling on existing images + (-) supports disabling-only on existing images + (+) enabled by default for new images if features not specified + rbd help mirror image demote usage: rbd mirror image demote [--pool ] [--namespace ] [--image ] diff --git a/src/tools/rbd/CMakeLists.txt b/src/tools/rbd/CMakeLists.txt index 17154505ef64..55bc116d9c99 100644 --- a/src/tools/rbd/CMakeLists.txt +++ b/src/tools/rbd/CMakeLists.txt @@ -26,6 +26,7 @@ set(rbd_srcs action/List.cc action/Lock.cc action/MergeDiff.cc + action/Migration.cc action/MirrorPool.cc action/MirrorImage.cc action/Namespace.cc diff --git a/src/tools/rbd/action/Migration.cc b/src/tools/rbd/action/Migration.cc new file mode 100644 index 000000000000..fe05b14bef31 --- /dev/null +++ b/src/tools/rbd/action/Migration.cc @@ -0,0 +1,270 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "common/errno.h" + +#include "tools/rbd/ArgumentTypes.h" +#include "tools/rbd/Shell.h" +#include "tools/rbd/Utils.h" + +#include +#include + +namespace rbd { +namespace action { +namespace migration { + +namespace at = argument_types; +namespace po = boost::program_options; + +static int do_prepare(librados::IoCtx& io_ctx, const std::string &image_name, + librados::IoCtx& dest_io_ctx, + const std::string &dest_image_name, + librbd::ImageOptions& opts) { + int r = librbd::RBD().migration_prepare(io_ctx, image_name.c_str(), + dest_io_ctx, dest_image_name.c_str(), + opts); + if (r < 0) { + std::cerr << "rbd: preparing migration failed: " << cpp_strerror(r) + << std::endl; + return r; + } + return 0; +} + +static int do_execute(librados::IoCtx& io_ctx, const std::string &image_name, + bool no_progress) { + utils::ProgressContext pc("Image migration", no_progress); + int r = librbd::RBD().migration_execute_with_progress(io_ctx, + image_name.c_str(), pc); + if (r < 0) { + pc.fail(); + std::cerr << "rbd: migration failed: " << cpp_strerror(r) << std::endl; + return r; + } + pc.finish(); + return 0; +} + +static int do_abort(librados::IoCtx& io_ctx, const std::string &image_name, + bool no_progress) { + utils::ProgressContext pc("Abort image migration", no_progress); + int r = librbd::RBD().migration_abort_with_progress(io_ctx, + image_name.c_str(), pc); + if (r < 0) { + pc.fail(); + std::cerr << "rbd: aborting migration failed: " << cpp_strerror(r) + << std::endl; + return r; + } + pc.finish(); + return 0; +} + +static int do_commit(librados::IoCtx& io_ctx, const std::string &image_name, + bool no_progress) { + utils::ProgressContext pc("Commit image migration", no_progress); + int r = librbd::RBD().migration_commit_with_progress(io_ctx, + image_name.c_str(), pc); + if (r < 0) { + pc.fail(); + std::cerr << "rbd: committing migration failed: " << cpp_strerror(r) + << std::endl; + return r; + } + pc.finish(); + return 0; +} + +void get_prepare_arguments(po::options_description *positional, + po::options_description *options) { + at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE); + at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST); + at::add_create_image_options(options, true); + at::add_flatten_option(options); +} + +int execute_prepare(const po::variables_map &vm, + const std::vector &ceph_global_init_args) { + size_t arg_index = 0; + std::string pool_name; + std::string namespace_name; + std::string image_name; + int r = utils::get_pool_image_snapshot_names( + vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &namespace_name, + &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, + utils::SPEC_VALIDATION_NONE); + if (r < 0) { + return r; + } + + librados::Rados rados; + librados::IoCtx io_ctx; + r = utils::init(pool_name, namespace_name, &rados, &io_ctx); + if (r < 0) { + return r; + } + io_ctx.set_osdmap_full_try(); + + std::string dest_pool_name; + std::string dest_namespace_name; + std::string dest_image_name; + r = utils::get_pool_image_snapshot_names( + vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &dest_pool_name, + &dest_namespace_name, &dest_image_name, nullptr, false, + utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL); + if (r < 0) { + return r; + } + + librbd::ImageOptions opts; + r = utils::get_image_options(vm, true, &opts); + if (r < 0) { + return r; + } + + librados::IoCtx dest_io_ctx; + if (!dest_pool_name.empty()) { + r = utils::init_io_ctx(rados, dest_pool_name, dest_namespace_name, + &dest_io_ctx); + if (r < 0) { + return r; + } + } + + r = do_prepare(io_ctx, image_name, dest_pool_name.empty() ? io_ctx : + dest_io_ctx, dest_image_name, opts); + if (r < 0) { + return r; + } + + return 0; +} + +void get_execute_arguments(po::options_description *positional, + po::options_description *options) { + at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); + at::add_no_progress_option(options); +} + +int execute_execute(const po::variables_map &vm, + const std::vector &ceph_global_init_args) { + size_t arg_index = 0; + std::string pool_name; + std::string namespace_name; + std::string image_name; + int r = utils::get_pool_image_snapshot_names( + vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, + &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, + utils::SPEC_VALIDATION_NONE); + if (r < 0) { + return r; + } + + librados::Rados rados; + librados::IoCtx io_ctx; + r = utils::init(pool_name, namespace_name, &rados, &io_ctx); + if (r < 0) { + return r; + } + io_ctx.set_osdmap_full_try(); + + r = do_execute(io_ctx, image_name, vm[at::NO_PROGRESS].as()); + if (r < 0) { + return r; + } + + return 0; +} + +void get_abort_arguments(po::options_description *positional, + po::options_description *options) { + at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); + at::add_no_progress_option(options); +} + +int execute_abort(const po::variables_map &vm, + const std::vector &ceph_global_init_args) { + size_t arg_index = 0; + std::string pool_name; + std::string namespace_name; + std::string image_name; + int r = utils::get_pool_image_snapshot_names( + vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, + &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, + utils::SPEC_VALIDATION_NONE); + if (r < 0) { + return r; + } + + librados::Rados rados; + librados::IoCtx io_ctx; + r = utils::init(pool_name, namespace_name, &rados, &io_ctx); + if (r < 0) { + return r; + } + io_ctx.set_osdmap_full_try(); + + r = do_abort(io_ctx, image_name, vm[at::NO_PROGRESS].as()); + if (r < 0) { + return r; + } + + return 0; +} + +void get_commit_arguments(po::options_description *positional, + po::options_description *options) { + at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); + at::add_no_progress_option(options); +} + +int execute_commit(const po::variables_map &vm, + const std::vector &ceph_global_init_args) { + size_t arg_index = 0; + std::string pool_name; + std::string namespace_name; + std::string image_name; + int r = utils::get_pool_image_snapshot_names( + vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, + &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, + utils::SPEC_VALIDATION_NONE); + if (r < 0) { + return r; + } + + librados::Rados rados; + librados::IoCtx io_ctx; + r = utils::init(pool_name, namespace_name, &rados, &io_ctx); + if (r < 0) { + return r; + } + io_ctx.set_osdmap_full_try(); + + r = do_commit(io_ctx, image_name, vm[at::NO_PROGRESS].as()); + if (r < 0) { + return r; + } + + return 0; +} + +Shell::Action action_prepare( + {"migration", "prepare"}, {}, "Prepare image migration.", + at::get_long_features_help(), &get_prepare_arguments, &execute_prepare); + +Shell::Action action_execute( + {"migration", "execute"}, {}, "Execute image migration.", "", + &get_execute_arguments, &execute_execute); + +Shell::Action action_abort( + {"migration", "abort"}, {}, "Cancel interrupted image migration.", "", + &get_abort_arguments, &execute_abort); + +Shell::Action action_commit( + {"migration", "commit"}, {}, "Commit image migration.", "", + &get_commit_arguments, &execute_commit); + +} // namespace migration +} // namespace action +} // namespace rbd