]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rbd: CLI to migrate images
authorMykola Golub <to.my.trociny@gmail.com>
Wed, 22 Nov 2017 13:50:19 +0000 (15:50 +0200)
committerJason Dillaman <dillaman@redhat.com>
Tue, 14 Aug 2018 22:29:45 +0000 (18:29 -0400)
Signed-off-by: Mykola Golub <mgolub@suse.com>
doc/man/8/rbd.rst
qa/workunits/rbd/cli_generic.sh
src/test/cli/rbd/help.t
src/tools/rbd/CMakeLists.txt
src/tools/rbd/action/Migration.cc [new file with mode: 0644]

index 40f986ab9d5064a7710bf826c6e005aadeaf2868..01282548935877b1899bc0d40a02c97cbe52848a 100644 (file)
@@ -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.
 
index 0ecb75186bd6a209a18a0681d4fbe5203f14973d..95adbbbacf1077d75019695d9c7a5c9d4e865c46 100755 (executable)
@@ -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
index e7d6aff7b621e7aba76a5108224f4f9a45f3e14f..bbbbcaf6751fdff17855d38b5f148b2ab84b3e94 100644 (file)
       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.
     --path arg           path to merged diff (or '-' for stdout)
     --no-progress        disable progress output
   
+  rbd help migration abort
+  usage: rbd migration abort [--pool <pool>] [--namespace <namespace>] 
+                             [--image <image>] [--no-progress] 
+                             <image-spec> 
+  
+  Cancel interrupted image migration.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/[<namespace-name>/]]<image-name>)
+  
+  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 <pool>] [--namespace <namespace>] 
+                              [--image <image>] [--no-progress] 
+                              <image-spec> 
+  
+  Commit image migration.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/[<namespace-name>/]]<image-name>)
+  
+  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 <pool>] [--namespace <namespace>] 
+                               [--image <image>] [--no-progress] 
+                               <image-spec> 
+  
+  Execute image migration.
+  
+  Positional arguments
+    <image-spec>         image specification
+                         (example: [<pool-name>/[<namespace-name>/]]<image-name>)
+  
+  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 <pool>] [--namespace <namespace>] 
+                               [--image <image>] [--dest-pool <dest-pool>] 
+                               [--dest-namespace <dest-namespace>] 
+                               [--dest <dest>] [--image-format <image-format>] 
+                               [--new-format] [--order <order>] 
+                               [--object-size <object-size>] 
+                               [--image-feature <image-feature>] 
+                               [--image-shared] [--stripe-unit <stripe-unit>] 
+                               [--stripe-count <stripe-count>] 
+                               [--data-pool <data-pool>] 
+                               [--journal-splay-width <journal-splay-width>] 
+                               [--journal-object-size <journal-object-size>] 
+                               [--journal-pool <journal-pool>] [--flatten] 
+                               <source-image-spec> <dest-image-spec> 
+  
+  Prepare image migration.
+  
+  Positional arguments
+    <source-image-spec>       source image specification
+                              (example:
+                              [<pool-name>/[<namespace-name>/]]<image-name>)
+    <dest-image-spec>         destination image specification
+                              (example:
+                              [<pool-name>/[<namespace-name>/]]<image-name>)
+  
+  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 <pool>] [--namespace <namespace>] 
                                  [--image <image>] 
index 17154505ef64e4809a5b4d2007adb8114e3b1ea9..55bc116d9c99a4df9e2e7c306329f972e6bb86c9 100644 (file)
@@ -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 (file)
index 0000000..fe05b14
--- /dev/null
@@ -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 <iostream>
+#include <boost/program_options.hpp>
+
+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<std::string> &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<std::string> &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<bool>());
+  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<std::string> &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<bool>());
+  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<std::string> &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<bool>());
+  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