From d22ca3d9780bb0e6033fe9d604d915313af798d7 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 22 Dec 2020 09:46:04 -0500 Subject: [PATCH 1/5] librbd/migration: require snapshot when importing from native source Since we cannot mark the source image read-only when running in import-only migration mode, we should require the user to provide a snapshot to ensure that data cannot change while the migration is running. Signed-off-by: Jason Dillaman --- qa/workunits/rbd/cli_migration.sh | 17 +++-- src/librbd/migration/NativeFormat.cc | 94 ++++++++++++++++++++++++++++ src/librbd/migration/NativeFormat.h | 5 ++ src/test/cli/rbd/help.t | 62 +++++++++--------- src/test/pybind/test_rbd.py | 10 ++- src/tools/rbd/action/Migration.cc | 26 ++++++-- 6 files changed, 170 insertions(+), 44 deletions(-) diff --git a/qa/workunits/rbd/cli_migration.sh b/qa/workunits/rbd/cli_migration.sh index e1a4d2df8ce..5f24f8ac2ce 100755 --- a/qa/workunits/rbd/cli_migration.sh +++ b/qa/workunits/rbd/cli_migration.sh @@ -75,13 +75,17 @@ test_import_native_format() { local base_image=$1 local dest_image=$2 + rbd migration prepare --import-only "rbd/${base_image}@2" ${dest_image} + rbd migration abort ${dest_image} + local pool_id=$(ceph osd pool ls detail --format xml | xmlstarlet sel -t -v "//pools/pool[pool_name='rbd']/pool_id") cat > ${TEMPDIR}/spec.json <::open(Context* on_finish) { return; } + auto& snap_name_val = m_json_object[SNAP_NAME_KEY]; + if (snap_name_val.type() == json_spirit::str_type) { + m_snap_name = snap_name_val.get_str(); + } else if (snap_name_val.type() != json_spirit::null_type) { + lderr(cct) << "invalid snap name" << dendl; + on_finish->complete(-EINVAL); + return; + } + + auto& snap_id_val = m_json_object[SNAP_ID_KEY]; + if (!m_snap_name.empty() && snap_id_val.type() != json_spirit::null_type) { + lderr(cct) << "cannot specify both snap name and snap id" << dendl; + on_finish->complete(-EINVAL); + return; + } else if (snap_id_val.type() == json_spirit::str_type) { + try { + m_snap_id = boost::lexical_cast(snap_id_val.get_str()); + } catch (boost::bad_lexical_cast &) { + } + } else if (snap_id_val.type() == json_spirit::int_type) { + m_snap_id = snap_id_val.get_uint64(); + } + + if (snap_id_val.type() != json_spirit::null_type && + m_snap_id == CEPH_NOSNAP) { + lderr(cct) << "invalid snap id" << dendl; + on_finish->complete(-EINVAL); + return; + } + + // snapshot is required for import to keep source read-only + if (m_import_only && m_snap_name.empty() && m_snap_id == CEPH_NOSNAP) { + lderr(cct) << "snapshot required for import" << dendl; + on_finish->complete(-EINVAL); + return; + } + // TODO add support for external clusters librados::IoCtx io_ctx; int r = util::create_ioctx(m_image_ctx->md_ctx, "source image", @@ -153,9 +193,63 @@ void NativeFormat::open(Context* on_finish) { } // open the source RBD image + on_finish = new LambdaContext([this, on_finish](int r) { + handle_open(r, on_finish); }); m_image_ctx->state->open(flags, on_finish); } +template +void NativeFormat::handle_open(int r, Context* on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 10) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to open image: " << cpp_strerror(r) << dendl; + on_finish->complete(r); + return; + } + + if (m_snap_id == CEPH_NOSNAP && m_snap_name.empty()) { + on_finish->complete(0); + return; + } + + if (!m_snap_name.empty()) { + std::shared_lock image_locker{m_image_ctx->image_lock}; + m_snap_id = m_image_ctx->get_snap_id(cls::rbd::UserSnapshotNamespace{}, + m_snap_name); + } + + if (m_snap_id == CEPH_NOSNAP) { + lderr(cct) << "failed to locate snapshot " << m_snap_name << dendl; + on_finish = new LambdaContext([on_finish](int) { + on_finish->complete(-ENOENT); }); + m_image_ctx->state->close(on_finish); + return; + } + + on_finish = new LambdaContext([this, on_finish](int r) { + handle_snap_set(r, on_finish); }); + m_image_ctx->state->snap_set(m_snap_id, on_finish); +} + +template +void NativeFormat::handle_snap_set(int r, Context* on_finish) { + auto cct = m_image_ctx->cct; + ldout(cct, 10) << "r=" << r << dendl; + + if (r < 0) { + lderr(cct) << "failed to set snapshot " << m_snap_id << ": " + << cpp_strerror(r) << dendl; + on_finish = new LambdaContext([r, on_finish](int) { + on_finish->complete(r); }); + m_image_ctx->state->close(on_finish); + return; + } + + on_finish->complete(0); +} + template void NativeFormat::close(Context* on_finish) { auto cct = m_image_ctx->cct; diff --git a/src/librbd/migration/NativeFormat.h b/src/librbd/migration/NativeFormat.h index 0b2406ecbc8..e58c041214e 100644 --- a/src/librbd/migration/NativeFormat.h +++ b/src/librbd/migration/NativeFormat.h @@ -66,6 +66,11 @@ private: std::string m_pool_namespace; std::string m_image_name; std::string m_image_id; + std::string m_snap_name; + uint64_t m_snap_id = CEPH_NOSNAP; + + void handle_open(int r, Context* on_finish); + void handle_snap_set(int r, Context* on_finish); }; diff --git a/src/test/cli/rbd/help.t b/src/test/cli/rbd/help.t index fcc3cd9126b..fb9f1b4737c 100644 --- a/src/test/cli/rbd/help.t +++ b/src/test/cli/rbd/help.t @@ -1491,7 +1491,7 @@ [--source-spec-path ] [--source-spec ] [--pool ] [--namespace ] [--image ] - [--dest-pool ] + [--snap ] [--dest-pool ] [--dest-namespace ] [--dest ] [--image-format ] [--new-format] [--order ] @@ -1504,40 +1504,44 @@ [--journal-splay-width ] [--journal-object-size ] [--journal-pool ] [--flatten] - + Prepare image migration. Positional arguments - source image specification - (example: [/[/]]) - destination image specification - (example: [/[/]]) + source image or snapshot specification + (example: + [/[/]][@]) + destination image specification + (example: + [/[/]]) Optional arguments - --import-only only import data from source - --source-spec-path arg source-spec file (or '-' for stdin) - --source-spec arg source-spec - -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 [default: 2] - --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 - --mirror-image-mode arg mirror image mode [journal or snapshot] - --journal-splay-width arg number of active journal objects - --journal-object-size arg size of journal objects [4K <= size <= 64M] - --journal-pool arg pool for journal objects - --flatten fill clone with parent data (make it independent) + --import-only only import data from source + --source-spec-path arg source-spec file (or '-' for stdin) + --source-spec arg source-spec + -p [ --pool ] arg source pool name + --namespace arg source namespace name + --image arg source image name + --snap arg source snapshot name + --dest-pool arg destination pool name + --dest-namespace arg destination namespace name + --dest arg destination image name + --image-format arg image format [default: 2] + --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 + --mirror-image-mode arg mirror image mode [journal or snapshot] + --journal-splay-width arg number of active journal objects + --journal-object-size arg size of journal objects [4K <= size <= 64M] + --journal-pool arg pool for journal objects + --flatten fill clone with parent data (make it independent) Image Features: (*) supports enabling/disabling on existing images diff --git a/src/test/pybind/test_rbd.py b/src/test/pybind/test_rbd.py index 74cde0764d6..503c72c979c 100644 --- a/src/test/pybind/test_rbd.py +++ b/src/test/pybind/test_rbd.py @@ -2644,13 +2644,15 @@ class TestMigration(object): create_image() with Image(ioctx, image_name) as image: image_id = image.id() + image.create_snap('snap') source_spec = json.dumps( {'type': 'native', 'pool_id': ioctx.get_pool_id(), 'pool_namespace': '', 'image_name': image_name, - 'image_id': image_id}) + 'image_id': image_id, + 'snap_name': 'snap'}) dst_image_name = get_temp_image_name() RBD().migration_prepare_import(source_spec, ioctx, dst_image_name, features=63, order=23, stripe_unit=1<<23, @@ -2667,6 +2669,12 @@ class TestMigration(object): RBD().migration_execute(ioctx, dst_image_name) RBD().migration_commit(ioctx, dst_image_name) + + with Image(ioctx, image_name) as image: + image.remove_snap('snap') + with Image(ioctx, dst_image_name) as image: + image.remove_snap('snap') + RBD().remove(ioctx, dst_image_name) RBD().remove(ioctx, image_name) diff --git a/src/tools/rbd/action/Migration.cc b/src/tools/rbd/action/Migration.cc index eef82d1a1b3..e4e12276820 100644 --- a/src/tools/rbd/action/Migration.cc +++ b/src/tools/rbd/action/Migration.cc @@ -138,7 +138,8 @@ void get_prepare_arguments(po::options_description *positional, "source-spec file (or '-' for stdin)") ("source-spec", po::value(), "source-spec"); - at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE); + at::add_image_or_snap_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); @@ -146,13 +147,18 @@ void get_prepare_arguments(po::options_description *positional, int execute_prepare(const po::variables_map &vm, const std::vector &ceph_global_init_args) { + bool import_only = vm["import-only"].as(); + size_t arg_index = 0; std::string pool_name; std::string namespace_name; std::string image_name; + std::string snap_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, + &image_name, import_only ? &snap_name : nullptr, true, + import_only ? utils::SNAPSHOT_PRESENCE_PERMITTED : + utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_NONE); if (r < 0) { return r; @@ -169,7 +175,6 @@ int execute_prepare(const po::variables_map &vm, return r; } - bool import_only = vm["import-only"].as(); std::string source_spec; if (vm.count("source-spec") && vm.count("source-spec-path")) { std::cerr << "rbd: cannot specify both source-image-spec and " @@ -223,12 +228,18 @@ int execute_prepare(const po::variables_map &vm, } if (import_only && source_spec.empty()) { + if (snap_name.empty()) { + std::cerr << "rbd: snapshot name was not specified" << std::endl; + return -EINVAL; + } + std::stringstream ss; ss << R"({)" << R"("type":"native",)" << R"("pool_id":)" << io_ctx.get_id() << R"(,)" << R"("pool_namespace":")" << io_ctx.get_namespace() << R"(",)" - << R"("image_name":")" << image_name << R"(")" + << R"("image_name":")" << image_name << R"(",)" + << R"("snap_name":")" << snap_name << R"(")" << R"(})"; source_spec = ss.str(); @@ -238,12 +249,19 @@ int execute_prepare(const po::variables_map &vm, } io_ctx = dst_io_ctx; image_name = dst_image_name; + snap_name = ""; } else if (!import_only && !source_spec.empty()) { std::cerr << "rbd: --import-only must be used in combination with " << "source-spec/source-spec-path" << std::endl; return -EINVAL; } + if (!snap_name.empty()) { + std::cerr << "rbd: snapshot name specified for a command that doesn't " + << "use it" << std::endl; + return -EINVAL; + } + librbd::ImageOptions opts; r = utils::get_image_options(vm, true, &opts); if (r < 0) { From 9eb47021b8f72274ce27898e2b32afec28e90175 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 22 Dec 2020 13:34:51 -0500 Subject: [PATCH 2/5] librbd: helper utility to retrieve config from the MON config store A special "config://" URI prefix can be used to denote configuration settings that should be (securely) pulled from the MON config store. This will be first used in a follow-up commit to support storing the S3 access and secret keys in the MON config store. This "config://" syntax is already in-use by RGW and ceph-iscsi for pulling secrets when deployed via cephadm. Signed-off-by: Jason Dillaman --- src/librbd/Utils.cc | 39 +++++++++++++++++++++++++++++++++++++++ src/librbd/Utils.h | 4 ++++ 2 files changed, 43 insertions(+) diff --git a/src/librbd/Utils.cc b/src/librbd/Utils.cc index af2b1655797..dadf6ab8126 100644 --- a/src/librbd/Utils.cc +++ b/src/librbd/Utils.cc @@ -11,9 +11,11 @@ #include "include/neorados/RADOS.hpp" #include "include/rbd/features.h" #include "common/dout.h" +#include "common/errno.h" #include "librbd/ImageCtx.h" #include "librbd/Features.h" +#include #include #include @@ -23,6 +25,11 @@ namespace librbd { namespace util { +namespace { + +const std::string CONFIG_KEY_URI_PREFIX{"config://"}; + +} // anonymous namespace const std::string group_header_name(const std::string &group_id) { @@ -200,5 +207,37 @@ uint64_t reserve_async_request_id() { return ++async_request_seq; } +bool is_config_key_uri(const std::string& uri) { + return boost::starts_with(uri, CONFIG_KEY_URI_PREFIX); +} + +int get_config_key(librados::Rados& rados, const std::string& uri, + std::string* value) { + auto cct = reinterpret_cast(rados.cct()); + + if (!is_config_key_uri(uri)) { + return -EINVAL; + } + + std::string key = uri.substr(CONFIG_KEY_URI_PREFIX.size()); + std::string cmd = + "{" + "\"prefix\": \"config-key get\", " + "\"key\": \"" + key + "\"" + "}"; + + bufferlist in_bl; + bufferlist out_bl; + int r = rados.mon_command(cmd, in_bl, &out_bl, nullptr); + if (r < 0) { + lderr(cct) << "failed to retrieve MON config key " << key << ": " + << cpp_strerror(r) << dendl; + return r; + } + + *value = std::string(out_bl.c_str(), out_bl.length()); + return 0; +} + } // namespace util } // namespace librbd diff --git a/src/librbd/Utils.h b/src/librbd/Utils.h index 736a5063ac8..dee91feee5e 100644 --- a/src/librbd/Utils.h +++ b/src/librbd/Utils.h @@ -276,6 +276,10 @@ SnapContext get_snap_context( uint64_t reserve_async_request_id(); +bool is_config_key_uri(const std::string& uri); +int get_config_key(librados::Rados& rados, const std::string& uri, + std::string* value); + } // namespace util } // namespace librbd From 0c51b58f67176304515bb1f3d7b460b9a3722572 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 22 Dec 2020 13:36:57 -0500 Subject: [PATCH 3/5] librbd/migration: optionally pull S3 keys from MON config store This allows the S3 keys to be better protected since the MON can be configured to restrict access to keys by user and the results can be encrypted in transit. Signed-off-by: Jason Dillaman --- src/librbd/migration/S3Stream.cc | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/librbd/migration/S3Stream.cc b/src/librbd/migration/S3Stream.cc index f812ef0294a..222a08ee3fe 100644 --- a/src/librbd/migration/S3Stream.cc +++ b/src/librbd/migration/S3Stream.cc @@ -89,8 +89,31 @@ void S3Stream::open(Context* on_finish) { } m_url = url_value.get_str(); + + librados::Rados rados(m_image_ctx->md_ctx); + int r = 0; m_access_key = access_key.get_str(); + if (util::is_config_key_uri(m_access_key)) { + r = util::get_config_key(rados, m_access_key, &m_access_key); + if (r < 0) { + lderr(m_cct) << "failed to retrieve access key from config: " + << cpp_strerror(r) << dendl; + on_finish->complete(r); + return; + } + } + m_secret_key = secret_key.get_str(); + if (util::is_config_key_uri(m_secret_key)) { + r = util::get_config_key(rados, m_secret_key, &m_secret_key); + if (r < 0) { + lderr(m_cct) << "failed to retrieve secret key from config: " + << cpp_strerror(r) << dendl; + on_finish->complete(r); + return; + } + } + ldout(m_cct, 10) << "url=" << m_url << ", " << "access_key=" << m_access_key << dendl; From 7044a9d6ffc6485ec7fffc4f947b0683f1f0ffc9 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Tue, 22 Dec 2020 13:56:59 -0500 Subject: [PATCH 4/5] librbd/migration: fixed missing return statements on stream error path Signed-off-by: Jason Dillaman --- src/librbd/migration/HttpStream.cc | 1 + src/librbd/migration/S3Stream.cc | 1 + 2 files changed, 2 insertions(+) diff --git a/src/librbd/migration/HttpStream.cc b/src/librbd/migration/HttpStream.cc index 7b8f91a325c..fa3cc00320e 100644 --- a/src/librbd/migration/HttpStream.cc +++ b/src/librbd/migration/HttpStream.cc @@ -56,6 +56,7 @@ void HttpStream::close(Context* on_finish) { if (!m_http_client) { on_finish->complete(0); + return; } m_http_client->close(on_finish); diff --git a/src/librbd/migration/S3Stream.cc b/src/librbd/migration/S3Stream.cc index 222a08ee3fe..46165f208f9 100644 --- a/src/librbd/migration/S3Stream.cc +++ b/src/librbd/migration/S3Stream.cc @@ -128,6 +128,7 @@ void S3Stream::close(Context* on_finish) { if (!m_http_client) { on_finish->complete(0); + return; } m_http_client->close(on_finish); From c2bf642ecf141753088eb3dbf88c65ab600268d5 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Mon, 21 Dec 2020 14:40:13 -0500 Subject: [PATCH 5/5] doc/rbd: incorporate external data source support for live-migration Signed-off-by: Jason Dillaman --- doc/rbd/rbd-live-migration.rst | 234 ++++++++++++++++++++++++++++++--- 1 file changed, 214 insertions(+), 20 deletions(-) diff --git a/doc/rbd/rbd-live-migration.rst b/doc/rbd/rbd-live-migration.rst index 10a80280ff3..a0ba991fd6c 100644 --- a/doc/rbd/rbd-live-migration.rst +++ b/doc/rbd/rbd-live-migration.rst @@ -4,20 +4,33 @@ .. index:: Ceph Block Device; live-migration -RBD images can be live-migrated between different pools within the same cluster -or between different image formats and layouts. When started, the source image -will be deep-copied to the destination image, pulling all snapshot history and -optionally preserving any link to the source image's parent to preserve -sparseness. +RBD images can be live-migrated between different pools within the same cluster; +between different image formats and layouts; or from external data sources. +When started, the source will be deep-copied to the destination image, pulling +all snapshot history while preserving the sparse allocation of data where +possible. -This copy process can safely run in the background while the new target image is -in use. There is currently a requirement to temporarily stop using the source -image before preparing a migration. This helps to ensure that the client using -the image is updated to point to the new target image. +By default, when live-migrating RBD images within the same Ceph cluster, the +source image will be marked read-only and all clients will instead redirect +IOs to the new target image. In addition, this mode can optionally preserve the +link to the source image's parent to preserve sparseness, or it can flatten the +image during the migration to remove the dependency on the source image's +parent. + +The live-migration process can also be used in an import-only mode where the +source image remains unmodified and the target image can be linked to an +external data source such as a backing file, HTTP(s) file, or S3 object. + +The live-migration copy process can safely run in the background while the new +target image is in use. There is currently a requirement to temporarily stop +using the source image before preparing a migration when not using the +import-only mode of operation. This helps to ensure that the client using the +image is updated to point to the new target image. .. note:: - Image live-migration requires the Ceph Nautilus release or later. The ``krbd`` - kernel module does not support live-migration at this time. + Image live-migration requires the Ceph Nautilus release or later. Support for + external data sources requires the Ceph Pacific release of later. The + ``krbd`` kernel module does not support live-migration at this time. .. ditaa:: @@ -36,11 +49,14 @@ the image is updated to point to the new target image. The live-migration process is comprised of three steps: #. **Prepare Migration:** The initial step creates the new target image and - cross-links the source and target images. Similar to `layered images`_, - attempts to read uninitialized extents within the target image will - internally redirect the read to the source image, and writes to - uninitialized extents within the target will internally deep-copy the - overlapping source image block to the target image. + links the target image to the source. When not configured in the import-only + mode, the source image will also be linked to the target image and marked + read-only. + + Similar to `layered images`_, attempts to read uninitialized data extents + within the target image will internally redirect the read to the source + image, and writes to uninitialized extents within the target will internally + deep-copy the overlapping source image block to the target image. #. **Execute Migration:** This is a background operation that deep-copies all @@ -51,14 +67,15 @@ The live-migration process is comprised of three steps: #. **Finish Migration:** Once the background migration process has completed, the migration can be committed or aborted. Committing the migration will remove the cross-links between the source and target images, and will - remove the source image. Aborting the migration will remove the cross-links, - and will remove the target image. + remove the source image if not configured in the import-only mode. Aborting + the migration will remove the cross-links, and will remove the target image. Prepare Migration ================= -The live-migration process is initiated by running the `rbd migration prepare` -command, providing the source and target images:: +The default live-migration process for images within the same Ceph cluster is +initiated by running the `rbd migration prepare` command, providing the source +and target images:: $ rbd migration prepare migration_source [migration_target] @@ -91,6 +108,183 @@ usage during the migration process:: 5e2cba2f62e migration_source +Prepare Import-Only Migration +============================= + +The import-only live-migration process is initiated by running the same +`rbd migration prepare` command, but adding the `--import-only` optional +and providing a JSON-encoded ``source-spec`` to describe how to access +the source image data. This ``source-spec`` can either be passed +directly via the `--source-spec` optional, or via a file or STDIN via the +`--source-spec-file` optional:: + + $ rbd migration prepare --import-only --source-spec "" migration_target + +The `rbd migration prepare` command accepts all the same layout optionals as the +`rbd create` command. + +The `rbd status` command will show the current state of the live-migration:: + + $ rbd status migration_target + Watchers: none + Migration: + source: {"stream":{"file_path":"/mnt/image.raw","type":"file"},"type":"raw"} + destination: rbd/migration_target (ac69113dc1d7) + state: prepared + +The general format for the ``source-spec`` JSON is as follows:: + + { + "type": "", + + "stream": { + "type": "", + + } + } + +The following formats are currently supported: ``native`` and ``raw``. The +following streams are currently supported: ``file``, ``http``, and ``s3``. + +Formats +~~~~~~~ + +The ``native`` format can be used to describe a native RBD image within a +Ceph cluster as the source image. Its ``source-spec`` JSON is encoded +as follows:: + + { + "type": "native", + "pool_name": "", + ["pool_id": ,] (optional alternative to "pool_name") + ["pool_namespace": "", + ["image_id": "",] (optional if image in trash) + "snap_name": "", + ["snap_id": "",] (optional alternative to "snap_name") + } + +Note that the ``native`` format does not include the ``stream`` object since +it utilizes native Ceph operations. For example, to import from the image +``rbd/ns1/image1@snap1``, the ``source-spec`` could be encoded as:: + + { + "type": "native", + "pool_name": "rbd", + "pool_namespace": "ns1", + "image_name": "image1", + "snap_name": "snap1" + } + +The ``raw`` format can be used to describe a thick-provisioned, raw block device +export (i.e. `rbd export --export-format 1 `). The ``raw`` format +data can be linked to any supported stream source described below. For example, +its base ``source-spec`` JSON is encoded as follows:: + + { + "type": "raw", + "stream": { + + }, + "snapshots": [ + { + "type": "raw", + "name": "", + "stream": { + + } + }, + ] (optional oldest to newest ordering of snapshots) + } + +The inclusion of the ``snapshots`` array is optional and currently only supports +thick-provisioned ``raw`` snapshot exports. + +Additional formats such as QCOW2, RBD export-format v2, and RBD export-diff +snapshots will be added in a future release. + +Streams +~~~~~~~ + +The ``file`` stream can be used to import from a locally accessible POSIX file +source. Its ``source-spec`` JSON is encoded as follows:: + + { + + "stream": { + "type": "file", + "file_path": "" + } + } + +For example, to import a raw-format image from a file located at +"/mnt/image.raw", its ``source-spec`` JSON is encoded as follows:: + + { + "type": "raw", + "stream": { + "type": "file", + "file_path": "/mnt/image.raw" + } + } + +The ``http`` stream can be used to import from a remote HTTP or HTTPS web +server. Its ``source-spec`` JSON is encoded as follows:: + + { + + "stream": { + "type": "http", + "url": "" + } + } + +For example, to import a raw-format image from a file located at +``http://download.ceph.com/image.raw``, its ``source-spec`` JSON is encoded +as follows:: + + { + "type": "raw", + "stream": { + "type": "http", + "url": "http://download.ceph.com/image.raw" + } + } + +The ``s3`` stream can be used to import from a remote S3 bucket. Its +``source-spec`` JSON is encoded as follows:: + + { + + "stream": { + "type": "s3", + "url": "", + "access_key": "", + "secret_key": "" + } + } + +For example, to import a raw-format image from a file located at +`http://s3.ceph.com/bucket/image.raw`, its ``source-spec`` JSON is encoded +as follows:: + + { + "type": "raw", + "stream": { + "type": "s3", + "url": "http://s3.ceph.com/bucket/image.raw", + "access_key": "NX5QOQKC6BH2IDN8HC7A", + "secret_key": "LnEsqNNqZIpkzauboDcLXLcYaWwLQ3Kop0zAnKIn" + } + } + +.. note:: + The ``access_key`` and ``secret_key`` parameters support storing the keys in + the MON config-key store by prefixing the key values with ``config://`` + followed by the path in the MON config-key store to the value. Values can be + stored in the config-key store via ``ceph config-key set `` + (e.g. ``ceph config-key set rbd/s3/access_key NX5QOQKC6BH2IDN8HC7A``). + Execute Migration =================