1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 #include "librbd/api/Migration.h"
5 #include "include/rados/librados.hpp"
6 #include "include/stringify.h"
7 #include "common/dout.h"
8 #include "common/errno.h"
9 #include "cls/rbd/cls_rbd_client.h"
10 #include "librbd/ExclusiveLock.h"
11 #include "librbd/ImageCtx.h"
12 #include "librbd/ImageState.h"
13 #include "librbd/Operations.h"
14 #include "librbd/Utils.h"
15 #include "librbd/api/Config.h"
16 #include "librbd/api/Group.h"
17 #include "librbd/api/Image.h"
18 #include "librbd/api/Snapshot.h"
19 #include "librbd/api/Trash.h"
20 #include "librbd/deep_copy/MetadataCopyRequest.h"
21 #include "librbd/deep_copy/SnapshotCopyRequest.h"
22 #include "librbd/exclusive_lock/Policy.h"
23 #include "librbd/image/AttachChildRequest.h"
24 #include "librbd/image/AttachParentRequest.h"
25 #include "librbd/image/CloneRequest.h"
26 #include "librbd/image/CreateRequest.h"
27 #include "librbd/image/DetachChildRequest.h"
28 #include "librbd/image/DetachParentRequest.h"
29 #include "librbd/image/ListWatchersRequest.h"
30 #include "librbd/image/RemoveRequest.h"
31 #include "librbd/internal.h"
32 #include "librbd/io/ImageRequestWQ.h"
33 #include "librbd/mirror/DisableRequest.h"
34 #include "librbd/mirror/EnableRequest.h"
36 #include <boost/scope_exit.hpp>
38 #define dout_subsys ceph_subsys_rbd
40 #define dout_prefix *_dout << "librbd::Migration: " << __func__ << ": "
44 inline bool operator==(const linked_image_spec_t
& rhs
,
45 const linked_image_spec_t
& lhs
) {
46 bool result
= (rhs
.pool_id
== lhs
.pool_id
&&
47 rhs
.pool_namespace
== lhs
.pool_namespace
&&
48 rhs
.image_id
== lhs
.image_id
);
54 using util::create_rados_callback
;
58 class MigrationProgressContext
: public ProgressContext
{
60 MigrationProgressContext(librados::IoCtx
& io_ctx
,
61 const std::string
&header_oid
,
62 cls::rbd::MigrationState state
,
63 ProgressContext
*prog_ctx
)
64 : m_io_ctx(io_ctx
), m_header_oid(header_oid
), m_state(state
),
65 m_prog_ctx(prog_ctx
), m_cct(reinterpret_cast<CephContext
*>(io_ctx
.cct())),
66 m_lock(util::unique_lock_name("librbd::api::MigrationProgressContext",
68 ceph_assert(m_prog_ctx
!= nullptr);
71 ~MigrationProgressContext() {
72 wait_for_in_flight_updates();
75 int update_progress(uint64_t offset
, uint64_t total
) override
{
76 ldout(m_cct
, 20) << "offset=" << offset
<< ", total=" << total
<< dendl
;
78 m_prog_ctx
->update_progress(offset
, total
);
80 std::string description
= stringify(offset
* 100 / total
) + "% complete";
82 send_state_description_update(description
);
88 librados::IoCtx
& m_io_ctx
;
89 std::string m_header_oid
;
90 cls::rbd::MigrationState m_state
;
91 ProgressContext
*m_prog_ctx
;
96 std::string m_state_description
;
97 bool m_pending_update
= false;
98 int m_in_flight_state_updates
= 0;
100 void send_state_description_update(const std::string
&description
) {
101 Mutex::Locker
locker(m_lock
);
103 if (description
== m_state_description
) {
107 m_state_description
= description
;
109 if (m_in_flight_state_updates
> 0) {
110 m_pending_update
= true;
114 set_state_description();
117 void set_state_description() {
118 ldout(m_cct
, 20) << "state_description=" << m_state_description
<< dendl
;
120 ceph_assert(m_lock
.is_locked());
122 librados::ObjectWriteOperation op
;
123 cls_client::migration_set_state(&op
, m_state
, m_state_description
);
125 using klass
= MigrationProgressContext
;
126 librados::AioCompletion
*comp
=
127 create_rados_callback
<klass
, &klass::handle_set_state_description
>(this);
128 int r
= m_io_ctx
.aio_operate(m_header_oid
, comp
, &op
);
132 m_in_flight_state_updates
++;
135 void handle_set_state_description(int r
) {
136 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
138 Mutex::Locker
locker(m_lock
);
140 m_in_flight_state_updates
--;
143 lderr(m_cct
) << "failed to update migration state: " << cpp_strerror(r
)
145 } else if (m_pending_update
) {
146 set_state_description();
147 m_pending_update
= false;
153 void wait_for_in_flight_updates() {
154 Mutex::Locker
locker(m_lock
);
156 ldout(m_cct
, 20) << "m_in_flight_state_updates="
157 << m_in_flight_state_updates
<< dendl
;
159 m_pending_update
= false;
160 while (m_in_flight_state_updates
> 0) {
166 int trash_search(librados::IoCtx
&io_ctx
, rbd_trash_image_source_t source
,
167 const std::string
&image_name
, std::string
*image_id
) {
168 std::vector
<trash_image_info_t
> entries
;
170 int r
= Trash
<>::list(io_ctx
, entries
, false);
175 for (auto &entry
: entries
) {
176 if (entry
.source
== source
&& entry
.name
== image_name
) {
177 *image_id
= entry
.id
;
185 template <typename I
>
186 int open_source_image(librados::IoCtx
& io_ctx
, const std::string
&image_name
,
187 I
**src_image_ctx
, librados::IoCtx
*dst_io_ctx
,
188 std::string
*dst_image_name
, std::string
*dst_image_id
,
189 bool *flatten
, bool *mirroring
,
190 cls::rbd::MigrationState
*state
,
191 std::string
*state_description
) {
192 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
194 librados::IoCtx src_io_ctx
;
195 std::string src_image_name
;
196 std::string src_image_id
;
197 cls::rbd::MigrationSpec migration_spec
;
198 I
*image_ctx
= I::create(image_name
, "", nullptr, io_ctx
, false);
200 ldout(cct
, 10) << "trying to open image by name " << io_ctx
.get_pool_name()
201 << "/" << image_name
<< dendl
;
203 int r
= image_ctx
->state
->open(OPEN_FLAG_IGNORE_MIGRATING
);
206 lderr(cct
) << "failed to open image: " << cpp_strerror(r
) << dendl
;
212 BOOST_SCOPE_EXIT_TPL(&r
, &image_ctx
) {
213 if (r
!= 0 && image_ctx
!= nullptr) {
214 image_ctx
->state
->close();
216 } BOOST_SCOPE_EXIT_END
;
219 // The opened image is either a source (then just proceed) or a
220 // destination (then look for the source image id in the migration
223 r
= cls_client::migration_get(&image_ctx
->md_ctx
, image_ctx
->header_oid
,
227 lderr(cct
) << "failed retrieving migration header: " << cpp_strerror(r
)
232 ldout(cct
, 10) << "migration spec: " << migration_spec
<< dendl
;
234 if (migration_spec
.header_type
!= cls::rbd::MIGRATION_HEADER_TYPE_SRC
&&
235 migration_spec
.header_type
!= cls::rbd::MIGRATION_HEADER_TYPE_DST
) {
236 lderr(cct
) << "unexpected migration header type: "
237 << migration_spec
.header_type
<< dendl
;
242 if (migration_spec
.header_type
== cls::rbd::MIGRATION_HEADER_TYPE_DST
) {
243 ldout(cct
, 10) << "the destination image is opened" << dendl
;
245 // Close and look for the source image.
246 r
= image_ctx
->state
->close();
249 lderr(cct
) << "failed closing image: " << cpp_strerror(r
)
254 r
= util::create_ioctx(io_ctx
, "source image", migration_spec
.pool_id
,
255 migration_spec
.pool_namespace
, &src_io_ctx
);
260 src_image_name
= migration_spec
.image_name
;
261 src_image_id
= migration_spec
.image_id
;
263 ldout(cct
, 10) << "the source image is opened" << dendl
;
266 assert (r
== -ENOENT
);
268 ldout(cct
, 10) << "source image is not found. Trying trash" << dendl
;
270 r
= trash_search(io_ctx
, RBD_TRASH_IMAGE_SOURCE_MIGRATION
, image_name
,
273 lderr(cct
) << "failed to determine image id: " << cpp_strerror(r
)
278 ldout(cct
, 10) << "source image id from trash: " << src_image_id
<< dendl
;
280 src_io_ctx
.dup(io_ctx
);
283 if (image_ctx
== nullptr) {
284 int flags
= OPEN_FLAG_IGNORE_MIGRATING
;
286 if (src_image_id
.empty()) {
287 ldout(cct
, 20) << "trying to open v1 image by name "
288 << src_io_ctx
.get_pool_name() << "/" << src_image_name
291 flags
|= OPEN_FLAG_OLD_FORMAT
;
293 ldout(cct
, 20) << "trying to open v2 image by id "
294 << src_io_ctx
.get_pool_name() << "/" << src_image_id
298 image_ctx
= I::create(src_image_name
, src_image_id
, nullptr, src_io_ctx
,
300 r
= image_ctx
->state
->open(flags
);
302 lderr(cct
) << "failed to open source image " << src_io_ctx
.get_pool_name()
303 << "/" << (src_image_id
.empty() ? src_image_name
: src_image_id
)
304 << ": " << cpp_strerror(r
) << dendl
;
309 r
= cls_client::migration_get(&image_ctx
->md_ctx
, image_ctx
->header_oid
,
312 lderr(cct
) << "failed retrieving migration header: " << cpp_strerror(r
)
317 ldout(cct
, 20) << "migration spec: " << migration_spec
<< dendl
;
320 r
= util::create_ioctx(image_ctx
->md_ctx
, "source image",
321 migration_spec
.pool_id
, migration_spec
.pool_namespace
,
327 *src_image_ctx
= image_ctx
;
328 *dst_image_name
= migration_spec
.image_name
;
329 *dst_image_id
= migration_spec
.image_id
;
330 *flatten
= migration_spec
.flatten
;
331 *mirroring
= migration_spec
.mirroring
;
332 *state
= migration_spec
.state
;
333 *state_description
= migration_spec
.state_description
;
338 } // anonymous namespace
340 template <typename I
>
341 int Migration
<I
>::prepare(librados::IoCtx
& io_ctx
,
342 const std::string
&image_name
,
343 librados::IoCtx
& dest_io_ctx
,
344 const std::string
&dest_image_name_
,
345 ImageOptions
& opts
) {
346 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
348 std::string dest_image_name
= dest_image_name_
.empty() ? image_name
:
351 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< " -> "
352 << dest_io_ctx
.get_pool_name() << "/" << dest_image_name
353 << ", opts=" << opts
<< dendl
;
355 auto image_ctx
= I::create(image_name
, "", nullptr, io_ctx
, false);
356 int r
= image_ctx
->state
->open(0);
358 lderr(cct
) << "failed to open image: " << cpp_strerror(r
) << dendl
;
361 BOOST_SCOPE_EXIT_TPL(image_ctx
) {
362 image_ctx
->state
->close();
363 } BOOST_SCOPE_EXIT_END
;
365 std::list
<obj_watch_t
> watchers
;
366 int flags
= librbd::image::LIST_WATCHERS_FILTER_OUT_MY_INSTANCE
|
367 librbd::image::LIST_WATCHERS_FILTER_OUT_MIRROR_INSTANCES
;
368 C_SaferCond on_list_watchers
;
369 auto list_watchers_request
= librbd::image::ListWatchersRequest
<I
>::create(
370 *image_ctx
, flags
, &watchers
, &on_list_watchers
);
371 list_watchers_request
->send();
372 r
= on_list_watchers
.wait();
374 lderr(cct
) << "failed listing watchers:" << cpp_strerror(r
) << dendl
;
377 if (!watchers
.empty()) {
378 lderr(cct
) << "image has watchers - not migrating" << dendl
;
383 if (opts
.get(RBD_IMAGE_OPTION_FORMAT
, &format
) != 0) {
384 opts
.set(RBD_IMAGE_OPTION_FORMAT
, format
);
387 lderr(cct
) << "unsupported destination image format: " << format
<< dendl
;
393 RWLock::RLocker
snap_locker(image_ctx
->snap_lock
);
394 features
= image_ctx
->features
;
396 opts
.get(RBD_IMAGE_OPTION_FEATURES
, &features
);
397 if ((features
& ~RBD_FEATURES_ALL
) != 0) {
398 lderr(cct
) << "librbd does not support requested features" << dendl
;
401 features
&= ~RBD_FEATURES_INTERNAL
;
402 features
|= RBD_FEATURE_MIGRATING
;
403 opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
405 uint64_t order
= image_ctx
->order
;
406 if (opts
.get(RBD_IMAGE_OPTION_ORDER
, &order
) != 0) {
407 opts
.set(RBD_IMAGE_OPTION_ORDER
, order
);
409 r
= image::CreateRequest
<I
>::validate_order(cct
, order
);
414 uint64_t stripe_unit
= image_ctx
->stripe_unit
;
415 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_UNIT
, &stripe_unit
) != 0) {
416 opts
.set(RBD_IMAGE_OPTION_STRIPE_UNIT
, stripe_unit
);
418 uint64_t stripe_count
= image_ctx
->stripe_count
;
419 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_COUNT
, &stripe_count
) != 0) {
420 opts
.set(RBD_IMAGE_OPTION_STRIPE_COUNT
, stripe_count
);
423 uint64_t flatten
= 0;
424 if (opts
.get(RBD_IMAGE_OPTION_FLATTEN
, &flatten
) == 0) {
425 opts
.unset(RBD_IMAGE_OPTION_FLATTEN
);
428 ldout(cct
, 20) << "updated opts=" << opts
<< dendl
;
430 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, "", opts
, flatten
> 0,
431 false, cls::rbd::MIGRATION_STATE_PREPARING
, "", nullptr);
432 r
= migration
.prepare();
434 features
&= ~RBD_FEATURE_MIGRATING
;
435 opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
440 template <typename I
>
441 int Migration
<I
>::execute(librados::IoCtx
& io_ctx
,
442 const std::string
&image_name
,
443 ProgressContext
&prog_ctx
) {
444 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
446 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< dendl
;
449 librados::IoCtx dest_io_ctx
;
450 std::string dest_image_name
;
451 std::string dest_image_id
;
454 cls::rbd::MigrationState state
;
455 std::string state_description
;
457 int r
= open_source_image(io_ctx
, image_name
, &image_ctx
, &dest_io_ctx
,
458 &dest_image_name
, &dest_image_id
, &flatten
,
459 &mirroring
, &state
, &state_description
);
464 BOOST_SCOPE_EXIT_TPL(image_ctx
) {
465 image_ctx
->state
->close();
466 } BOOST_SCOPE_EXIT_END
;
468 if (state
!= cls::rbd::MIGRATION_STATE_PREPARED
) {
469 lderr(cct
) << "current migration state is '" << state
<< "'"
470 << " (should be 'prepared')" << dendl
;
474 ldout(cct
, 5) << "migrating " << image_ctx
->md_ctx
.get_pool_name() << "/"
475 << image_ctx
->name
<< " -> " << dest_io_ctx
.get_pool_name()
476 << "/" << dest_image_name
<< dendl
;
479 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, dest_image_id
,
480 opts
, flatten
, mirroring
, state
, state_description
,
482 r
= migration
.execute();
490 template <typename I
>
491 int Migration
<I
>::abort(librados::IoCtx
& io_ctx
, const std::string
&image_name
,
492 ProgressContext
&prog_ctx
) {
493 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
495 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< dendl
;
498 librados::IoCtx dest_io_ctx
;
499 std::string dest_image_name
;
500 std::string dest_image_id
;
503 cls::rbd::MigrationState state
;
504 std::string state_description
;
506 int r
= open_source_image(io_ctx
, image_name
, &image_ctx
, &dest_io_ctx
,
507 &dest_image_name
, &dest_image_id
, &flatten
,
508 &mirroring
, &state
, &state_description
);
513 ldout(cct
, 5) << "canceling incomplete migration "
514 << image_ctx
->md_ctx
.get_pool_name() << "/" << image_ctx
->name
515 << " -> " << dest_io_ctx
.get_pool_name() << "/" << dest_image_name
519 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, dest_image_id
,
520 opts
, flatten
, mirroring
, state
, state_description
,
522 r
= migration
.abort();
524 image_ctx
->state
->close();
533 template <typename I
>
534 int Migration
<I
>::commit(librados::IoCtx
& io_ctx
,
535 const std::string
&image_name
,
536 ProgressContext
&prog_ctx
) {
537 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
539 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< dendl
;
542 librados::IoCtx dest_io_ctx
;
543 std::string dest_image_name
;
544 std::string dest_image_id
;
547 cls::rbd::MigrationState state
;
548 std::string state_description
;
550 int r
= open_source_image(io_ctx
, image_name
, &image_ctx
, &dest_io_ctx
,
551 &dest_image_name
, &dest_image_id
, &flatten
,
552 &mirroring
, &state
, &state_description
);
557 if (state
!= cls::rbd::MIGRATION_STATE_EXECUTED
) {
558 lderr(cct
) << "current migration state is '" << state
<< "'"
559 << " (should be 'executed')" << dendl
;
560 image_ctx
->state
->close();
564 ldout(cct
, 5) << "migrating " << image_ctx
->md_ctx
.get_pool_name() << "/"
565 << image_ctx
->name
<< " -> " << dest_io_ctx
.get_pool_name()
566 << "/" << dest_image_name
<< dendl
;
569 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, dest_image_id
,
570 opts
, flatten
, mirroring
, state
, state_description
,
572 r
= migration
.commit();
574 // image_ctx is closed in commit when removing src image
583 template <typename I
>
584 int Migration
<I
>::status(librados::IoCtx
& io_ctx
,
585 const std::string
&image_name
,
586 image_migration_status_t
*status
) {
587 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
589 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< dendl
;
592 librados::IoCtx dest_io_ctx
;
593 std::string dest_image_name
;
594 std::string dest_image_id
;
597 cls::rbd::MigrationState state
;
598 std::string state_description
;
600 int r
= open_source_image(io_ctx
, image_name
, &image_ctx
, &dest_io_ctx
,
601 &dest_image_name
, &dest_image_id
, &flatten
,
602 &mirroring
, &state
, &state_description
);
607 ldout(cct
, 5) << "migrating " << image_ctx
->md_ctx
.get_pool_name() << "/"
608 << image_ctx
->name
<< " -> " << dest_io_ctx
.get_pool_name()
609 << "/" << dest_image_name
<< dendl
;
612 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, dest_image_id
,
613 opts
, flatten
, mirroring
, state
, state_description
,
615 r
= migration
.status(status
);
617 image_ctx
->state
->close();
626 template <typename I
>
627 Migration
<I
>::Migration(I
*src_image_ctx
, librados::IoCtx
& dst_io_ctx
,
628 const std::string
&dstname
,
629 const std::string
&dst_image_id
,
630 ImageOptions
& opts
, bool flatten
, bool mirroring
,
631 cls::rbd::MigrationState state
,
632 const std::string
&state_description
,
633 ProgressContext
*prog_ctx
)
634 : m_cct(static_cast<CephContext
*>(dst_io_ctx
.cct())),
635 m_src_image_ctx(src_image_ctx
), m_dst_io_ctx(dst_io_ctx
),
636 m_src_old_format(m_src_image_ctx
->old_format
),
637 m_src_image_name(m_src_image_ctx
->old_format
? m_src_image_ctx
->name
: ""),
638 m_src_image_id(m_src_image_ctx
->id
),
639 m_src_header_oid(m_src_image_ctx
->header_oid
), m_dst_image_name(dstname
),
640 m_dst_image_id(dst_image_id
.empty() ?
641 util::generate_image_id(m_dst_io_ctx
) : dst_image_id
),
642 m_dst_header_oid(util::header_name(m_dst_image_id
)), m_image_options(opts
),
643 m_flatten(flatten
), m_mirroring(mirroring
), m_prog_ctx(prog_ctx
),
644 m_src_migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_SRC
,
645 m_dst_io_ctx
.get_id(), m_dst_io_ctx
.get_namespace(),
646 m_dst_image_name
, m_dst_image_id
, {}, 0, flatten
,
647 mirroring
, state
, state_description
),
648 m_dst_migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_DST
,
649 src_image_ctx
->md_ctx
.get_id(),
650 src_image_ctx
->md_ctx
.get_namespace(),
651 m_src_image_ctx
->name
, m_src_image_ctx
->id
, {}, 0,
652 flatten
, mirroring
, state
, state_description
) {
653 m_src_io_ctx
.dup(src_image_ctx
->md_ctx
);
656 template <typename I
>
657 int Migration
<I
>::prepare() {
658 ldout(m_cct
, 10) << dendl
;
660 int r
= validate_src_snaps();
665 r
= disable_mirroring(m_src_image_ctx
, &m_mirroring
);
670 r
= unlink_src_image();
672 enable_mirroring(m_src_image_ctx
, m_mirroring
);
679 enable_mirroring(m_src_image_ctx
, m_mirroring
);
683 r
= create_dst_image();
689 ldout(m_cct
, 10) << "succeeded" << dendl
;
694 template <typename I
>
695 int Migration
<I
>::execute() {
696 ldout(m_cct
, 10) << dendl
;
698 auto dst_image_ctx
= I::create(m_dst_image_name
, m_dst_image_id
, nullptr,
699 m_dst_io_ctx
, false);
700 int r
= dst_image_ctx
->state
->open(0);
702 lderr(m_cct
) << "failed to open destination image: " << cpp_strerror(r
)
707 BOOST_SCOPE_EXIT_TPL(dst_image_ctx
) {
708 dst_image_ctx
->state
->close();
709 } BOOST_SCOPE_EXIT_END
;
711 r
= set_state(cls::rbd::MIGRATION_STATE_EXECUTING
, "");
717 MigrationProgressContext
prog_ctx(m_src_io_ctx
, m_src_header_oid
,
718 cls::rbd::MIGRATION_STATE_EXECUTING
,
720 r
= dst_image_ctx
->operations
->migrate(prog_ctx
);
722 RWLock::RLocker
owner_locker(dst_image_ctx
->owner_lock
);
723 if (dst_image_ctx
->exclusive_lock
!= nullptr &&
724 !dst_image_ctx
->exclusive_lock
->accept_ops()) {
725 ldout(m_cct
, 5) << "lost exclusive lock, retrying remote" << dendl
;
732 lderr(m_cct
) << "migration failed: " << cpp_strerror(r
) << dendl
;
736 r
= set_state(cls::rbd::MIGRATION_STATE_EXECUTED
, "");
741 dst_image_ctx
->notify_update();
743 ldout(m_cct
, 10) << "succeeded" << dendl
;
748 template <typename I
>
749 int Migration
<I
>::abort() {
750 ldout(m_cct
, 10) << dendl
;
754 m_src_image_ctx
->owner_lock
.get_read();
755 if (m_src_image_ctx
->exclusive_lock
!= nullptr &&
756 !m_src_image_ctx
->exclusive_lock
->is_lock_owner()) {
758 m_src_image_ctx
->exclusive_lock
->acquire_lock(&ctx
);
759 m_src_image_ctx
->owner_lock
.put_read();
762 lderr(m_cct
) << "error acquiring exclusive lock: " << cpp_strerror(r
)
767 m_src_image_ctx
->owner_lock
.put_read();
770 group_info_t group_info
;
771 group_info
.pool
= -1;
773 auto dst_image_ctx
= I::create(m_dst_image_name
, m_dst_image_id
, nullptr,
774 m_dst_io_ctx
, false);
775 r
= dst_image_ctx
->state
->open(OPEN_FLAG_IGNORE_MIGRATING
);
777 ldout(m_cct
, 1) << "failed to open destination image: " << cpp_strerror(r
)
780 ldout(m_cct
, 10) << "relinking children" << dendl
;
782 r
= relink_children(dst_image_ctx
, m_src_image_ctx
);
787 ldout(m_cct
, 10) << "removing dst image snapshots" << dendl
;
789 BOOST_SCOPE_EXIT_TPL(&dst_image_ctx
) {
790 if (dst_image_ctx
!= nullptr) {
791 dst_image_ctx
->state
->close();
793 } BOOST_SCOPE_EXIT_END
;
795 std::vector
<librbd::snap_info_t
> snaps
;
796 r
= snap_list(dst_image_ctx
, snaps
);
798 lderr(m_cct
) << "failed listing snapshots: " << cpp_strerror(r
)
803 for (auto &snap
: snaps
) {
804 librbd::NoOpProgressContext prog_ctx
;
805 int r
= snap_remove(dst_image_ctx
, snap
.name
.c_str(),
806 RBD_SNAP_REMOVE_UNPROTECT
, prog_ctx
);
808 lderr(m_cct
) << "failed removing snapshot: " << cpp_strerror(r
)
814 ldout(m_cct
, 10) << "removing group" << dendl
;
816 r
= remove_group(dst_image_ctx
, &group_info
);
817 if (r
< 0 && r
!= -ENOENT
) {
821 ldout(m_cct
, 10) << "removing dst image" << dendl
;
823 ceph_assert(dst_image_ctx
->ignore_migrating
);
825 ThreadPool
*thread_pool
;
826 ContextWQ
*op_work_queue
;
827 ImageCtx::get_thread_pool_instance(m_cct
, &thread_pool
, &op_work_queue
);
828 C_SaferCond on_remove
;
829 auto req
= librbd::image::RemoveRequest
<>::create(
830 m_dst_io_ctx
, dst_image_ctx
, false, false, *m_prog_ctx
, op_work_queue
,
833 r
= on_remove
.wait();
835 dst_image_ctx
= nullptr;
838 lderr(m_cct
) << "failed removing destination image '"
839 << m_dst_io_ctx
.get_pool_name() << "/" << m_dst_image_name
840 << " (" << m_dst_image_id
<< ")': " << cpp_strerror(r
)
846 r
= relink_src_image();
851 r
= add_group(m_src_image_ctx
, group_info
);
856 r
= remove_migration(m_src_image_ctx
);
861 r
= enable_mirroring(m_src_image_ctx
, m_mirroring
);
866 ldout(m_cct
, 10) << "succeeded" << dendl
;
871 template <typename I
>
872 int Migration
<I
>::commit() {
873 ldout(m_cct
, 10) << dendl
;
875 BOOST_SCOPE_EXIT_TPL(&m_src_image_ctx
) {
876 if (m_src_image_ctx
!= nullptr) {
877 m_src_image_ctx
->state
->close();
879 } BOOST_SCOPE_EXIT_END
;
881 auto dst_image_ctx
= I::create(m_dst_image_name
, m_dst_image_id
, nullptr,
882 m_dst_io_ctx
, false);
883 int r
= dst_image_ctx
->state
->open(0);
885 lderr(m_cct
) << "failed to open destination image: " << cpp_strerror(r
)
890 BOOST_SCOPE_EXIT_TPL(dst_image_ctx
) {
891 dst_image_ctx
->state
->close();
892 } BOOST_SCOPE_EXIT_END
;
894 r
= remove_migration(dst_image_ctx
);
899 r
= remove_src_image();
904 r
= enable_mirroring(dst_image_ctx
, m_mirroring
);
909 ldout(m_cct
, 10) << "succeeded" << dendl
;
914 template <typename I
>
915 int Migration
<I
>::status(image_migration_status_t
*status
) {
916 ldout(m_cct
, 10) << dendl
;
918 status
->source_pool_id
= m_dst_migration_spec
.pool_id
;
919 status
->source_pool_namespace
= m_dst_migration_spec
.pool_namespace
;
920 status
->source_image_name
= m_dst_migration_spec
.image_name
;
921 status
->source_image_id
= m_dst_migration_spec
.image_id
;
922 status
->dest_pool_id
= m_src_migration_spec
.pool_id
;
923 status
->dest_pool_namespace
= m_src_migration_spec
.pool_namespace
;
924 status
->dest_image_name
= m_src_migration_spec
.image_name
;
925 status
->dest_image_id
= m_src_migration_spec
.image_id
;
927 switch (m_src_migration_spec
.state
) {
928 case cls::rbd::MIGRATION_STATE_ERROR
:
929 status
->state
= RBD_IMAGE_MIGRATION_STATE_ERROR
;
931 case cls::rbd::MIGRATION_STATE_PREPARING
:
932 status
->state
= RBD_IMAGE_MIGRATION_STATE_PREPARING
;
934 case cls::rbd::MIGRATION_STATE_PREPARED
:
935 status
->state
= RBD_IMAGE_MIGRATION_STATE_PREPARED
;
937 case cls::rbd::MIGRATION_STATE_EXECUTING
:
938 status
->state
= RBD_IMAGE_MIGRATION_STATE_EXECUTING
;
940 case cls::rbd::MIGRATION_STATE_EXECUTED
:
941 status
->state
= RBD_IMAGE_MIGRATION_STATE_EXECUTED
;
944 status
->state
= RBD_IMAGE_MIGRATION_STATE_UNKNOWN
;
948 status
->state_description
= m_src_migration_spec
.state_description
;
953 template <typename I
>
954 int Migration
<I
>::set_state(cls::rbd::MigrationState state
,
955 const std::string
&description
) {
956 int r
= cls_client::migration_set_state(&m_src_io_ctx
, m_src_header_oid
,
959 lderr(m_cct
) << "failed to set source migration header: " << cpp_strerror(r
)
964 r
= cls_client::migration_set_state(&m_dst_io_ctx
, m_dst_header_oid
, state
,
967 lderr(m_cct
) << "failed to set destination migration header: "
968 << cpp_strerror(r
) << dendl
;
975 template <typename I
>
976 int Migration
<I
>::list_src_snaps(std::vector
<librbd::snap_info_t
> *snaps
) {
977 ldout(m_cct
, 10) << dendl
;
979 int r
= snap_list(m_src_image_ctx
, *snaps
);
981 lderr(m_cct
) << "failed listing snapshots: " << cpp_strerror(r
) << dendl
;
985 for (auto &snap
: *snaps
) {
986 librbd::snap_namespace_type_t namespace_type
;
987 r
= Snapshot
<I
>::get_namespace_type(m_src_image_ctx
, snap
.id
,
990 lderr(m_cct
) << "error getting snap namespace type: " << cpp_strerror(r
)
995 if (namespace_type
!= RBD_SNAP_NAMESPACE_TYPE_USER
) {
996 if (namespace_type
== RBD_SNAP_NAMESPACE_TYPE_TRASH
) {
997 lderr(m_cct
) << "image has snapshots with linked clones that must be "
998 << "deleted or flattened before the image can be migrated"
1001 lderr(m_cct
) << "image has non-user type snapshots "
1002 << "that are not supported by migration" << dendl
;
1011 template <typename I
>
1012 int Migration
<I
>::validate_src_snaps() {
1013 ldout(m_cct
, 10) << dendl
;
1015 std::vector
<librbd::snap_info_t
> snaps
;
1016 int r
= list_src_snaps(&snaps
);
1021 uint64_t dst_features
= 0;
1022 r
= m_image_options
.get(RBD_IMAGE_OPTION_FEATURES
, &dst_features
);
1023 ceph_assert(r
== 0);
1025 if (!m_src_image_ctx
->test_features(RBD_FEATURE_LAYERING
)) {
1029 for (auto &snap
: snaps
) {
1030 RWLock::RLocker
snap_locker(m_src_image_ctx
->snap_lock
);
1031 cls::rbd::ParentImageSpec parent_spec
{m_src_image_ctx
->md_ctx
.get_id(),
1032 m_src_image_ctx
->md_ctx
.get_namespace(),
1033 m_src_image_ctx
->id
, snap
.id
};
1034 std::vector
<librbd::linked_image_spec_t
> child_images
;
1035 r
= api::Image
<I
>::list_children(m_src_image_ctx
, parent_spec
,
1038 lderr(m_cct
) << "failed listing children: " << cpp_strerror(r
)
1042 if (!child_images
.empty()) {
1043 ldout(m_cct
, 1) << m_src_image_ctx
->name
<< "@" << snap
.name
1044 << " has children" << dendl
;
1046 if ((dst_features
& RBD_FEATURE_LAYERING
) == 0) {
1047 lderr(m_cct
) << "can't migrate to destination without layering feature: "
1048 << "image has children" << dendl
;
1058 template <typename I
>
1059 int Migration
<I
>::set_migration() {
1060 ldout(m_cct
, 10) << dendl
;
1062 m_src_image_ctx
->ignore_migrating
= true;
1064 int r
= cls_client::migration_set(&m_src_io_ctx
, m_src_header_oid
,
1065 m_src_migration_spec
);
1067 lderr(m_cct
) << "failed to set migration header: " << cpp_strerror(r
)
1072 m_src_image_ctx
->notify_update();
1077 template <typename I
>
1078 int Migration
<I
>::remove_migration(I
*image_ctx
) {
1079 ldout(m_cct
, 10) << dendl
;
1083 r
= cls_client::migration_remove(&image_ctx
->md_ctx
, image_ctx
->header_oid
);
1088 lderr(m_cct
) << "failed removing migration header: " << cpp_strerror(r
)
1093 image_ctx
->notify_update();
1098 template <typename I
>
1099 int Migration
<I
>::unlink_src_image() {
1100 if (m_src_old_format
) {
1101 return v1_unlink_src_image();
1103 return v2_unlink_src_image();
1107 template <typename I
>
1108 int Migration
<I
>::v1_unlink_src_image() {
1109 ldout(m_cct
, 10) << dendl
;
1111 int r
= tmap_rm(m_src_io_ctx
, m_src_image_name
);
1113 lderr(m_cct
) << "failed removing " << m_src_image_name
<< " from tmap: "
1114 << cpp_strerror(r
) << dendl
;
1121 template <typename I
>
1122 int Migration
<I
>::v2_unlink_src_image() {
1123 ldout(m_cct
, 10) << dendl
;
1125 m_src_image_ctx
->owner_lock
.get_read();
1126 if (m_src_image_ctx
->exclusive_lock
!= nullptr &&
1127 m_src_image_ctx
->exclusive_lock
->is_lock_owner()) {
1129 m_src_image_ctx
->exclusive_lock
->release_lock(&ctx
);
1130 m_src_image_ctx
->owner_lock
.put_read();
1133 lderr(m_cct
) << "error releasing exclusive lock: " << cpp_strerror(r
)
1138 m_src_image_ctx
->owner_lock
.put_read();
1141 int r
= Trash
<I
>::move(m_src_io_ctx
, RBD_TRASH_IMAGE_SOURCE_MIGRATION
,
1142 m_src_image_ctx
->name
, 0);
1144 lderr(m_cct
) << "failed moving image to trash: " << cpp_strerror(r
)
1152 template <typename I
>
1153 int Migration
<I
>::relink_src_image() {
1154 if (m_src_old_format
) {
1155 return v1_relink_src_image();
1157 return v2_relink_src_image();
1161 template <typename I
>
1162 int Migration
<I
>::v1_relink_src_image() {
1163 ldout(m_cct
, 10) << dendl
;
1165 int r
= tmap_set(m_src_io_ctx
, m_src_image_name
);
1167 lderr(m_cct
) << "failed adding " << m_src_image_name
<< " to tmap: "
1168 << cpp_strerror(r
) << dendl
;
1175 template <typename I
>
1176 int Migration
<I
>::v2_relink_src_image() {
1177 ldout(m_cct
, 10) << dendl
;
1179 int r
= Trash
<I
>::restore(m_src_io_ctx
,
1180 {cls::rbd::TRASH_IMAGE_SOURCE_MIGRATION
},
1181 m_src_image_ctx
->id
, m_src_image_ctx
->name
);
1183 lderr(m_cct
) << "failed restoring image from trash: " << cpp_strerror(r
)
1191 template <typename I
>
1192 int Migration
<I
>::create_dst_image() {
1193 ldout(m_cct
, 10) << dendl
;
1196 cls::rbd::ParentImageSpec parent_spec
;
1198 RWLock::RLocker
snap_locker(m_src_image_ctx
->snap_lock
);
1199 RWLock::RLocker
parent_locker(m_src_image_ctx
->parent_lock
);
1200 size
= m_src_image_ctx
->size
;
1202 // use oldest snapshot or HEAD for parent spec
1203 if (!m_src_image_ctx
->snap_info
.empty()) {
1204 parent_spec
= m_src_image_ctx
->snap_info
.begin()->second
.parent
.spec
;
1206 parent_spec
= m_src_image_ctx
->parent_md
.spec
;
1210 ThreadPool
*thread_pool
;
1211 ContextWQ
*op_work_queue
;
1212 ImageCtx::get_thread_pool_instance(m_cct
, &thread_pool
, &op_work_queue
);
1214 ConfigProxy config
{m_cct
->_conf
};
1215 api::Config
<I
>::apply_pool_overrides(m_dst_io_ctx
, &config
);
1218 C_SaferCond on_create
;
1219 librados::IoCtx parent_io_ctx
;
1220 if (parent_spec
.pool_id
== -1) {
1221 auto *req
= image::CreateRequest
<I
>::create(
1222 config
, m_dst_io_ctx
, m_dst_image_name
, m_dst_image_id
, size
,
1223 m_image_options
, "", "", true /* skip_mirror_enable */, op_work_queue
,
1227 r
= util::create_ioctx(m_src_image_ctx
->md_ctx
, "destination image",
1228 parent_spec
.pool_id
, parent_spec
.pool_namespace
,
1234 auto *req
= image::CloneRequest
<I
>::create(
1235 config
, parent_io_ctx
, parent_spec
.image_id
, "", parent_spec
.snap_id
,
1236 m_dst_io_ctx
, m_dst_image_name
, m_dst_image_id
, m_image_options
, "", "",
1237 op_work_queue
, &on_create
);
1241 r
= on_create
.wait();
1243 lderr(m_cct
) << "header creation failed: " << cpp_strerror(r
) << dendl
;
1247 auto dst_image_ctx
= I::create(m_dst_image_name
, m_dst_image_id
, nullptr,
1248 m_dst_io_ctx
, false);
1250 r
= dst_image_ctx
->state
->open(OPEN_FLAG_IGNORE_MIGRATING
);
1252 lderr(m_cct
) << "failed to open newly created header: " << cpp_strerror(r
)
1257 BOOST_SCOPE_EXIT_TPL(dst_image_ctx
) {
1258 dst_image_ctx
->state
->close();
1259 } BOOST_SCOPE_EXIT_END
;
1262 RWLock::RLocker
owner_locker(dst_image_ctx
->owner_lock
);
1263 r
= dst_image_ctx
->operations
->prepare_image_update(
1264 exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL
, true);
1266 lderr(m_cct
) << "cannot obtain exclusive lock" << dendl
;
1269 if (dst_image_ctx
->exclusive_lock
!= nullptr) {
1270 dst_image_ctx
->exclusive_lock
->block_requests(0);
1276 C_SaferCond on_snapshot_copy
;
1277 auto snapshot_copy_req
= librbd::deep_copy::SnapshotCopyRequest
<I
>::create(
1278 m_src_image_ctx
, dst_image_ctx
, CEPH_NOSNAP
, m_flatten
,
1279 m_src_image_ctx
->op_work_queue
, &snap_seqs
, &on_snapshot_copy
);
1280 snapshot_copy_req
->send();
1281 r
= on_snapshot_copy
.wait();
1283 lderr(m_cct
) << "failed to copy snapshots: " << cpp_strerror(r
) << dendl
;
1287 C_SaferCond on_metadata_copy
;
1288 auto metadata_copy_req
= librbd::deep_copy::MetadataCopyRequest
<I
>::create(
1289 m_src_image_ctx
, dst_image_ctx
, &on_metadata_copy
);
1290 metadata_copy_req
->send();
1291 r
= on_metadata_copy
.wait();
1293 lderr(m_cct
) << "failed to copy metadata: " << cpp_strerror(r
) << dendl
;
1297 m_dst_migration_spec
= {cls::rbd::MIGRATION_HEADER_TYPE_DST
,
1298 m_src_io_ctx
.get_id(), m_src_io_ctx
.get_namespace(),
1299 m_src_image_name
, m_src_image_id
, snap_seqs
, size
,
1300 m_flatten
, m_mirroring
,
1301 cls::rbd::MIGRATION_STATE_PREPARING
, ""};
1303 r
= cls_client::migration_set(&m_dst_io_ctx
, m_dst_header_oid
,
1304 m_dst_migration_spec
);
1306 lderr(m_cct
) << "failed to set migration header: " << cpp_strerror(r
)
1311 r
= update_group(m_src_image_ctx
, dst_image_ctx
);
1316 r
= set_state(cls::rbd::MIGRATION_STATE_PREPARED
, "");
1321 r
= dst_image_ctx
->state
->refresh();
1323 lderr(m_cct
) << "failed to refresh destination image: " << cpp_strerror(r
)
1328 r
= relink_children(m_src_image_ctx
, dst_image_ctx
);
1336 template <typename I
>
1337 int Migration
<I
>::remove_group(I
*image_ctx
, group_info_t
*group_info
) {
1338 int r
= librbd::api::Group
<I
>::image_get_group(image_ctx
, group_info
);
1340 lderr(m_cct
) << "failed to get image group: " << cpp_strerror(r
) << dendl
;
1344 if (group_info
->pool
== -1) {
1348 ceph_assert(!image_ctx
->id
.empty());
1350 ldout(m_cct
, 10) << dendl
;
1353 r
= util::create_ioctx(image_ctx
->md_ctx
, "group", group_info
->pool
, {},
1359 r
= librbd::api::Group
<I
>::image_remove_by_id(group_ioctx
,
1360 group_info
->name
.c_str(),
1362 image_ctx
->id
.c_str());
1364 lderr(m_cct
) << "failed to remove image from group: " << cpp_strerror(r
)
1372 template <typename I
>
1373 int Migration
<I
>::add_group(I
*image_ctx
, group_info_t
&group_info
) {
1374 if (group_info
.pool
== -1) {
1378 ldout(m_cct
, 10) << dendl
;
1381 int r
= util::create_ioctx(image_ctx
->md_ctx
, "group", group_info
.pool
, {},
1387 r
= librbd::api::Group
<I
>::image_add(group_ioctx
, group_info
.name
.c_str(),
1389 image_ctx
->name
.c_str());
1391 lderr(m_cct
) << "failed to add image to group: " << cpp_strerror(r
)
1399 template <typename I
>
1400 int Migration
<I
>::update_group(I
*from_image_ctx
, I
*to_image_ctx
) {
1401 ldout(m_cct
, 10) << dendl
;
1403 group_info_t group_info
;
1405 int r
= remove_group(from_image_ctx
, &group_info
);
1407 return r
== -ENOENT
? 0 : r
;
1410 r
= add_group(to_image_ctx
, group_info
);
1418 template <typename I
>
1419 int Migration
<I
>::disable_mirroring(I
*image_ctx
, bool *was_enabled
) {
1420 *was_enabled
= false;
1422 if (!image_ctx
->test_features(RBD_FEATURE_JOURNALING
)) {
1426 cls::rbd::MirrorImage mirror_image
;
1427 int r
= cls_client::mirror_image_get(&image_ctx
->md_ctx
, image_ctx
->id
,
1430 ldout(m_cct
, 10) << "mirroring is not enabled for this image" << dendl
;
1435 lderr(m_cct
) << "failed to retrieve mirror image: " << cpp_strerror(r
)
1440 if (mirror_image
.state
== cls::rbd::MIRROR_IMAGE_STATE_ENABLED
) {
1441 *was_enabled
= true;
1444 ldout(m_cct
, 10) << dendl
;
1447 auto req
= mirror::DisableRequest
<I
>::create(image_ctx
, false, true, &ctx
);
1451 lderr(m_cct
) << "failed to disable mirroring: " << cpp_strerror(r
)
1456 m_src_migration_spec
.mirroring
= true;
1461 template <typename I
>
1462 int Migration
<I
>::enable_mirroring(I
*image_ctx
, bool was_enabled
) {
1464 if (!image_ctx
->test_features(RBD_FEATURE_JOURNALING
)) {
1468 cls::rbd::MirrorMode mirror_mode
;
1469 int r
= cls_client::mirror_mode_get(&image_ctx
->md_ctx
, &mirror_mode
);
1470 if (r
< 0 && r
!= -ENOENT
) {
1471 lderr(m_cct
) << "failed to retrieve mirror mode: " << cpp_strerror(r
)
1476 if (mirror_mode
== cls::rbd::MIRROR_MODE_DISABLED
) {
1477 ldout(m_cct
, 10) << "mirroring is not enabled for destination pool"
1481 if (mirror_mode
== cls::rbd::MIRROR_MODE_IMAGE
&& !was_enabled
) {
1482 ldout(m_cct
, 10) << "mirroring is not enabled for image" << dendl
;
1486 ldout(m_cct
, 10) << dendl
;
1489 auto req
= mirror::EnableRequest
<I
>::create(image_ctx
->md_ctx
, image_ctx
->id
,
1490 "", image_ctx
->op_work_queue
,
1495 lderr(m_cct
) << "failed to enable mirroring: " << cpp_strerror(r
)
1503 // When relinking children we should be careful as it my be interrupted
1504 // at any moment by some reason and we may end up in an inconsistent
1505 // state, which we have to be able to fix with "migration abort". Below
1506 // are all possible states during migration (P1 - sourse parent, P2 -
1507 // destination parent, C - child):
1509 // P1 P2 P1 P2 P1 P2 P1 P2
1516 // (1) and (4) are the initial and the final consistent states. (2)
1517 // and (3) are intermediate inconsistent states that have to be fixed
1518 // by relink_children running in "migration abort" mode. For this, it
1519 // scans P2 for all children attached and relinks (fixes) states (3)
1520 // and (4) to state (1). Then it scans P1 for remaining children and
1521 // fixes the states (2).
1523 template <typename I
>
1524 int Migration
<I
>::relink_children(I
*from_image_ctx
, I
*to_image_ctx
) {
1525 ldout(m_cct
, 10) << dendl
;
1527 std::vector
<librbd::snap_info_t
> snaps
;
1528 int r
= list_src_snaps(&snaps
);
1533 bool migration_abort
= (to_image_ctx
== m_src_image_ctx
);
1535 for (auto it
= snaps
.begin(); it
!= snaps
.end(); it
++) {
1537 std::vector
<librbd::linked_image_spec_t
> src_child_images
;
1539 if (from_image_ctx
!= m_src_image_ctx
) {
1540 ceph_assert(migration_abort
);
1542 // We run list snaps against the src image to get only those snapshots
1543 // that are migrated. If the "from" image is not the src image
1544 // (abort migration case), we need to remap snap ids.
1545 // Also collect the list of the children currently attached to the
1546 // source, so we could make a proper decision later about relinking.
1548 RWLock::RLocker
src_snap_locker(to_image_ctx
->snap_lock
);
1549 cls::rbd::ParentImageSpec src_parent_spec
{to_image_ctx
->md_ctx
.get_id(),
1550 to_image_ctx
->md_ctx
.get_namespace(),
1551 to_image_ctx
->id
, snap
.id
};
1552 r
= api::Image
<I
>::list_children(to_image_ctx
, src_parent_spec
,
1555 lderr(m_cct
) << "failed listing children: " << cpp_strerror(r
)
1560 RWLock::RLocker
snap_locker(from_image_ctx
->snap_lock
);
1561 snap
.id
= from_image_ctx
->get_snap_id(cls::rbd::UserSnapshotNamespace(),
1563 if (snap
.id
== CEPH_NOSNAP
) {
1564 ldout(m_cct
, 5) << "skipping snapshot " << snap
.name
<< dendl
;
1569 std::vector
<librbd::linked_image_spec_t
> child_images
;
1571 RWLock::RLocker
snap_locker(from_image_ctx
->snap_lock
);
1572 cls::rbd::ParentImageSpec parent_spec
{from_image_ctx
->md_ctx
.get_id(),
1573 from_image_ctx
->md_ctx
.get_namespace(),
1574 from_image_ctx
->id
, snap
.id
};
1575 r
= api::Image
<I
>::list_children(from_image_ctx
, parent_spec
,
1578 lderr(m_cct
) << "failed listing children: " << cpp_strerror(r
)
1584 for (auto &child_image
: child_images
) {
1585 r
= relink_child(from_image_ctx
, to_image_ctx
, snap
, child_image
,
1586 migration_abort
, true);
1591 src_child_images
.erase(std::remove(src_child_images
.begin(),
1592 src_child_images
.end(), child_image
),
1593 src_child_images
.end());
1596 for (auto &child_image
: src_child_images
) {
1597 r
= relink_child(from_image_ctx
, to_image_ctx
, snap
, child_image
,
1598 migration_abort
, false);
1608 template <typename I
>
1609 int Migration
<I
>::relink_child(I
*from_image_ctx
, I
*to_image_ctx
,
1610 const librbd::snap_info_t
&from_snap
,
1611 const librbd::linked_image_spec_t
&child_image
,
1612 bool migration_abort
, bool reattach_child
) {
1613 ldout(m_cct
, 10) << from_snap
.name
<< " " << child_image
.pool_name
<< "/"
1614 << child_image
.pool_namespace
<< "/"
1615 << child_image
.image_name
<< " (migration_abort="
1616 << migration_abort
<< ", reattach_child=" << reattach_child
1619 librados::snap_t to_snap_id
;
1621 RWLock::RLocker
snap_locker(to_image_ctx
->snap_lock
);
1622 to_snap_id
= to_image_ctx
->get_snap_id(cls::rbd::UserSnapshotNamespace(),
1624 if (to_snap_id
== CEPH_NOSNAP
) {
1625 lderr(m_cct
) << "no snapshot " << from_snap
.name
<< " on destination image"
1631 librados::IoCtx child_io_ctx
;
1632 int r
= util::create_ioctx(to_image_ctx
->md_ctx
,
1633 "child image " + child_image
.image_name
,
1634 child_image
.pool_id
, child_image
.pool_namespace
,
1640 I
*child_image_ctx
= I::create("", child_image
.image_id
, nullptr,
1641 child_io_ctx
, false);
1642 r
= child_image_ctx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
1644 lderr(m_cct
) << "failed to open child image: " << cpp_strerror(r
) << dendl
;
1647 BOOST_SCOPE_EXIT_TPL(child_image_ctx
) {
1648 child_image_ctx
->state
->close();
1649 } BOOST_SCOPE_EXIT_END
;
1651 uint32_t clone_format
= 1;
1652 if (child_image_ctx
->test_op_features(RBD_OPERATION_FEATURE_CLONE_CHILD
)) {
1656 cls::rbd::ParentImageSpec parent_spec
;
1657 uint64_t parent_overlap
;
1659 RWLock::RLocker
snap_locker(child_image_ctx
->snap_lock
);
1660 RWLock::RLocker
parent_locker(child_image_ctx
->parent_lock
);
1662 // use oldest snapshot or HEAD for parent spec
1663 if (!child_image_ctx
->snap_info
.empty()) {
1664 parent_spec
= child_image_ctx
->snap_info
.begin()->second
.parent
.spec
;
1665 parent_overlap
= child_image_ctx
->snap_info
.begin()->second
.parent
.overlap
;
1667 parent_spec
= child_image_ctx
->parent_md
.spec
;
1668 parent_overlap
= child_image_ctx
->parent_md
.overlap
;
1672 if (migration_abort
&&
1673 parent_spec
.pool_id
== to_image_ctx
->md_ctx
.get_id() &&
1674 parent_spec
.pool_namespace
== to_image_ctx
->md_ctx
.get_namespace() &&
1675 parent_spec
.image_id
== to_image_ctx
->id
&&
1676 parent_spec
.snap_id
== to_snap_id
) {
1677 ldout(m_cct
, 10) << "no need for parent re-attach" << dendl
;
1679 if (parent_spec
.pool_id
!= from_image_ctx
->md_ctx
.get_id() ||
1680 parent_spec
.pool_namespace
!= from_image_ctx
->md_ctx
.get_namespace() ||
1681 parent_spec
.image_id
!= from_image_ctx
->id
||
1682 parent_spec
.snap_id
!= from_snap
.id
) {
1683 lderr(m_cct
) << "parent is not source image: " << parent_spec
.pool_id
1684 << "/" << parent_spec
.pool_namespace
<< "/"
1685 << parent_spec
.image_id
<< "@" << parent_spec
.snap_id
1690 parent_spec
.pool_id
= to_image_ctx
->md_ctx
.get_id();
1691 parent_spec
.pool_namespace
= to_image_ctx
->md_ctx
.get_namespace();
1692 parent_spec
.image_id
= to_image_ctx
->id
;
1693 parent_spec
.snap_id
= to_snap_id
;
1695 C_SaferCond on_reattach_parent
;
1696 auto reattach_parent_req
= image::AttachParentRequest
<I
>::create(
1697 *child_image_ctx
, parent_spec
, parent_overlap
, true, &on_reattach_parent
);
1698 reattach_parent_req
->send();
1699 r
= on_reattach_parent
.wait();
1701 lderr(m_cct
) << "failed to re-attach parent: " << cpp_strerror(r
) << dendl
;
1706 if (reattach_child
) {
1707 C_SaferCond on_reattach_child
;
1708 auto reattach_child_req
= image::AttachChildRequest
<I
>::create(
1709 child_image_ctx
, to_image_ctx
, to_snap_id
, from_image_ctx
, from_snap
.id
,
1710 clone_format
, &on_reattach_child
);
1711 reattach_child_req
->send();
1712 r
= on_reattach_child
.wait();
1714 lderr(m_cct
) << "failed to re-attach child: " << cpp_strerror(r
) << dendl
;
1719 child_image_ctx
->notify_update();
1724 template <typename I
>
1725 int Migration
<I
>::remove_src_image() {
1726 ldout(m_cct
, 10) << dendl
;
1728 std::vector
<librbd::snap_info_t
> snaps
;
1729 int r
= list_src_snaps(&snaps
);
1734 for (auto it
= snaps
.rbegin(); it
!= snaps
.rend(); it
++) {
1737 librbd::NoOpProgressContext prog_ctx
;
1738 int r
= snap_remove(m_src_image_ctx
, snap
.name
.c_str(),
1739 RBD_SNAP_REMOVE_UNPROTECT
, prog_ctx
);
1741 lderr(m_cct
) << "failed removing source image snapshot '" << snap
.name
1742 << "': " << cpp_strerror(r
) << dendl
;
1747 ceph_assert(m_src_image_ctx
->ignore_migrating
);
1749 ThreadPool
*thread_pool
;
1750 ContextWQ
*op_work_queue
;
1751 ImageCtx::get_thread_pool_instance(m_cct
, &thread_pool
, &op_work_queue
);
1752 C_SaferCond on_remove
;
1753 auto req
= librbd::image::RemoveRequest
<I
>::create(
1754 m_src_io_ctx
, m_src_image_ctx
, false, true, *m_prog_ctx
, op_work_queue
,
1757 r
= on_remove
.wait();
1759 m_src_image_ctx
= nullptr;
1761 // For old format image it will return -ENOENT due to expected
1762 // tmap_rm failure at the end.
1763 if (r
< 0 && r
!= -ENOENT
) {
1764 lderr(m_cct
) << "failed removing source image: " << cpp_strerror(r
)
1769 if (!m_src_image_id
.empty()) {
1770 r
= cls_client::trash_remove(&m_src_io_ctx
, m_src_image_id
);
1771 if (r
< 0 && r
!= -ENOENT
) {
1772 lderr(m_cct
) << "error removing image " << m_src_image_id
1773 << " from rbd_trash object" << dendl
;
1781 } // namespace librbd
1783 template class librbd::api::Migration
<librbd::ImageCtx
>;