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 "common/Cond.h"
10 #include "cls/rbd/cls_rbd_client.h"
11 #include "librbd/ExclusiveLock.h"
12 #include "librbd/ImageCtx.h"
13 #include "librbd/ImageState.h"
14 #include "librbd/Operations.h"
15 #include "librbd/Utils.h"
16 #include "librbd/api/Config.h"
17 #include "librbd/api/Group.h"
18 #include "librbd/api/Image.h"
19 #include "librbd/api/Snapshot.h"
20 #include "librbd/api/Trash.h"
21 #include "librbd/deep_copy/MetadataCopyRequest.h"
22 #include "librbd/deep_copy/SnapshotCopyRequest.h"
23 #include "librbd/exclusive_lock/Policy.h"
24 #include "librbd/image/AttachChildRequest.h"
25 #include "librbd/image/AttachParentRequest.h"
26 #include "librbd/image/CloneRequest.h"
27 #include "librbd/image/CreateRequest.h"
28 #include "librbd/image/DetachChildRequest.h"
29 #include "librbd/image/DetachParentRequest.h"
30 #include "librbd/image/ListWatchersRequest.h"
31 #include "librbd/image/RemoveRequest.h"
32 #include "librbd/internal.h"
33 #include "librbd/io/ImageRequestWQ.h"
34 #include "librbd/mirror/DisableRequest.h"
35 #include "librbd/mirror/EnableRequest.h"
37 #include <boost/scope_exit.hpp>
39 #define dout_subsys ceph_subsys_rbd
41 #define dout_prefix *_dout << "librbd::Migration: " << __func__ << ": "
45 inline bool operator==(const linked_image_spec_t
& rhs
,
46 const linked_image_spec_t
& lhs
) {
47 bool result
= (rhs
.pool_id
== lhs
.pool_id
&&
48 rhs
.pool_namespace
== lhs
.pool_namespace
&&
49 rhs
.image_id
== lhs
.image_id
);
55 using util::create_rados_callback
;
59 class MigrationProgressContext
: public ProgressContext
{
61 MigrationProgressContext(librados::IoCtx
& io_ctx
,
62 const std::string
&header_oid
,
63 cls::rbd::MigrationState state
,
64 ProgressContext
*prog_ctx
)
65 : m_io_ctx(io_ctx
), m_header_oid(header_oid
), m_state(state
),
66 m_prog_ctx(prog_ctx
), m_cct(reinterpret_cast<CephContext
*>(io_ctx
.cct())),
67 m_lock(ceph::make_mutex(
68 util::unique_lock_name("librbd::api::MigrationProgressContext",
70 ceph_assert(m_prog_ctx
!= nullptr);
73 ~MigrationProgressContext() {
74 wait_for_in_flight_updates();
77 int update_progress(uint64_t offset
, uint64_t total
) override
{
78 ldout(m_cct
, 20) << "offset=" << offset
<< ", total=" << total
<< dendl
;
80 m_prog_ctx
->update_progress(offset
, total
);
82 std::string description
= stringify(offset
* 100 / total
) + "% complete";
84 send_state_description_update(description
);
90 librados::IoCtx
& m_io_ctx
;
91 std::string m_header_oid
;
92 cls::rbd::MigrationState m_state
;
93 ProgressContext
*m_prog_ctx
;
96 mutable ceph::mutex m_lock
;
97 ceph::condition_variable m_cond
;
98 std::string m_state_description
;
99 bool m_pending_update
= false;
100 int m_in_flight_state_updates
= 0;
102 void send_state_description_update(const std::string
&description
) {
103 std::lock_guard locker
{m_lock
};
105 if (description
== m_state_description
) {
109 m_state_description
= description
;
111 if (m_in_flight_state_updates
> 0) {
112 m_pending_update
= true;
116 set_state_description();
119 void set_state_description() {
120 ldout(m_cct
, 20) << "state_description=" << m_state_description
<< dendl
;
122 ceph_assert(ceph_mutex_is_locked(m_lock
));
124 librados::ObjectWriteOperation op
;
125 cls_client::migration_set_state(&op
, m_state
, m_state_description
);
127 using klass
= MigrationProgressContext
;
128 librados::AioCompletion
*comp
=
129 create_rados_callback
<klass
, &klass::handle_set_state_description
>(this);
130 int r
= m_io_ctx
.aio_operate(m_header_oid
, comp
, &op
);
134 m_in_flight_state_updates
++;
137 void handle_set_state_description(int r
) {
138 ldout(m_cct
, 20) << "r=" << r
<< dendl
;
140 std::lock_guard locker
{m_lock
};
142 m_in_flight_state_updates
--;
145 lderr(m_cct
) << "failed to update migration state: " << cpp_strerror(r
)
147 } else if (m_pending_update
) {
148 set_state_description();
149 m_pending_update
= false;
155 void wait_for_in_flight_updates() {
156 std::unique_lock locker
{m_lock
};
158 ldout(m_cct
, 20) << "m_in_flight_state_updates="
159 << m_in_flight_state_updates
<< dendl
;
160 m_pending_update
= false;
161 m_cond
.wait(locker
, [this] { return m_in_flight_state_updates
<= 0; });
165 int trash_search(librados::IoCtx
&io_ctx
, rbd_trash_image_source_t source
,
166 const std::string
&image_name
, std::string
*image_id
) {
167 std::vector
<trash_image_info_t
> entries
;
169 int r
= Trash
<>::list(io_ctx
, entries
, false);
174 for (auto &entry
: entries
) {
175 if (entry
.source
== source
&& entry
.name
== image_name
) {
176 *image_id
= entry
.id
;
184 template <typename I
>
185 int open_source_image(librados::IoCtx
& io_ctx
, const std::string
&image_name
,
186 I
**src_image_ctx
, librados::IoCtx
*dst_io_ctx
,
187 std::string
*dst_image_name
, std::string
*dst_image_id
,
188 bool *flatten
, bool *mirroring
,
189 cls::rbd::MirrorImageMode
*mirror_image_mode
,
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 *mirror_image_mode
= migration_spec
.mirror_image_mode
;
333 *state
= migration_spec
.state
;
334 *state_description
= migration_spec
.state_description
;
339 } // anonymous namespace
341 template <typename I
>
342 int Migration
<I
>::prepare(librados::IoCtx
& io_ctx
,
343 const std::string
&image_name
,
344 librados::IoCtx
& dest_io_ctx
,
345 const std::string
&dest_image_name_
,
346 ImageOptions
& opts
) {
347 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
349 std::string dest_image_name
= dest_image_name_
.empty() ? image_name
:
352 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< " -> "
353 << dest_io_ctx
.get_pool_name() << "/" << dest_image_name
354 << ", opts=" << opts
<< dendl
;
356 auto image_ctx
= I::create(image_name
, "", nullptr, io_ctx
, false);
357 int r
= image_ctx
->state
->open(0);
359 lderr(cct
) << "failed to open image: " << cpp_strerror(r
) << dendl
;
362 BOOST_SCOPE_EXIT_TPL(image_ctx
) {
363 image_ctx
->state
->close();
364 } BOOST_SCOPE_EXIT_END
;
366 std::list
<obj_watch_t
> watchers
;
367 int flags
= librbd::image::LIST_WATCHERS_FILTER_OUT_MY_INSTANCE
|
368 librbd::image::LIST_WATCHERS_FILTER_OUT_MIRROR_INSTANCES
;
369 C_SaferCond on_list_watchers
;
370 auto list_watchers_request
= librbd::image::ListWatchersRequest
<I
>::create(
371 *image_ctx
, flags
, &watchers
, &on_list_watchers
);
372 list_watchers_request
->send();
373 r
= on_list_watchers
.wait();
375 lderr(cct
) << "failed listing watchers:" << cpp_strerror(r
) << dendl
;
378 if (!watchers
.empty()) {
379 lderr(cct
) << "image has watchers - not migrating" << dendl
;
384 if (opts
.get(RBD_IMAGE_OPTION_FORMAT
, &format
) != 0) {
385 opts
.set(RBD_IMAGE_OPTION_FORMAT
, format
);
388 lderr(cct
) << "unsupported destination image format: " << format
<< dendl
;
394 std::shared_lock image_locker
{image_ctx
->image_lock
};
395 features
= image_ctx
->features
;
397 opts
.get(RBD_IMAGE_OPTION_FEATURES
, &features
);
398 if ((features
& ~RBD_FEATURES_ALL
) != 0) {
399 lderr(cct
) << "librbd does not support requested features" << dendl
;
402 features
&= ~RBD_FEATURES_INTERNAL
;
403 features
|= RBD_FEATURE_MIGRATING
;
404 opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
406 uint64_t order
= image_ctx
->order
;
407 if (opts
.get(RBD_IMAGE_OPTION_ORDER
, &order
) != 0) {
408 opts
.set(RBD_IMAGE_OPTION_ORDER
, order
);
410 r
= image::CreateRequest
<I
>::validate_order(cct
, order
);
415 uint64_t stripe_unit
= image_ctx
->stripe_unit
;
416 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_UNIT
, &stripe_unit
) != 0) {
417 opts
.set(RBD_IMAGE_OPTION_STRIPE_UNIT
, stripe_unit
);
419 uint64_t stripe_count
= image_ctx
->stripe_count
;
420 if (opts
.get(RBD_IMAGE_OPTION_STRIPE_COUNT
, &stripe_count
) != 0) {
421 opts
.set(RBD_IMAGE_OPTION_STRIPE_COUNT
, stripe_count
);
424 uint64_t flatten
= 0;
425 if (opts
.get(RBD_IMAGE_OPTION_FLATTEN
, &flatten
) == 0) {
426 opts
.unset(RBD_IMAGE_OPTION_FLATTEN
);
429 ldout(cct
, 20) << "updated opts=" << opts
<< dendl
;
431 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, "", opts
,
432 flatten
> 0, false, cls::rbd::MIRROR_IMAGE_MODE_JOURNAL
,
433 cls::rbd::MIGRATION_STATE_PREPARING
, "", nullptr);
434 r
= migration
.prepare();
436 features
&= ~RBD_FEATURE_MIGRATING
;
437 opts
.set(RBD_IMAGE_OPTION_FEATURES
, features
);
442 template <typename I
>
443 int Migration
<I
>::execute(librados::IoCtx
& io_ctx
,
444 const std::string
&image_name
,
445 ProgressContext
&prog_ctx
) {
446 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
448 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< dendl
;
451 librados::IoCtx dest_io_ctx
;
452 std::string dest_image_name
;
453 std::string dest_image_id
;
456 cls::rbd::MirrorImageMode mirror_image_mode
;
457 cls::rbd::MigrationState state
;
458 std::string state_description
;
460 int r
= open_source_image(io_ctx
, image_name
, &image_ctx
, &dest_io_ctx
,
461 &dest_image_name
, &dest_image_id
, &flatten
,
462 &mirroring
, &mirror_image_mode
, &state
,
468 BOOST_SCOPE_EXIT_TPL(image_ctx
) {
469 image_ctx
->state
->close();
470 } BOOST_SCOPE_EXIT_END
;
472 if (state
!= cls::rbd::MIGRATION_STATE_PREPARED
) {
473 lderr(cct
) << "current migration state is '" << state
<< "'"
474 << " (should be 'prepared')" << dendl
;
478 ldout(cct
, 5) << "migrating " << image_ctx
->md_ctx
.get_pool_name() << "/"
479 << image_ctx
->name
<< " -> " << dest_io_ctx
.get_pool_name()
480 << "/" << dest_image_name
<< dendl
;
483 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, dest_image_id
,
484 opts
, flatten
, mirroring
, mirror_image_mode
, state
,
485 state_description
, &prog_ctx
);
486 r
= migration
.execute();
494 template <typename I
>
495 int Migration
<I
>::abort(librados::IoCtx
& io_ctx
, const std::string
&image_name
,
496 ProgressContext
&prog_ctx
) {
497 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
499 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< dendl
;
502 librados::IoCtx dest_io_ctx
;
503 std::string dest_image_name
;
504 std::string dest_image_id
;
507 cls::rbd::MirrorImageMode mirror_image_mode
;
508 cls::rbd::MigrationState state
;
509 std::string state_description
;
511 int r
= open_source_image(io_ctx
, image_name
, &image_ctx
, &dest_io_ctx
,
512 &dest_image_name
, &dest_image_id
, &flatten
,
513 &mirroring
, &mirror_image_mode
, &state
,
519 ldout(cct
, 5) << "canceling incomplete migration "
520 << image_ctx
->md_ctx
.get_pool_name() << "/" << image_ctx
->name
521 << " -> " << dest_io_ctx
.get_pool_name() << "/" << dest_image_name
525 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, dest_image_id
,
526 opts
, flatten
, mirroring
, mirror_image_mode
, state
,
527 state_description
, &prog_ctx
);
528 r
= migration
.abort();
530 image_ctx
->state
->close();
539 template <typename I
>
540 int Migration
<I
>::commit(librados::IoCtx
& io_ctx
,
541 const std::string
&image_name
,
542 ProgressContext
&prog_ctx
) {
543 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
545 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< dendl
;
548 librados::IoCtx dest_io_ctx
;
549 std::string dest_image_name
;
550 std::string dest_image_id
;
553 cls::rbd::MirrorImageMode mirror_image_mode
;
554 cls::rbd::MigrationState state
;
555 std::string state_description
;
557 int r
= open_source_image(io_ctx
, image_name
, &image_ctx
, &dest_io_ctx
,
558 &dest_image_name
, &dest_image_id
, &flatten
,
559 &mirroring
, &mirror_image_mode
, &state
,
565 if (state
!= cls::rbd::MIGRATION_STATE_EXECUTED
) {
566 lderr(cct
) << "current migration state is '" << state
<< "'"
567 << " (should be 'executed')" << dendl
;
568 image_ctx
->state
->close();
572 ldout(cct
, 5) << "migrating " << image_ctx
->md_ctx
.get_pool_name() << "/"
573 << image_ctx
->name
<< " -> " << dest_io_ctx
.get_pool_name()
574 << "/" << dest_image_name
<< dendl
;
577 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, dest_image_id
,
578 opts
, flatten
, mirroring
, mirror_image_mode
, state
,
579 state_description
, &prog_ctx
);
580 r
= migration
.commit();
582 // image_ctx is closed in commit when removing src image
591 template <typename I
>
592 int Migration
<I
>::status(librados::IoCtx
& io_ctx
,
593 const std::string
&image_name
,
594 image_migration_status_t
*status
) {
595 CephContext
* cct
= reinterpret_cast<CephContext
*>(io_ctx
.cct());
597 ldout(cct
, 10) << io_ctx
.get_pool_name() << "/" << image_name
<< dendl
;
600 librados::IoCtx dest_io_ctx
;
601 std::string dest_image_name
;
602 std::string dest_image_id
;
605 cls::rbd::MirrorImageMode mirror_image_mode
;
606 cls::rbd::MigrationState state
;
607 std::string state_description
;
609 int r
= open_source_image(io_ctx
, image_name
, &image_ctx
, &dest_io_ctx
,
610 &dest_image_name
, &dest_image_id
, &flatten
,
611 &mirroring
, &mirror_image_mode
, &state
,
617 ldout(cct
, 5) << "migrating " << image_ctx
->md_ctx
.get_pool_name() << "/"
618 << image_ctx
->name
<< " -> " << dest_io_ctx
.get_pool_name()
619 << "/" << dest_image_name
<< dendl
;
622 Migration
migration(image_ctx
, dest_io_ctx
, dest_image_name
, dest_image_id
,
623 opts
, flatten
, mirroring
, mirror_image_mode
, state
,
624 state_description
, nullptr);
625 r
= migration
.status(status
);
627 image_ctx
->state
->close();
636 template <typename I
>
637 Migration
<I
>::Migration(I
*src_image_ctx
, librados::IoCtx
& dst_io_ctx
,
638 const std::string
&dstname
,
639 const std::string
&dst_image_id
,
640 ImageOptions
& opts
, bool flatten
, bool mirroring
,
641 cls::rbd::MirrorImageMode mirror_image_mode
,
642 cls::rbd::MigrationState state
,
643 const std::string
&state_description
,
644 ProgressContext
*prog_ctx
)
645 : m_cct(static_cast<CephContext
*>(dst_io_ctx
.cct())),
646 m_src_image_ctx(src_image_ctx
), m_dst_io_ctx(dst_io_ctx
),
647 m_src_old_format(m_src_image_ctx
->old_format
),
648 m_src_image_name(m_src_image_ctx
->old_format
? m_src_image_ctx
->name
: ""),
649 m_src_image_id(m_src_image_ctx
->id
),
650 m_src_header_oid(m_src_image_ctx
->header_oid
), m_dst_image_name(dstname
),
651 m_dst_image_id(dst_image_id
.empty() ?
652 util::generate_image_id(m_dst_io_ctx
) : dst_image_id
),
653 m_dst_header_oid(util::header_name(m_dst_image_id
)), m_image_options(opts
),
654 m_flatten(flatten
), m_mirroring(mirroring
),
655 m_mirror_image_mode(mirror_image_mode
), m_prog_ctx(prog_ctx
),
656 m_src_migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_SRC
,
657 m_dst_io_ctx
.get_id(), m_dst_io_ctx
.get_namespace(),
658 m_dst_image_name
, m_dst_image_id
, {}, 0, mirroring
,
659 mirror_image_mode
, flatten
, state
, state_description
),
660 m_dst_migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_DST
,
661 src_image_ctx
->md_ctx
.get_id(),
662 src_image_ctx
->md_ctx
.get_namespace(),
663 m_src_image_ctx
->name
, m_src_image_ctx
->id
, {}, 0,
664 mirroring
, mirror_image_mode
, flatten
, state
,
666 m_src_io_ctx
.dup(src_image_ctx
->md_ctx
);
669 template <typename I
>
670 int Migration
<I
>::prepare() {
671 ldout(m_cct
, 10) << dendl
;
673 int r
= validate_src_snaps();
678 r
= disable_mirroring(m_src_image_ctx
, &m_mirroring
, &m_mirror_image_mode
);
683 r
= unlink_src_image();
685 enable_mirroring(m_src_image_ctx
, m_mirroring
, m_mirror_image_mode
);
692 enable_mirroring(m_src_image_ctx
, m_mirroring
, m_mirror_image_mode
);
696 r
= create_dst_image();
702 ldout(m_cct
, 10) << "succeeded" << dendl
;
707 template <typename I
>
708 int Migration
<I
>::execute() {
709 ldout(m_cct
, 10) << dendl
;
711 auto dst_image_ctx
= I::create(m_dst_image_name
, m_dst_image_id
, nullptr,
712 m_dst_io_ctx
, false);
713 int r
= dst_image_ctx
->state
->open(0);
715 lderr(m_cct
) << "failed to open destination image: " << cpp_strerror(r
)
720 BOOST_SCOPE_EXIT_TPL(dst_image_ctx
) {
721 dst_image_ctx
->state
->close();
722 } BOOST_SCOPE_EXIT_END
;
724 r
= set_state(cls::rbd::MIGRATION_STATE_EXECUTING
, "");
730 MigrationProgressContext
prog_ctx(m_src_io_ctx
, m_src_header_oid
,
731 cls::rbd::MIGRATION_STATE_EXECUTING
,
733 r
= dst_image_ctx
->operations
->migrate(prog_ctx
);
735 std::shared_lock owner_locker
{dst_image_ctx
->owner_lock
};
736 if (dst_image_ctx
->exclusive_lock
!= nullptr &&
737 !dst_image_ctx
->exclusive_lock
->accept_ops()) {
738 ldout(m_cct
, 5) << "lost exclusive lock, retrying remote" << dendl
;
745 lderr(m_cct
) << "migration failed: " << cpp_strerror(r
) << dendl
;
749 r
= set_state(cls::rbd::MIGRATION_STATE_EXECUTED
, "");
754 dst_image_ctx
->notify_update();
756 ldout(m_cct
, 10) << "succeeded" << dendl
;
761 template <typename I
>
762 int Migration
<I
>::abort() {
763 ldout(m_cct
, 10) << dendl
;
767 m_src_image_ctx
->owner_lock
.lock_shared();
768 if (m_src_image_ctx
->exclusive_lock
!= nullptr &&
769 !m_src_image_ctx
->exclusive_lock
->is_lock_owner()) {
771 m_src_image_ctx
->exclusive_lock
->acquire_lock(&ctx
);
772 m_src_image_ctx
->owner_lock
.unlock_shared();
775 lderr(m_cct
) << "error acquiring exclusive lock: " << cpp_strerror(r
)
780 m_src_image_ctx
->owner_lock
.unlock_shared();
783 group_info_t group_info
;
784 group_info
.pool
= -1;
786 auto dst_image_ctx
= I::create(m_dst_image_name
, m_dst_image_id
, nullptr,
787 m_dst_io_ctx
, false);
788 r
= dst_image_ctx
->state
->open(OPEN_FLAG_IGNORE_MIGRATING
);
790 ldout(m_cct
, 1) << "failed to open destination image: " << cpp_strerror(r
)
793 ldout(m_cct
, 10) << "relinking children" << dendl
;
795 r
= relink_children(dst_image_ctx
, m_src_image_ctx
);
800 ldout(m_cct
, 10) << "removing dst image snapshots" << dendl
;
802 BOOST_SCOPE_EXIT_TPL(&dst_image_ctx
) {
803 if (dst_image_ctx
!= nullptr) {
804 dst_image_ctx
->state
->close();
806 } BOOST_SCOPE_EXIT_END
;
808 std::vector
<librbd::snap_info_t
> snaps
;
809 r
= Snapshot
<I
>::list(dst_image_ctx
, snaps
);
811 lderr(m_cct
) << "failed listing snapshots: " << cpp_strerror(r
)
816 for (auto &snap
: snaps
) {
817 librbd::NoOpProgressContext prog_ctx
;
818 int r
= Snapshot
<I
>::remove(dst_image_ctx
, snap
.name
.c_str(),
819 RBD_SNAP_REMOVE_UNPROTECT
, prog_ctx
);
821 lderr(m_cct
) << "failed removing snapshot: " << cpp_strerror(r
)
827 ldout(m_cct
, 10) << "removing group" << dendl
;
829 r
= remove_group(dst_image_ctx
, &group_info
);
830 if (r
< 0 && r
!= -ENOENT
) {
834 ldout(m_cct
, 10) << "removing dst image" << dendl
;
836 ceph_assert(dst_image_ctx
->ignore_migrating
);
838 ThreadPool
*thread_pool
;
839 ContextWQ
*op_work_queue
;
840 ImageCtx::get_thread_pool_instance(m_cct
, &thread_pool
, &op_work_queue
);
841 C_SaferCond on_remove
;
842 auto req
= librbd::image::RemoveRequest
<>::create(
843 m_dst_io_ctx
, dst_image_ctx
, false, false, *m_prog_ctx
, op_work_queue
,
846 r
= on_remove
.wait();
848 dst_image_ctx
= nullptr;
851 lderr(m_cct
) << "failed removing destination image '"
852 << m_dst_io_ctx
.get_pool_name() << "/" << m_dst_image_name
853 << " (" << m_dst_image_id
<< ")': " << cpp_strerror(r
)
859 r
= relink_src_image();
864 r
= add_group(m_src_image_ctx
, group_info
);
869 r
= remove_migration(m_src_image_ctx
);
874 r
= enable_mirroring(m_src_image_ctx
, m_mirroring
, m_mirror_image_mode
);
879 ldout(m_cct
, 10) << "succeeded" << dendl
;
884 template <typename I
>
885 int Migration
<I
>::commit() {
886 ldout(m_cct
, 10) << dendl
;
888 BOOST_SCOPE_EXIT_TPL(&m_src_image_ctx
) {
889 if (m_src_image_ctx
!= nullptr) {
890 m_src_image_ctx
->state
->close();
892 } BOOST_SCOPE_EXIT_END
;
894 auto dst_image_ctx
= I::create(m_dst_image_name
, m_dst_image_id
, nullptr,
895 m_dst_io_ctx
, false);
896 int r
= dst_image_ctx
->state
->open(0);
898 lderr(m_cct
) << "failed to open destination image: " << cpp_strerror(r
)
903 BOOST_SCOPE_EXIT_TPL(dst_image_ctx
) {
904 dst_image_ctx
->state
->close();
905 } BOOST_SCOPE_EXIT_END
;
907 r
= remove_migration(dst_image_ctx
);
912 r
= remove_src_image();
917 r
= enable_mirroring(dst_image_ctx
, m_mirroring
, m_mirror_image_mode
);
922 ldout(m_cct
, 10) << "succeeded" << dendl
;
927 template <typename I
>
928 int Migration
<I
>::status(image_migration_status_t
*status
) {
929 ldout(m_cct
, 10) << dendl
;
931 status
->source_pool_id
= m_dst_migration_spec
.pool_id
;
932 status
->source_pool_namespace
= m_dst_migration_spec
.pool_namespace
;
933 status
->source_image_name
= m_dst_migration_spec
.image_name
;
934 status
->source_image_id
= m_dst_migration_spec
.image_id
;
935 status
->dest_pool_id
= m_src_migration_spec
.pool_id
;
936 status
->dest_pool_namespace
= m_src_migration_spec
.pool_namespace
;
937 status
->dest_image_name
= m_src_migration_spec
.image_name
;
938 status
->dest_image_id
= m_src_migration_spec
.image_id
;
940 switch (m_src_migration_spec
.state
) {
941 case cls::rbd::MIGRATION_STATE_ERROR
:
942 status
->state
= RBD_IMAGE_MIGRATION_STATE_ERROR
;
944 case cls::rbd::MIGRATION_STATE_PREPARING
:
945 status
->state
= RBD_IMAGE_MIGRATION_STATE_PREPARING
;
947 case cls::rbd::MIGRATION_STATE_PREPARED
:
948 status
->state
= RBD_IMAGE_MIGRATION_STATE_PREPARED
;
950 case cls::rbd::MIGRATION_STATE_EXECUTING
:
951 status
->state
= RBD_IMAGE_MIGRATION_STATE_EXECUTING
;
953 case cls::rbd::MIGRATION_STATE_EXECUTED
:
954 status
->state
= RBD_IMAGE_MIGRATION_STATE_EXECUTED
;
957 status
->state
= RBD_IMAGE_MIGRATION_STATE_UNKNOWN
;
961 status
->state_description
= m_src_migration_spec
.state_description
;
966 template <typename I
>
967 int Migration
<I
>::set_state(cls::rbd::MigrationState state
,
968 const std::string
&description
) {
969 int r
= cls_client::migration_set_state(&m_src_io_ctx
, m_src_header_oid
,
972 lderr(m_cct
) << "failed to set source migration header: " << cpp_strerror(r
)
977 r
= cls_client::migration_set_state(&m_dst_io_ctx
, m_dst_header_oid
, state
,
980 lderr(m_cct
) << "failed to set destination migration header: "
981 << cpp_strerror(r
) << dendl
;
988 template <typename I
>
989 int Migration
<I
>::list_src_snaps(std::vector
<librbd::snap_info_t
> *snaps
) {
990 ldout(m_cct
, 10) << dendl
;
992 int r
= Snapshot
<I
>::list(m_src_image_ctx
, *snaps
);
994 lderr(m_cct
) << "failed listing snapshots: " << cpp_strerror(r
) << dendl
;
998 for (auto &snap
: *snaps
) {
999 librbd::snap_namespace_type_t namespace_type
;
1000 r
= Snapshot
<I
>::get_namespace_type(m_src_image_ctx
, snap
.id
,
1003 lderr(m_cct
) << "error getting snap namespace type: " << cpp_strerror(r
)
1008 if (namespace_type
!= RBD_SNAP_NAMESPACE_TYPE_USER
) {
1009 if (namespace_type
== RBD_SNAP_NAMESPACE_TYPE_TRASH
) {
1010 lderr(m_cct
) << "image has snapshots with linked clones that must be "
1011 << "deleted or flattened before the image can be migrated"
1014 lderr(m_cct
) << "image has non-user type snapshots "
1015 << "that are not supported by migration" << dendl
;
1024 template <typename I
>
1025 int Migration
<I
>::validate_src_snaps() {
1026 ldout(m_cct
, 10) << dendl
;
1028 std::vector
<librbd::snap_info_t
> snaps
;
1029 int r
= list_src_snaps(&snaps
);
1034 uint64_t dst_features
= 0;
1035 r
= m_image_options
.get(RBD_IMAGE_OPTION_FEATURES
, &dst_features
);
1036 ceph_assert(r
== 0);
1038 if (!m_src_image_ctx
->test_features(RBD_FEATURE_LAYERING
)) {
1042 for (auto &snap
: snaps
) {
1043 std::shared_lock image_locker
{m_src_image_ctx
->image_lock
};
1044 cls::rbd::ParentImageSpec parent_spec
{m_src_image_ctx
->md_ctx
.get_id(),
1045 m_src_image_ctx
->md_ctx
.get_namespace(),
1046 m_src_image_ctx
->id
, snap
.id
};
1047 std::vector
<librbd::linked_image_spec_t
> child_images
;
1048 r
= api::Image
<I
>::list_children(m_src_image_ctx
, parent_spec
,
1051 lderr(m_cct
) << "failed listing children: " << cpp_strerror(r
)
1055 if (!child_images
.empty()) {
1056 ldout(m_cct
, 1) << m_src_image_ctx
->name
<< "@" << snap
.name
1057 << " has children" << dendl
;
1059 if ((dst_features
& RBD_FEATURE_LAYERING
) == 0) {
1060 lderr(m_cct
) << "can't migrate to destination without layering feature: "
1061 << "image has children" << dendl
;
1071 template <typename I
>
1072 int Migration
<I
>::set_migration() {
1073 ldout(m_cct
, 10) << dendl
;
1075 m_src_image_ctx
->ignore_migrating
= true;
1077 int r
= cls_client::migration_set(&m_src_io_ctx
, m_src_header_oid
,
1078 m_src_migration_spec
);
1080 lderr(m_cct
) << "failed to set migration header: " << cpp_strerror(r
)
1085 m_src_image_ctx
->notify_update();
1090 template <typename I
>
1091 int Migration
<I
>::remove_migration(I
*image_ctx
) {
1092 ldout(m_cct
, 10) << dendl
;
1096 r
= cls_client::migration_remove(&image_ctx
->md_ctx
, image_ctx
->header_oid
);
1101 lderr(m_cct
) << "failed removing migration header: " << cpp_strerror(r
)
1106 image_ctx
->notify_update();
1111 template <typename I
>
1112 int Migration
<I
>::unlink_src_image() {
1113 if (m_src_old_format
) {
1114 return v1_unlink_src_image();
1116 return v2_unlink_src_image();
1120 template <typename I
>
1121 int Migration
<I
>::v1_unlink_src_image() {
1122 ldout(m_cct
, 10) << dendl
;
1124 int r
= tmap_rm(m_src_io_ctx
, m_src_image_name
);
1126 lderr(m_cct
) << "failed removing " << m_src_image_name
<< " from tmap: "
1127 << cpp_strerror(r
) << dendl
;
1134 template <typename I
>
1135 int Migration
<I
>::v2_unlink_src_image() {
1136 ldout(m_cct
, 10) << dendl
;
1138 m_src_image_ctx
->owner_lock
.lock_shared();
1139 if (m_src_image_ctx
->exclusive_lock
!= nullptr &&
1140 m_src_image_ctx
->exclusive_lock
->is_lock_owner()) {
1142 m_src_image_ctx
->exclusive_lock
->release_lock(&ctx
);
1143 m_src_image_ctx
->owner_lock
.unlock_shared();
1146 lderr(m_cct
) << "error releasing exclusive lock: " << cpp_strerror(r
)
1151 m_src_image_ctx
->owner_lock
.unlock_shared();
1154 int r
= Trash
<I
>::move(m_src_io_ctx
, RBD_TRASH_IMAGE_SOURCE_MIGRATION
,
1155 m_src_image_ctx
->name
, 0);
1157 lderr(m_cct
) << "failed moving image to trash: " << cpp_strerror(r
)
1165 template <typename I
>
1166 int Migration
<I
>::relink_src_image() {
1167 if (m_src_old_format
) {
1168 return v1_relink_src_image();
1170 return v2_relink_src_image();
1174 template <typename I
>
1175 int Migration
<I
>::v1_relink_src_image() {
1176 ldout(m_cct
, 10) << dendl
;
1178 int r
= tmap_set(m_src_io_ctx
, m_src_image_name
);
1180 lderr(m_cct
) << "failed adding " << m_src_image_name
<< " to tmap: "
1181 << cpp_strerror(r
) << dendl
;
1188 template <typename I
>
1189 int Migration
<I
>::v2_relink_src_image() {
1190 ldout(m_cct
, 10) << dendl
;
1192 int r
= Trash
<I
>::restore(m_src_io_ctx
,
1193 {cls::rbd::TRASH_IMAGE_SOURCE_MIGRATION
},
1194 m_src_image_ctx
->id
, m_src_image_ctx
->name
);
1196 lderr(m_cct
) << "failed restoring image from trash: " << cpp_strerror(r
)
1204 template <typename I
>
1205 int Migration
<I
>::create_dst_image() {
1206 ldout(m_cct
, 10) << dendl
;
1209 cls::rbd::ParentImageSpec parent_spec
;
1211 std::shared_lock image_locker
{m_src_image_ctx
->image_lock
};
1212 size
= m_src_image_ctx
->size
;
1214 // use oldest snapshot or HEAD for parent spec
1215 if (!m_src_image_ctx
->snap_info
.empty()) {
1216 parent_spec
= m_src_image_ctx
->snap_info
.begin()->second
.parent
.spec
;
1218 parent_spec
= m_src_image_ctx
->parent_md
.spec
;
1222 ThreadPool
*thread_pool
;
1223 ContextWQ
*op_work_queue
;
1224 ImageCtx::get_thread_pool_instance(m_cct
, &thread_pool
, &op_work_queue
);
1226 ConfigProxy config
{m_cct
->_conf
};
1227 api::Config
<I
>::apply_pool_overrides(m_dst_io_ctx
, &config
);
1230 C_SaferCond on_create
;
1231 librados::IoCtx parent_io_ctx
;
1232 if (parent_spec
.pool_id
== -1) {
1233 auto *req
= image::CreateRequest
<I
>::create(
1234 config
, m_dst_io_ctx
, m_dst_image_name
, m_dst_image_id
, size
,
1235 m_image_options
, true /* skip_mirror_enable */,
1236 cls::rbd::MIRROR_IMAGE_MODE_JOURNAL
, "", "", op_work_queue
, &on_create
);
1239 r
= util::create_ioctx(m_src_image_ctx
->md_ctx
, "destination image",
1240 parent_spec
.pool_id
, parent_spec
.pool_namespace
,
1246 auto *req
= image::CloneRequest
<I
>::create(
1247 config
, parent_io_ctx
, parent_spec
.image_id
, "", {}, parent_spec
.snap_id
,
1248 m_dst_io_ctx
, m_dst_image_name
, m_dst_image_id
, m_image_options
,
1249 cls::rbd::MIRROR_IMAGE_MODE_JOURNAL
, "", "", op_work_queue
, &on_create
);
1253 r
= on_create
.wait();
1255 lderr(m_cct
) << "header creation failed: " << cpp_strerror(r
) << dendl
;
1259 auto dst_image_ctx
= I::create(m_dst_image_name
, m_dst_image_id
, nullptr,
1260 m_dst_io_ctx
, false);
1262 r
= dst_image_ctx
->state
->open(OPEN_FLAG_IGNORE_MIGRATING
);
1264 lderr(m_cct
) << "failed to open newly created header: " << cpp_strerror(r
)
1269 BOOST_SCOPE_EXIT_TPL(dst_image_ctx
) {
1270 dst_image_ctx
->state
->close();
1271 } BOOST_SCOPE_EXIT_END
;
1274 std::shared_lock owner_locker
{dst_image_ctx
->owner_lock
};
1275 r
= dst_image_ctx
->operations
->prepare_image_update(
1276 exclusive_lock::OPERATION_REQUEST_TYPE_GENERAL
, true);
1278 lderr(m_cct
) << "cannot obtain exclusive lock" << dendl
;
1281 if (dst_image_ctx
->exclusive_lock
!= nullptr) {
1282 dst_image_ctx
->exclusive_lock
->block_requests(0);
1288 C_SaferCond on_snapshot_copy
;
1289 auto snapshot_copy_req
= librbd::deep_copy::SnapshotCopyRequest
<I
>::create(
1290 m_src_image_ctx
, dst_image_ctx
, 0, CEPH_NOSNAP
, 0, m_flatten
,
1291 m_src_image_ctx
->op_work_queue
, &snap_seqs
, &on_snapshot_copy
);
1292 snapshot_copy_req
->send();
1293 r
= on_snapshot_copy
.wait();
1295 lderr(m_cct
) << "failed to copy snapshots: " << cpp_strerror(r
) << dendl
;
1299 C_SaferCond on_metadata_copy
;
1300 auto metadata_copy_req
= librbd::deep_copy::MetadataCopyRequest
<I
>::create(
1301 m_src_image_ctx
, dst_image_ctx
, &on_metadata_copy
);
1302 metadata_copy_req
->send();
1303 r
= on_metadata_copy
.wait();
1305 lderr(m_cct
) << "failed to copy metadata: " << cpp_strerror(r
) << dendl
;
1309 m_dst_migration_spec
= {cls::rbd::MIGRATION_HEADER_TYPE_DST
,
1310 m_src_io_ctx
.get_id(), m_src_io_ctx
.get_namespace(),
1311 m_src_image_name
, m_src_image_id
, snap_seqs
, size
,
1312 m_mirroring
, m_mirror_image_mode
, m_flatten
,
1313 cls::rbd::MIGRATION_STATE_PREPARING
, ""};
1315 r
= cls_client::migration_set(&m_dst_io_ctx
, m_dst_header_oid
,
1316 m_dst_migration_spec
);
1318 lderr(m_cct
) << "failed to set migration header: " << cpp_strerror(r
)
1323 r
= update_group(m_src_image_ctx
, dst_image_ctx
);
1328 r
= set_state(cls::rbd::MIGRATION_STATE_PREPARED
, "");
1333 r
= dst_image_ctx
->state
->refresh();
1335 lderr(m_cct
) << "failed to refresh destination image: " << cpp_strerror(r
)
1340 r
= relink_children(m_src_image_ctx
, dst_image_ctx
);
1348 template <typename I
>
1349 int Migration
<I
>::remove_group(I
*image_ctx
, group_info_t
*group_info
) {
1350 int r
= librbd::api::Group
<I
>::image_get_group(image_ctx
, group_info
);
1352 lderr(m_cct
) << "failed to get image group: " << cpp_strerror(r
) << dendl
;
1356 if (group_info
->pool
== -1) {
1360 ceph_assert(!image_ctx
->id
.empty());
1362 ldout(m_cct
, 10) << dendl
;
1365 r
= util::create_ioctx(image_ctx
->md_ctx
, "group", group_info
->pool
, {},
1371 r
= librbd::api::Group
<I
>::image_remove_by_id(group_ioctx
,
1372 group_info
->name
.c_str(),
1374 image_ctx
->id
.c_str());
1376 lderr(m_cct
) << "failed to remove image from group: " << cpp_strerror(r
)
1384 template <typename I
>
1385 int Migration
<I
>::add_group(I
*image_ctx
, group_info_t
&group_info
) {
1386 if (group_info
.pool
== -1) {
1390 ldout(m_cct
, 10) << dendl
;
1393 int r
= util::create_ioctx(image_ctx
->md_ctx
, "group", group_info
.pool
, {},
1399 r
= librbd::api::Group
<I
>::image_add(group_ioctx
, group_info
.name
.c_str(),
1401 image_ctx
->name
.c_str());
1403 lderr(m_cct
) << "failed to add image to group: " << cpp_strerror(r
)
1411 template <typename I
>
1412 int Migration
<I
>::update_group(I
*from_image_ctx
, I
*to_image_ctx
) {
1413 ldout(m_cct
, 10) << dendl
;
1415 group_info_t group_info
;
1417 int r
= remove_group(from_image_ctx
, &group_info
);
1419 return r
== -ENOENT
? 0 : r
;
1422 r
= add_group(to_image_ctx
, group_info
);
1430 template <typename I
>
1431 int Migration
<I
>::disable_mirroring(
1432 I
*image_ctx
, bool *was_enabled
,
1433 cls::rbd::MirrorImageMode
*mirror_image_mode
) {
1434 *was_enabled
= false;
1436 cls::rbd::MirrorImage mirror_image
;
1437 int r
= cls_client::mirror_image_get(&image_ctx
->md_ctx
, image_ctx
->id
,
1440 ldout(m_cct
, 10) << "mirroring is not enabled for this image" << dendl
;
1445 lderr(m_cct
) << "failed to retrieve mirror image: " << cpp_strerror(r
)
1450 if (mirror_image
.state
== cls::rbd::MIRROR_IMAGE_STATE_ENABLED
) {
1451 *was_enabled
= true;
1452 *mirror_image_mode
= mirror_image
.mode
;
1455 ldout(m_cct
, 10) << dendl
;
1458 auto req
= mirror::DisableRequest
<I
>::create(image_ctx
, false, true, &ctx
);
1462 lderr(m_cct
) << "failed to disable mirroring: " << cpp_strerror(r
)
1467 m_src_migration_spec
.mirroring
= true;
1472 template <typename I
>
1473 int Migration
<I
>::enable_mirroring(
1474 I
*image_ctx
, bool was_enabled
,
1475 cls::rbd::MirrorImageMode mirror_image_mode
) {
1476 cls::rbd::MirrorMode mirror_mode
;
1477 int r
= cls_client::mirror_mode_get(&image_ctx
->md_ctx
, &mirror_mode
);
1478 if (r
< 0 && r
!= -ENOENT
) {
1479 lderr(m_cct
) << "failed to retrieve mirror mode: " << cpp_strerror(r
)
1484 if (mirror_mode
== cls::rbd::MIRROR_MODE_DISABLED
) {
1485 ldout(m_cct
, 10) << "mirroring is not enabled for destination pool"
1489 if (mirror_mode
== cls::rbd::MIRROR_MODE_IMAGE
&& !was_enabled
) {
1490 ldout(m_cct
, 10) << "mirroring is not enabled for image" << dendl
;
1494 ldout(m_cct
, 10) << dendl
;
1497 auto req
= mirror::EnableRequest
<I
>::create(
1498 image_ctx
, mirror_image_mode
, &ctx
);
1502 lderr(m_cct
) << "failed to enable mirroring: " << cpp_strerror(r
)
1510 // When relinking children we should be careful as it my be interrupted
1511 // at any moment by some reason and we may end up in an inconsistent
1512 // state, which we have to be able to fix with "migration abort". Below
1513 // are all possible states during migration (P1 - sourse parent, P2 -
1514 // destination parent, C - child):
1516 // P1 P2 P1 P2 P1 P2 P1 P2
1523 // (1) and (4) are the initial and the final consistent states. (2)
1524 // and (3) are intermediate inconsistent states that have to be fixed
1525 // by relink_children running in "migration abort" mode. For this, it
1526 // scans P2 for all children attached and relinks (fixes) states (3)
1527 // and (4) to state (1). Then it scans P1 for remaining children and
1528 // fixes the states (2).
1530 template <typename I
>
1531 int Migration
<I
>::relink_children(I
*from_image_ctx
, I
*to_image_ctx
) {
1532 ldout(m_cct
, 10) << dendl
;
1534 std::vector
<librbd::snap_info_t
> snaps
;
1535 int r
= list_src_snaps(&snaps
);
1540 bool migration_abort
= (to_image_ctx
== m_src_image_ctx
);
1542 for (auto it
= snaps
.begin(); it
!= snaps
.end(); it
++) {
1544 std::vector
<librbd::linked_image_spec_t
> src_child_images
;
1546 if (from_image_ctx
!= m_src_image_ctx
) {
1547 ceph_assert(migration_abort
);
1549 // We run list snaps against the src image to get only those snapshots
1550 // that are migrated. If the "from" image is not the src image
1551 // (abort migration case), we need to remap snap ids.
1552 // Also collect the list of the children currently attached to the
1553 // source, so we could make a proper decision later about relinking.
1555 std::shared_lock src_image_locker
{to_image_ctx
->image_lock
};
1556 cls::rbd::ParentImageSpec src_parent_spec
{to_image_ctx
->md_ctx
.get_id(),
1557 to_image_ctx
->md_ctx
.get_namespace(),
1558 to_image_ctx
->id
, snap
.id
};
1559 r
= api::Image
<I
>::list_children(to_image_ctx
, src_parent_spec
,
1562 lderr(m_cct
) << "failed listing children: " << cpp_strerror(r
)
1567 std::shared_lock image_locker
{from_image_ctx
->image_lock
};
1568 snap
.id
= from_image_ctx
->get_snap_id(cls::rbd::UserSnapshotNamespace(),
1570 if (snap
.id
== CEPH_NOSNAP
) {
1571 ldout(m_cct
, 5) << "skipping snapshot " << snap
.name
<< dendl
;
1576 std::vector
<librbd::linked_image_spec_t
> child_images
;
1578 std::shared_lock image_locker
{from_image_ctx
->image_lock
};
1579 cls::rbd::ParentImageSpec parent_spec
{from_image_ctx
->md_ctx
.get_id(),
1580 from_image_ctx
->md_ctx
.get_namespace(),
1581 from_image_ctx
->id
, snap
.id
};
1582 r
= api::Image
<I
>::list_children(from_image_ctx
, parent_spec
,
1585 lderr(m_cct
) << "failed listing children: " << cpp_strerror(r
)
1591 for (auto &child_image
: child_images
) {
1592 r
= relink_child(from_image_ctx
, to_image_ctx
, snap
, child_image
,
1593 migration_abort
, true);
1598 src_child_images
.erase(std::remove(src_child_images
.begin(),
1599 src_child_images
.end(), child_image
),
1600 src_child_images
.end());
1603 for (auto &child_image
: src_child_images
) {
1604 r
= relink_child(from_image_ctx
, to_image_ctx
, snap
, child_image
,
1605 migration_abort
, false);
1615 template <typename I
>
1616 int Migration
<I
>::relink_child(I
*from_image_ctx
, I
*to_image_ctx
,
1617 const librbd::snap_info_t
&from_snap
,
1618 const librbd::linked_image_spec_t
&child_image
,
1619 bool migration_abort
, bool reattach_child
) {
1620 ldout(m_cct
, 10) << from_snap
.name
<< " " << child_image
.pool_name
<< "/"
1621 << child_image
.pool_namespace
<< "/"
1622 << child_image
.image_name
<< " (migration_abort="
1623 << migration_abort
<< ", reattach_child=" << reattach_child
1626 librados::snap_t to_snap_id
;
1628 std::shared_lock image_locker
{to_image_ctx
->image_lock
};
1629 to_snap_id
= to_image_ctx
->get_snap_id(cls::rbd::UserSnapshotNamespace(),
1631 if (to_snap_id
== CEPH_NOSNAP
) {
1632 lderr(m_cct
) << "no snapshot " << from_snap
.name
<< " on destination image"
1638 librados::IoCtx child_io_ctx
;
1639 int r
= util::create_ioctx(to_image_ctx
->md_ctx
,
1640 "child image " + child_image
.image_name
,
1641 child_image
.pool_id
, child_image
.pool_namespace
,
1647 I
*child_image_ctx
= I::create("", child_image
.image_id
, nullptr,
1648 child_io_ctx
, false);
1649 r
= child_image_ctx
->state
->open(OPEN_FLAG_SKIP_OPEN_PARENT
);
1651 lderr(m_cct
) << "failed to open child image: " << cpp_strerror(r
) << dendl
;
1654 BOOST_SCOPE_EXIT_TPL(child_image_ctx
) {
1655 child_image_ctx
->state
->close();
1656 } BOOST_SCOPE_EXIT_END
;
1658 uint32_t clone_format
= 1;
1659 if (child_image_ctx
->test_op_features(RBD_OPERATION_FEATURE_CLONE_CHILD
)) {
1663 cls::rbd::ParentImageSpec parent_spec
;
1664 uint64_t parent_overlap
;
1666 std::shared_lock image_locker
{child_image_ctx
->image_lock
};
1668 // use oldest snapshot or HEAD for parent spec
1669 if (!child_image_ctx
->snap_info
.empty()) {
1670 parent_spec
= child_image_ctx
->snap_info
.begin()->second
.parent
.spec
;
1671 parent_overlap
= child_image_ctx
->snap_info
.begin()->second
.parent
.overlap
;
1673 parent_spec
= child_image_ctx
->parent_md
.spec
;
1674 parent_overlap
= child_image_ctx
->parent_md
.overlap
;
1678 if (migration_abort
&&
1679 parent_spec
.pool_id
== to_image_ctx
->md_ctx
.get_id() &&
1680 parent_spec
.pool_namespace
== to_image_ctx
->md_ctx
.get_namespace() &&
1681 parent_spec
.image_id
== to_image_ctx
->id
&&
1682 parent_spec
.snap_id
== to_snap_id
) {
1683 ldout(m_cct
, 10) << "no need for parent re-attach" << dendl
;
1685 if (parent_spec
.pool_id
!= from_image_ctx
->md_ctx
.get_id() ||
1686 parent_spec
.pool_namespace
!= from_image_ctx
->md_ctx
.get_namespace() ||
1687 parent_spec
.image_id
!= from_image_ctx
->id
||
1688 parent_spec
.snap_id
!= from_snap
.id
) {
1689 lderr(m_cct
) << "parent is not source image: " << parent_spec
.pool_id
1690 << "/" << parent_spec
.pool_namespace
<< "/"
1691 << parent_spec
.image_id
<< "@" << parent_spec
.snap_id
1696 parent_spec
.pool_id
= to_image_ctx
->md_ctx
.get_id();
1697 parent_spec
.pool_namespace
= to_image_ctx
->md_ctx
.get_namespace();
1698 parent_spec
.image_id
= to_image_ctx
->id
;
1699 parent_spec
.snap_id
= to_snap_id
;
1701 C_SaferCond on_reattach_parent
;
1702 auto reattach_parent_req
= image::AttachParentRequest
<I
>::create(
1703 *child_image_ctx
, parent_spec
, parent_overlap
, true, &on_reattach_parent
);
1704 reattach_parent_req
->send();
1705 r
= on_reattach_parent
.wait();
1707 lderr(m_cct
) << "failed to re-attach parent: " << cpp_strerror(r
) << dendl
;
1712 if (reattach_child
) {
1713 C_SaferCond on_reattach_child
;
1714 auto reattach_child_req
= image::AttachChildRequest
<I
>::create(
1715 child_image_ctx
, to_image_ctx
, to_snap_id
, from_image_ctx
, from_snap
.id
,
1716 clone_format
, &on_reattach_child
);
1717 reattach_child_req
->send();
1718 r
= on_reattach_child
.wait();
1720 lderr(m_cct
) << "failed to re-attach child: " << cpp_strerror(r
) << dendl
;
1725 child_image_ctx
->notify_update();
1730 template <typename I
>
1731 int Migration
<I
>::remove_src_image() {
1732 ldout(m_cct
, 10) << dendl
;
1734 std::vector
<librbd::snap_info_t
> snaps
;
1735 int r
= list_src_snaps(&snaps
);
1740 for (auto it
= snaps
.rbegin(); it
!= snaps
.rend(); it
++) {
1743 librbd::NoOpProgressContext prog_ctx
;
1744 int r
= Snapshot
<I
>::remove(m_src_image_ctx
, snap
.name
.c_str(),
1745 RBD_SNAP_REMOVE_UNPROTECT
, prog_ctx
);
1747 lderr(m_cct
) << "failed removing source image snapshot '" << snap
.name
1748 << "': " << cpp_strerror(r
) << dendl
;
1753 ceph_assert(m_src_image_ctx
->ignore_migrating
);
1755 ThreadPool
*thread_pool
;
1756 ContextWQ
*op_work_queue
;
1757 ImageCtx::get_thread_pool_instance(m_cct
, &thread_pool
, &op_work_queue
);
1758 C_SaferCond on_remove
;
1759 auto req
= librbd::image::RemoveRequest
<I
>::create(
1760 m_src_io_ctx
, m_src_image_ctx
, false, true, *m_prog_ctx
, op_work_queue
,
1763 r
= on_remove
.wait();
1765 m_src_image_ctx
= nullptr;
1767 // For old format image it will return -ENOENT due to expected
1768 // tmap_rm failure at the end.
1769 if (r
< 0 && r
!= -ENOENT
) {
1770 lderr(m_cct
) << "failed removing source image: " << cpp_strerror(r
)
1775 if (!m_src_image_id
.empty()) {
1776 r
= cls_client::trash_remove(&m_src_io_ctx
, m_src_image_id
);
1777 if (r
< 0 && r
!= -ENOENT
) {
1778 lderr(m_cct
) << "error removing image " << m_src_image_id
1779 << " from rbd_trash object" << dendl
;
1787 } // namespace librbd
1789 template class librbd::api::Migration
<librbd::ImageCtx
>;