1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
6 * This is an OSD class that implements methods for
9 * Most of these deal with the rbd header object. Methods prefixed
10 * with old_ deal with the original rbd design, in which clients read
11 * and interpreted the header object directly.
13 * The new format is meant to be opaque to clients - all their
14 * interactions with non-data objects should go through this
15 * class. The OSD class interface leaves the class to implement its
16 * own argument and payload serialization/deserialization, so for ease
17 * of implementation we use the existing ceph encoding/decoding
18 * methods. Something like json might be preferable, but the rbd
19 * kernel module has to be able to understand format as well. The
20 * datatypes exposed to the clients are strings, unsigned integers,
21 * and vectors of those types. The on-wire format can be found in
22 * src/include/encoding.h.
24 * The methods for interacting with the new format document their
25 * parameters as the client sees them - it would be silly to mention
26 * in each one that they take an input and an output bufferlist.
28 #include "include/types.h"
34 #include "include/uuid.h"
35 #include "common/bit_vector.hpp"
36 #include "common/errno.h"
37 #include "objclass/objclass.h"
38 #include "osd/osd_types.h"
39 #include "include/rbd_types.h"
40 #include "include/rbd/object_map_types.h"
42 #include "cls/rbd/cls_rbd.h"
43 #include "cls/rbd/cls_rbd_types.h"
45 #include <boost/algorithm/string/predicate.hpp>
47 using std::istringstream
;
48 using std::ostringstream
;
54 using ceph::BitVector
;
55 using ceph::bufferlist
;
56 using ceph::bufferptr
;
65 * stripe_unit: size in bytes of the stripe unit. if not present,
66 * the stripe unit is assumed to match the object size (1 << order).
68 * stripe_count: number of objects to stripe over before looping back.
69 * if not present or 1, striping is disabled. this is the default.
76 #define RBD_MAX_KEYS_READ 64
77 #define RBD_SNAP_KEY_PREFIX "snapshot_"
78 #define RBD_SNAP_CHILDREN_KEY_PREFIX "snap_children_"
79 #define RBD_DIR_ID_KEY_PREFIX "id_"
80 #define RBD_DIR_NAME_KEY_PREFIX "name_"
81 #define RBD_METADATA_KEY_PREFIX "metadata_"
85 uint64_t get_encode_features(cls_method_context_t hctx
) {
86 uint64_t features
= 0;
87 ceph_release_t require_osd_release
= cls_get_required_osd_release(hctx
);
88 if (require_osd_release
>= ceph_release_t::nautilus
) {
89 features
|= CEPH_FEATURE_SERVER_NAUTILUS
;
94 bool calc_sparse_extent(const bufferptr
&bp
, size_t sparse_size
,
95 uint64_t length
, size_t *write_offset
,
96 size_t *write_length
, size_t *offset
) {
98 if (*offset
+ sparse_size
> length
) {
99 extent_size
= length
- *offset
;
101 extent_size
= sparse_size
;
104 bufferptr
extent(bp
, *offset
, extent_size
);
105 *offset
+= extent_size
;
107 bool extent_is_zero
= extent
.is_zero();
108 if (!extent_is_zero
) {
109 *write_length
+= extent_size
;
111 if (extent_is_zero
&& *write_length
== 0) {
112 *write_offset
+= extent_size
;
115 if ((extent_is_zero
|| *offset
== length
) && *write_length
!= 0) {
121 } // anonymous namespace
123 static int snap_read_header(cls_method_context_t hctx
, bufferlist
& bl
)
125 unsigned snap_count
= 0;
126 uint64_t snap_names_len
= 0;
127 struct rbd_obj_header_ondisk
*header
;
129 CLS_LOG(20, "snapshots_list");
132 int len
= sizeof(*header
) +
133 snap_count
* sizeof(struct rbd_obj_snap_ondisk
) +
136 int rc
= cls_cxx_read(hctx
, 0, len
, &bl
);
140 if (bl
.length() < sizeof(*header
))
143 header
= (struct rbd_obj_header_ondisk
*)bl
.c_str();
146 if ((snap_count
!= header
->snap_count
) ||
147 (snap_names_len
!= header
->snap_names_len
)) {
148 snap_count
= header
->snap_count
;
149 snap_names_len
= header
->snap_names_len
;
159 static void key_from_snap_id(snapid_t snap_id
, string
*out
)
162 oss
<< RBD_SNAP_KEY_PREFIX
163 << std::setw(16) << std::setfill('0') << std::hex
<< snap_id
;
167 static snapid_t
snap_id_from_key(const string
&key
) {
168 istringstream
iss(key
);
170 iss
.ignore(strlen(RBD_SNAP_KEY_PREFIX
)) >> std::hex
>> id
;
175 static int read_key(cls_method_context_t hctx
, const string
&key
, T
*out
)
178 int r
= cls_cxx_map_get_val(hctx
, key
, &bl
);
181 CLS_ERR("error reading omap key %s: %s", key
.c_str(), cpp_strerror(r
).c_str());
187 auto it
= bl
.cbegin();
189 } catch (const ceph::buffer::error
&err
) {
190 CLS_ERR("error decoding %s", key
.c_str());
197 template <typename T
>
198 static int write_key(cls_method_context_t hctx
, const string
&key
, const T
&t
) {
202 int r
= cls_cxx_map_set_val(hctx
, key
, &bl
);
204 CLS_ERR("failed to set omap key: %s", key
.c_str());
210 template <typename T
>
211 static int write_key(cls_method_context_t hctx
, const string
&key
, const T
&t
,
214 encode(t
, bl
, features
);
216 int r
= cls_cxx_map_set_val(hctx
, key
, &bl
);
218 CLS_ERR("failed to set omap key: %s", key
.c_str());
224 static int remove_key(cls_method_context_t hctx
, const string
&key
) {
225 int r
= cls_cxx_map_remove_key(hctx
, key
);
226 if (r
< 0 && r
!= -ENOENT
) {
227 CLS_ERR("failed to remove key: %s", key
.c_str());
233 static bool is_valid_id(const string
&id
) {
236 for (size_t i
= 0; i
< id
.size(); ++i
) {
237 if (!isalnum(id
[i
])) {
245 * verify that the header object exists
247 * @return 0 if the object exists, -ENOENT if it does not, or other error
249 static int check_exists(cls_method_context_t hctx
)
253 return cls_cxx_stat(hctx
, &size
, &mtime
);
259 * check that given feature(s) are set
261 * @param hctx context
262 * @param need features needed
263 * @return 0 if features are set, negative error (like ENOEXEC) otherwise
265 int require_feature(cls_method_context_t hctx
, uint64_t need
)
268 int r
= read_key(hctx
, "features", &features
);
269 if (r
== -ENOENT
) // this implies it's an old-style image with no features
273 if ((features
& need
) != need
) {
274 CLS_LOG(10, "require_feature missing feature %llx, have %llx",
275 (unsigned long long)need
, (unsigned long long)features
);
281 std::string
snap_children_key_from_snap_id(snapid_t snap_id
)
284 oss
<< RBD_SNAP_CHILDREN_KEY_PREFIX
285 << std::setw(16) << std::setfill('0') << std::hex
<< snap_id
;
289 int set_op_features(cls_method_context_t hctx
, uint64_t op_features
,
291 uint64_t orig_features
;
292 int r
= read_key(hctx
, "features", &orig_features
);
294 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r
).c_str());
298 uint64_t orig_op_features
= 0;
299 r
= read_key(hctx
, "op_features", &orig_op_features
);
300 if (r
< 0 && r
!= -ENOENT
) {
301 CLS_ERR("Could not read op features off disk: %s", cpp_strerror(r
).c_str());
305 op_features
= (orig_op_features
& ~mask
) | (op_features
& mask
);
306 CLS_LOG(10, "op_features=%" PRIu64
" orig_op_features=%" PRIu64
,
307 op_features
, orig_op_features
);
308 if (op_features
== orig_op_features
) {
312 uint64_t features
= orig_features
;
313 if (op_features
== 0ULL) {
314 features
&= ~RBD_FEATURE_OPERATIONS
;
316 r
= cls_cxx_map_remove_key(hctx
, "op_features");
321 features
|= RBD_FEATURE_OPERATIONS
;
324 encode(op_features
, bl
);
325 r
= cls_cxx_map_set_val(hctx
, "op_features", &bl
);
329 CLS_ERR("error updating op features: %s", cpp_strerror(r
).c_str());
333 if (features
!= orig_features
) {
335 encode(features
, bl
);
336 r
= cls_cxx_map_set_val(hctx
, "features", &bl
);
338 CLS_ERR("error updating features: %s", cpp_strerror(r
).c_str());
346 int set_migration(cls_method_context_t hctx
,
347 const cls::rbd::MigrationSpec
&migration_spec
, bool init
) {
350 int r
= cls_cxx_map_get_val(hctx
, "migration", &bl
);
353 CLS_LOG(10, "migration already set");
356 CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r
).c_str());
360 uint64_t features
= 0;
361 r
= read_key(hctx
, "features", &features
);
363 CLS_LOG(20, "no features, assuming v1 format");
365 r
= cls_cxx_read(hctx
, 0, sizeof(RBD_HEADER_TEXT
), &header
);
367 CLS_ERR("failed to read v1 header: %s", cpp_strerror(r
).c_str());
370 if (header
.length() != sizeof(RBD_HEADER_TEXT
)) {
371 CLS_ERR("unrecognized v1 header format");
374 if (memcmp(RBD_HEADER_TEXT
, header
.c_str(), header
.length()) != 0) {
375 if (memcmp(RBD_MIGRATE_HEADER_TEXT
, header
.c_str(),
376 header
.length()) == 0) {
377 CLS_LOG(10, "migration already set");
380 CLS_ERR("unrecognized v1 header format");
384 if (migration_spec
.header_type
!= cls::rbd::MIGRATION_HEADER_TYPE_SRC
) {
385 CLS_LOG(10, "v1 format image can only be migration source");
390 header
.append(RBD_MIGRATE_HEADER_TEXT
);
391 r
= cls_cxx_write(hctx
, 0, header
.length(), &header
);
393 CLS_ERR("error updating v1 header: %s", cpp_strerror(r
).c_str());
397 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r
).c_str());
399 } else if ((features
& RBD_FEATURE_MIGRATING
) != 0ULL) {
400 if (migration_spec
.header_type
!= cls::rbd::MIGRATION_HEADER_TYPE_DST
) {
401 CLS_LOG(10, "migrating feature already set");
405 features
|= RBD_FEATURE_MIGRATING
;
407 encode(features
, bl
);
408 r
= cls_cxx_map_set_val(hctx
, "features", &bl
);
410 CLS_ERR("error updating features: %s", cpp_strerror(r
).c_str());
417 encode(migration_spec
, bl
);
418 int r
= cls_cxx_map_set_val(hctx
, "migration", &bl
);
420 CLS_ERR("error setting migration: %s", cpp_strerror(r
).c_str());
427 int read_migration(cls_method_context_t hctx
,
428 cls::rbd::MigrationSpec
*migration_spec
) {
429 uint64_t features
= 0;
430 int r
= read_key(hctx
, "features", &features
);
432 CLS_LOG(20, "no features, assuming v1 format");
434 r
= cls_cxx_read(hctx
, 0, sizeof(RBD_HEADER_TEXT
), &header
);
436 CLS_ERR("failed to read v1 header: %s", cpp_strerror(r
).c_str());
439 if (header
.length() != sizeof(RBD_HEADER_TEXT
)) {
440 CLS_ERR("unrecognized v1 header format");
443 if (memcmp(RBD_MIGRATE_HEADER_TEXT
, header
.c_str(), header
.length()) != 0) {
444 if (memcmp(RBD_HEADER_TEXT
, header
.c_str(), header
.length()) == 0) {
445 CLS_LOG(10, "migration feature not set");
448 CLS_ERR("unrecognized v1 header format");
452 if (migration_spec
->header_type
!= cls::rbd::MIGRATION_HEADER_TYPE_SRC
) {
453 CLS_LOG(10, "v1 format image can only be migration source");
457 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r
).c_str());
459 } else if ((features
& RBD_FEATURE_MIGRATING
) == 0ULL) {
460 CLS_LOG(10, "migration feature not set");
464 r
= read_key(hctx
, "migration", migration_spec
);
466 CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r
).c_str());
473 int remove_migration(cls_method_context_t hctx
) {
474 int r
= remove_key(hctx
, "migration");
479 uint64_t features
= 0;
480 r
= read_key(hctx
, "features", &features
);
482 CLS_LOG(20, "no features, assuming v1 format");
484 r
= cls_cxx_read(hctx
, 0, sizeof(RBD_MIGRATE_HEADER_TEXT
), &header
);
485 if (header
.length() != sizeof(RBD_MIGRATE_HEADER_TEXT
)) {
486 CLS_ERR("unrecognized v1 header format");
489 if (memcmp(RBD_MIGRATE_HEADER_TEXT
, header
.c_str(), header
.length()) != 0) {
490 if (memcmp(RBD_HEADER_TEXT
, header
.c_str(), header
.length()) == 0) {
491 CLS_LOG(10, "migration feature not set");
494 CLS_ERR("unrecognized v1 header format");
499 header
.append(RBD_HEADER_TEXT
);
500 r
= cls_cxx_write(hctx
, 0, header
.length(), &header
);
502 CLS_ERR("error updating v1 header: %s", cpp_strerror(r
).c_str());
506 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r
).c_str());
508 } else if ((features
& RBD_FEATURE_MIGRATING
) == 0ULL) {
509 CLS_LOG(10, "migrating feature not set");
511 features
&= ~RBD_FEATURE_MIGRATING
;
513 encode(features
, bl
);
514 r
= cls_cxx_map_set_val(hctx
, "features", &bl
);
516 CLS_ERR("error updating features: %s", cpp_strerror(r
).c_str());
527 int iterate(cls_method_context_t hctx
, L
& lambda
) {
528 int max_read
= RBD_MAX_KEYS_READ
;
529 string last_read
= RBD_SNAP_KEY_PREFIX
;
532 map
<string
, bufferlist
> vals
;
533 int r
= cls_cxx_map_get_vals(hctx
, last_read
, RBD_SNAP_KEY_PREFIX
,
534 max_read
, &vals
, &more
);
539 cls_rbd_snap snap_meta
;
540 for (auto& val
: vals
) {
541 auto iter
= val
.second
.cbegin();
543 decode(snap_meta
, iter
);
544 } catch (const ceph::buffer::error
&err
) {
545 CLS_ERR("error decoding snapshot metadata for snap : %s",
550 r
= lambda(snap_meta
);
557 last_read
= vals
.rbegin()->first
;
564 int write(cls_method_context_t hctx
, const std::string
& snap_key
,
565 cls_rbd_snap
&& snap
) {
567 uint64_t encode_features
= get_encode_features(hctx
);
568 if (snap
.migrate_parent_format(encode_features
)) {
569 // ensure the normalized parent link exists before removing it from the
571 cls_rbd_parent on_disk_parent
;
572 r
= read_key(hctx
, "parent", &on_disk_parent
);
573 if (r
< 0 && r
!= -ENOENT
) {
577 if (!on_disk_parent
.exists()) {
578 on_disk_parent
= snap
.parent
;
579 on_disk_parent
.head_overlap
= std::nullopt
;
581 r
= write_key(hctx
, "parent", on_disk_parent
, encode_features
);
587 // only store the parent overlap in the snapshot
588 snap
.parent_overlap
= snap
.parent
.head_overlap
;
592 r
= write_key(hctx
, snap_key
, snap
, encode_features
);
599 } // namespace snapshot
603 int attach(cls_method_context_t hctx
, cls_rbd_parent parent
,
605 int r
= check_exists(hctx
);
607 CLS_LOG(20, "cls_rbd::image::parent::attach: child doesn't exist");
611 r
= image::require_feature(hctx
, RBD_FEATURE_LAYERING
);
613 CLS_LOG(20, "cls_rbd::image::parent::attach: child does not support "
618 CLS_LOG(20, "cls_rbd::image::parent::attach: pool=%" PRIi64
", ns=%s, id=%s, "
619 "snapid=%" PRIu64
", size=%" PRIu64
,
620 parent
.pool_id
, parent
.pool_namespace
.c_str(),
621 parent
.image_id
.c_str(), parent
.snap_id
.val
,
622 parent
.head_overlap
.value_or(0ULL));
623 if (!parent
.exists() || parent
.head_overlap
.value_or(0ULL) == 0ULL) {
627 // make sure there isn't already a parent
628 cls_rbd_parent on_disk_parent
;
629 r
= read_key(hctx
, "parent", &on_disk_parent
);
630 if (r
< 0 && r
!= -ENOENT
) {
634 auto on_disk_parent_without_overlap
{on_disk_parent
};
635 on_disk_parent_without_overlap
.head_overlap
= parent
.head_overlap
;
638 (on_disk_parent
.head_overlap
||
639 on_disk_parent_without_overlap
!= parent
) &&
641 CLS_LOG(20, "cls_rbd::parent::attach: existing legacy parent "
642 "pool=%" PRIi64
", ns=%s, id=%s, snapid=%" PRIu64
", "
644 on_disk_parent
.pool_id
, on_disk_parent
.pool_namespace
.c_str(),
645 on_disk_parent
.image_id
.c_str(), on_disk_parent
.snap_id
.val
,
646 on_disk_parent
.head_overlap
.value_or(0ULL));
650 // our overlap is the min of our size and the parent's size.
652 r
= read_key(hctx
, "size", &our_size
);
657 parent
.head_overlap
= std::min(*parent
.head_overlap
, our_size
);
659 r
= write_key(hctx
, "parent", parent
, get_encode_features(hctx
));
667 int detach(cls_method_context_t hctx
, bool legacy_api
) {
668 int r
= check_exists(hctx
);
670 CLS_LOG(20, "cls_rbd::parent::detach: child doesn't exist");
675 r
= read_key(hctx
, "features", &features
);
676 if (r
== -ENOENT
|| ((features
& RBD_FEATURE_LAYERING
) == 0)) {
677 CLS_LOG(20, "cls_rbd::image::parent::detach: child does not support "
684 cls_rbd_parent on_disk_parent
;
685 r
= read_key(hctx
, "parent", &on_disk_parent
);
688 } else if (legacy_api
&& !on_disk_parent
.pool_namespace
.empty()) {
690 } else if (!on_disk_parent
.head_overlap
) {
694 auto detach_lambda
= [hctx
, features
](const cls_rbd_snap
& snap_meta
) {
695 if (snap_meta
.parent
.pool_id
!= -1 || snap_meta
.parent_overlap
) {
696 if ((features
& RBD_FEATURE_DEEP_FLATTEN
) != 0ULL) {
697 // remove parent reference from snapshot
698 cls_rbd_snap snap_meta_copy
= snap_meta
;
699 snap_meta_copy
.parent
= {};
700 snap_meta_copy
.parent_overlap
= std::nullopt
;
702 std::string snap_key
;
703 key_from_snap_id(snap_meta_copy
.id
, &snap_key
);
704 int r
= snapshot::write(hctx
, snap_key
, std::move(snap_meta_copy
));
715 r
= snapshot::iterate(hctx
, detach_lambda
);
716 bool has_child_snaps
= (r
== -EEXIST
);
717 if (r
< 0 && r
!= -EEXIST
) {
721 ceph_release_t require_osd_release
= cls_get_required_osd_release(hctx
);
722 if (has_child_snaps
&& require_osd_release
>= ceph_release_t::nautilus
) {
723 // remove overlap from HEAD revision but keep spec for snapshots
724 on_disk_parent
.head_overlap
= std::nullopt
;
725 r
= write_key(hctx
, "parent", on_disk_parent
, get_encode_features(hctx
));
730 r
= remove_key(hctx
, "parent");
731 if (r
< 0 && r
!= -ENOENT
) {
736 if (!has_child_snaps
) {
737 // disable clone child op feature if no longer associated
738 r
= set_op_features(hctx
, 0, RBD_OPERATION_FEATURE_CLONE_CHILD
);
746 } // namespace parent
750 * Initialize the header with basic metadata.
751 * Extra features may initialize more fields in the future.
752 * Everything is stored as key/value pairs as omaps in the header object.
754 * If features the OSD does not understand are requested, -ENOSYS is
758 * @param size number of bytes in the image (uint64_t)
759 * @param order bits to shift to determine the size of data objects (uint8_t)
760 * @param features what optional things this image will use (uint64_t)
761 * @param object_prefix a prefix for all the data objects
762 * @param data_pool_id pool id where data objects is stored (int64_t)
765 * @return 0 on success, negative error code on failure
767 int create(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
769 string object_prefix
;
770 uint64_t features
, size
;
772 int64_t data_pool_id
= -1;
775 auto iter
= in
->cbegin();
778 decode(features
, iter
);
779 decode(object_prefix
, iter
);
781 decode(data_pool_id
, iter
);
783 } catch (const ceph::buffer::error
&err
) {
787 CLS_LOG(20, "create object_prefix=%s size=%llu order=%u features=%llu",
788 object_prefix
.c_str(), (unsigned long long)size
, order
,
789 (unsigned long long)features
);
791 if (features
& ~RBD_FEATURES_ALL
) {
795 if (!object_prefix
.size()) {
799 bufferlist stored_prefixbl
;
800 int r
= cls_cxx_map_get_val(hctx
, "object_prefix", &stored_prefixbl
);
802 CLS_ERR("reading object_prefix returned %d", r
);
808 bufferlist featuresbl
;
809 bufferlist object_prefixbl
;
810 bufferlist snap_seqbl
;
811 bufferlist timestampbl
;
812 uint64_t snap_seq
= 0;
813 utime_t timestamp
= ceph_clock_now();
814 encode(size
, sizebl
);
815 encode(order
, orderbl
);
816 encode(features
, featuresbl
);
817 encode(object_prefix
, object_prefixbl
);
818 encode(snap_seq
, snap_seqbl
);
819 encode(timestamp
, timestampbl
);
821 map
<string
, bufferlist
> omap_vals
;
822 omap_vals
["size"] = sizebl
;
823 omap_vals
["order"] = orderbl
;
824 omap_vals
["features"] = featuresbl
;
825 omap_vals
["object_prefix"] = object_prefixbl
;
826 omap_vals
["snap_seq"] = snap_seqbl
;
827 omap_vals
["create_timestamp"] = timestampbl
;
828 omap_vals
["access_timestamp"] = timestampbl
;
829 omap_vals
["modify_timestamp"] = timestampbl
;
831 if ((features
& RBD_FEATURE_OPERATIONS
) != 0ULL) {
832 CLS_ERR("Attempting to set internal feature: operations");
836 if (features
& RBD_FEATURE_DATA_POOL
) {
837 if (data_pool_id
== -1) {
838 CLS_ERR("data pool not provided with feature enabled");
842 bufferlist data_pool_id_bl
;
843 encode(data_pool_id
, data_pool_id_bl
);
844 omap_vals
["data_pool_id"] = data_pool_id_bl
;
845 } else if (data_pool_id
!= -1) {
846 CLS_ERR("data pool provided with feature disabled");
850 r
= cls_cxx_map_set_vals(hctx
, &omap_vals
);
859 * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t) (deprecated)
860 * @param read_only true if the image will be used read-only (bool)
863 * @param features list of enabled features for the given snapshot (uint64_t)
864 * @param incompatible incompatible feature bits
865 * @returns 0 on success, negative error code on failure
867 int get_features(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
869 bool read_only
= false;
871 auto iter
= in
->cbegin();
874 decode(snap_id
, iter
);
876 decode(read_only
, iter
);
878 } catch (const ceph::buffer::error
&err
) {
882 CLS_LOG(20, "get_features read_only=%d", read_only
);
885 int r
= read_key(hctx
, "features", &features
);
887 CLS_ERR("failed to read features off disk: %s", cpp_strerror(r
).c_str());
891 uint64_t incompatible
= (read_only
? features
& RBD_FEATURES_INCOMPATIBLE
:
892 features
& RBD_FEATURES_RW_INCOMPATIBLE
);
893 encode(features
, *out
);
894 encode(incompatible
, *out
);
899 * set the image features
902 * @param features image features
903 * @param mask image feature mask
908 * @returns 0 on success, negative error code upon failure
910 int set_features(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
914 auto iter
= in
->cbegin();
916 decode(features
, iter
);
918 } catch (const ceph::buffer::error
&err
) {
922 // check that features exists to make sure this is a header object
923 // that was created correctly
924 uint64_t orig_features
= 0;
925 int r
= read_key(hctx
, "features", &orig_features
);
926 if (r
< 0 && r
!= -ENOENT
) {
927 CLS_ERR("Could not read image's features off disk: %s",
928 cpp_strerror(r
).c_str());
932 if ((mask
& RBD_FEATURES_INTERNAL
) != 0ULL) {
933 CLS_ERR("Attempting to set internal feature: %" PRIu64
,
934 static_cast<uint64_t>(mask
& RBD_FEATURES_INTERNAL
));
938 // newer clients might attempt to mask off features we don't support
939 mask
&= RBD_FEATURES_ALL
;
941 uint64_t enabled_features
= features
& mask
;
942 if ((enabled_features
& RBD_FEATURES_MUTABLE
) != enabled_features
) {
943 CLS_ERR("Attempting to enable immutable feature: %" PRIu64
,
944 static_cast<uint64_t>(enabled_features
& ~RBD_FEATURES_MUTABLE
));
948 uint64_t disabled_features
= ~features
& mask
;
949 uint64_t disable_mask
= (RBD_FEATURES_MUTABLE
| RBD_FEATURES_DISABLE_ONLY
);
950 if ((disabled_features
& disable_mask
) != disabled_features
) {
951 CLS_ERR("Attempting to disable immutable feature: %" PRIu64
,
952 enabled_features
& ~disable_mask
);
956 features
= (orig_features
& ~mask
) | (features
& mask
);
957 CLS_LOG(10, "set_features features=%" PRIu64
" orig_features=%" PRIu64
,
958 features
, orig_features
);
961 encode(features
, bl
);
962 r
= cls_cxx_map_set_val(hctx
, "features", &bl
);
964 CLS_ERR("error updating features: %s", cpp_strerror(r
).c_str());
972 * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
975 * @param order bits to shift to get the size of data objects (uint8_t)
976 * @param size size of the image in bytes for the given snapshot (uint64_t)
977 * @returns 0 on success, negative error code on failure
979 int get_size(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
981 uint64_t snap_id
, size
;
984 auto iter
= in
->cbegin();
986 decode(snap_id
, iter
);
987 } catch (const ceph::buffer::error
&err
) {
991 CLS_LOG(20, "get_size snap_id=%llu", (unsigned long long)snap_id
);
993 int r
= read_key(hctx
, "order", &order
);
995 CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r
).c_str());
999 if (snap_id
== CEPH_NOSNAP
) {
1000 r
= read_key(hctx
, "size", &size
);
1002 CLS_ERR("failed to read the image's size off of disk: %s", cpp_strerror(r
).c_str());
1007 string snapshot_key
;
1008 key_from_snap_id(snap_id
, &snapshot_key
);
1009 int r
= read_key(hctx
, snapshot_key
, &snap
);
1013 size
= snap
.image_size
;
1016 encode(order
, *out
);
1024 * @param size new capacity of the image in bytes (uint64_t)
1027 * @returns 0 on success, negative error code on failure
1029 int set_size(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1033 auto iter
= in
->cbegin();
1036 } catch (const ceph::buffer::error
&err
) {
1040 // check that size exists to make sure this is a header object
1041 // that was created correctly
1043 int r
= read_key(hctx
, "size", &orig_size
);
1045 CLS_ERR("Could not read image's size off disk: %s", cpp_strerror(r
).c_str());
1049 CLS_LOG(20, "set_size size=%llu orig_size=%llu", (unsigned long long)size
,
1050 (unsigned long long)orig_size
);
1053 encode(size
, sizebl
);
1054 r
= cls_cxx_map_set_val(hctx
, "size", &sizebl
);
1056 CLS_ERR("error writing snapshot metadata: %s", cpp_strerror(r
).c_str());
1060 // if we are shrinking, and have a parent, shrink our overlap with
1062 if (size
< orig_size
) {
1063 cls_rbd_parent parent
;
1064 r
= read_key(hctx
, "parent", &parent
);
1069 if (parent
.exists() && parent
.head_overlap
.value_or(0ULL) > size
) {
1070 parent
.head_overlap
= size
;
1071 r
= write_key(hctx
, "parent", parent
, get_encode_features(hctx
));
1082 * get the current protection status of the specified snapshot
1085 * @param snap_id (uint64_t) which snapshot to get the status of
1088 * @param status (uint8_t) one of:
1089 * RBD_PROTECTION_STATUS_{PROTECTED, UNPROTECTED, UNPROTECTING}
1091 * @returns 0 on success, negative error code on failure
1092 * @returns -EINVAL if snapid is CEPH_NOSNAP
1094 int get_protection_status(cls_method_context_t hctx
, bufferlist
*in
,
1099 auto iter
= in
->cbegin();
1101 decode(snap_id
, iter
);
1102 } catch (const ceph::buffer::error
&err
) {
1103 CLS_LOG(20, "get_protection_status: invalid decode");
1107 int r
= check_exists(hctx
);
1111 CLS_LOG(20, "get_protection_status snap_id=%llu",
1112 (unsigned long long)snap_id
.val
);
1114 if (snap_id
== CEPH_NOSNAP
)
1118 string snapshot_key
;
1119 key_from_snap_id(snap_id
.val
, &snapshot_key
);
1120 r
= read_key(hctx
, snapshot_key
, &snap
);
1122 CLS_ERR("could not read key for snapshot id %" PRIu64
, snap_id
.val
);
1126 if (snap
.protection_status
>= RBD_PROTECTION_STATUS_LAST
) {
1127 CLS_ERR("invalid protection status for snap id %llu: %u",
1128 (unsigned long long)snap_id
.val
, snap
.protection_status
);
1132 encode(snap
.protection_status
, *out
);
1137 * set the proctection status of a snapshot
1140 * @param snapid (uint64_t) which snapshot to set the status of
1141 * @param status (uint8_t) one of:
1142 * RBD_PROTECTION_STATUS_{PROTECTED, UNPROTECTED, UNPROTECTING}
1144 * @returns 0 on success, negative error code on failure
1145 * @returns -EINVAL if snapid is CEPH_NOSNAP
1147 int set_protection_status(cls_method_context_t hctx
, bufferlist
*in
,
1153 auto iter
= in
->cbegin();
1155 decode(snap_id
, iter
);
1156 decode(status
, iter
);
1157 } catch (const ceph::buffer::error
&err
) {
1158 CLS_LOG(20, "set_protection_status: invalid decode");
1162 int r
= check_exists(hctx
);
1166 r
= image::require_feature(hctx
, RBD_FEATURE_LAYERING
);
1168 CLS_LOG(20, "image does not support layering");
1172 CLS_LOG(20, "set_protection_status snapid=%llu status=%u",
1173 (unsigned long long)snap_id
.val
, status
);
1175 if (snap_id
== CEPH_NOSNAP
)
1178 if (status
>= RBD_PROTECTION_STATUS_LAST
) {
1179 CLS_LOG(10, "invalid protection status for snap id %llu: %u",
1180 (unsigned long long)snap_id
.val
, status
);
1185 string snapshot_key
;
1186 key_from_snap_id(snap_id
.val
, &snapshot_key
);
1187 r
= read_key(hctx
, snapshot_key
, &snap
);
1189 CLS_ERR("could not read key for snapshot id %" PRIu64
, snap_id
.val
);
1193 snap
.protection_status
= status
;
1194 r
= image::snapshot::write(hctx
, snapshot_key
, std::move(snap
));
1203 * get striping parameters
1209 * @param stripe unit (bytes)
1210 * @param stripe count (num objects)
1212 * @returns 0 on success
1214 int get_stripe_unit_count(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1216 int r
= check_exists(hctx
);
1220 CLS_LOG(20, "get_stripe_unit_count");
1222 r
= image::require_feature(hctx
, RBD_FEATURE_STRIPINGV2
);
1226 uint64_t stripe_unit
= 0, stripe_count
= 0;
1227 r
= read_key(hctx
, "stripe_unit", &stripe_unit
);
1229 // default to object size
1231 r
= read_key(hctx
, "order", &order
);
1233 CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r
).c_str());
1236 stripe_unit
= 1ull << order
;
1240 r
= read_key(hctx
, "stripe_count", &stripe_count
);
1249 encode(stripe_unit
, *out
);
1250 encode(stripe_count
, *out
);
1255 * set striping parameters
1258 * @param stripe unit (bytes)
1259 * @param stripe count (num objects)
1261 * @returns 0 on success
1263 int set_stripe_unit_count(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1265 uint64_t stripe_unit
, stripe_count
;
1267 auto iter
= in
->cbegin();
1269 decode(stripe_unit
, iter
);
1270 decode(stripe_count
, iter
);
1271 } catch (const ceph::buffer::error
&err
) {
1272 CLS_LOG(20, "set_stripe_unit_count: invalid decode");
1276 if (!stripe_count
|| !stripe_unit
)
1279 int r
= check_exists(hctx
);
1283 CLS_LOG(20, "set_stripe_unit_count");
1285 r
= image::require_feature(hctx
, RBD_FEATURE_STRIPINGV2
);
1290 r
= read_key(hctx
, "order", &order
);
1292 CLS_ERR("failed to read the order off of disk: %s", cpp_strerror(r
).c_str());
1295 if ((1ull << order
) % stripe_unit
|| stripe_unit
> (1ull << order
)) {
1296 CLS_ERR("stripe unit %llu is not a factor of the object size %llu",
1297 (unsigned long long)stripe_unit
, 1ull << order
);
1302 encode(stripe_unit
, bl
);
1303 r
= cls_cxx_map_set_val(hctx
, "stripe_unit", &bl
);
1305 CLS_ERR("error writing stripe_unit metadata: %s", cpp_strerror(r
).c_str());
1309 encode(stripe_count
, bl2
);
1310 r
= cls_cxx_map_set_val(hctx
, "stripe_count", &bl2
);
1312 CLS_ERR("error writing stripe_count metadata: %s", cpp_strerror(r
).c_str());
1319 int get_create_timestamp(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1321 CLS_LOG(20, "get_create_timestamp");
1325 int r
= cls_cxx_map_get_val(hctx
, "create_timestamp", &bl
);
1328 CLS_ERR("error reading create_timestamp: %s", cpp_strerror(r
).c_str());
1333 auto it
= bl
.cbegin();
1334 decode(timestamp
, it
);
1335 } catch (const ceph::buffer::error
&err
) {
1336 CLS_ERR("could not decode create_timestamp");
1341 encode(timestamp
, *out
);
1346 * get the image access timestamp
1352 * @param timestamp the image access timestamp
1354 * @returns 0 on success, negative error code upon failure
1356 int get_access_timestamp(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1358 CLS_LOG(20, "get_access_timestamp");
1362 int r
= cls_cxx_map_get_val(hctx
, "access_timestamp", &bl
);
1365 CLS_ERR("error reading access_timestamp: %s", cpp_strerror(r
).c_str());
1370 auto it
= bl
.cbegin();
1371 decode(timestamp
, it
);
1372 } catch (const ceph::buffer::error
&err
) {
1373 CLS_ERR("could not decode access_timestamp");
1378 encode(timestamp
, *out
);
1383 * get the image modify timestamp
1389 * @param timestamp the image modify timestamp
1391 * @returns 0 on success, negative error code upon failure
1393 int get_modify_timestamp(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1395 CLS_LOG(20, "get_modify_timestamp");
1399 int r
= cls_cxx_map_get_val(hctx
, "modify_timestamp", &bl
);
1402 CLS_ERR("error reading modify_timestamp: %s", cpp_strerror(r
).c_str());
1407 auto it
= bl
.cbegin();
1408 decode(timestamp
, it
);
1409 } catch (const ceph::buffer::error
&err
) {
1410 CLS_ERR("could not decode modify_timestamp");
1415 encode(timestamp
, *out
);
1421 * get the image flags
1424 * @param snap_id which snapshot to query, to CEPH_NOSNAP (uint64_t)
1427 * @param flags image flags
1429 * @returns 0 on success, negative error code upon failure
1431 int get_flags(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1434 auto iter
= in
->cbegin();
1436 decode(snap_id
, iter
);
1437 } catch (const ceph::buffer::error
&err
) {
1441 CLS_LOG(20, "get_flags snap_id=%llu", (unsigned long long)snap_id
);
1444 if (snap_id
== CEPH_NOSNAP
) {
1445 int r
= read_key(hctx
, "flags", &flags
);
1446 if (r
< 0 && r
!= -ENOENT
) {
1447 CLS_ERR("failed to read flags off disk: %s", cpp_strerror(r
).c_str());
1452 string snapshot_key
;
1453 key_from_snap_id(snap_id
, &snapshot_key
);
1454 int r
= read_key(hctx
, snapshot_key
, &snap
);
1461 encode(flags
, *out
);
1466 * set the image flags
1469 * @param flags image flags
1470 * @param mask image flag mask
1471 * @param snap_id which snapshot to update, or CEPH_NOSNAP (uint64_t)
1476 * @returns 0 on success, negative error code upon failure
1478 int set_flags(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1482 uint64_t snap_id
= CEPH_NOSNAP
;
1483 auto iter
= in
->cbegin();
1485 decode(flags
, iter
);
1488 decode(snap_id
, iter
);
1490 } catch (const ceph::buffer::error
&err
) {
1494 // check that size exists to make sure this is a header object
1495 // that was created correctly
1497 uint64_t orig_flags
= 0;
1498 cls_rbd_snap snap_meta
;
1499 string snap_meta_key
;
1500 if (snap_id
== CEPH_NOSNAP
) {
1501 r
= read_key(hctx
, "flags", &orig_flags
);
1502 if (r
< 0 && r
!= -ENOENT
) {
1503 CLS_ERR("Could not read image's flags off disk: %s",
1504 cpp_strerror(r
).c_str());
1508 key_from_snap_id(snap_id
, &snap_meta_key
);
1509 r
= read_key(hctx
, snap_meta_key
, &snap_meta
);
1511 CLS_ERR("Could not read snapshot: snap_id=%" PRIu64
": %s",
1512 snap_id
, cpp_strerror(r
).c_str());
1515 orig_flags
= snap_meta
.flags
;
1518 flags
= (orig_flags
& ~mask
) | (flags
& mask
);
1519 CLS_LOG(20, "set_flags snap_id=%" PRIu64
", orig_flags=%" PRIu64
", "
1520 "new_flags=%" PRIu64
", mask=%" PRIu64
, snap_id
, orig_flags
,
1523 if (snap_id
== CEPH_NOSNAP
) {
1524 r
= write_key(hctx
, "flags", flags
);
1526 snap_meta
.flags
= flags
;
1527 r
= image::snapshot::write(hctx
, snap_meta_key
, std::move(snap_meta
));
1537 * Get the operation-based image features
1542 * @param bitmask of enabled op features (uint64_t)
1543 * @returns 0 on success, negative error code on failure
1545 int op_features_get(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1547 CLS_LOG(20, "op_features_get");
1549 uint64_t op_features
= 0;
1550 int r
= read_key(hctx
, "op_features", &op_features
);
1551 if (r
< 0 && r
!= -ENOENT
) {
1552 CLS_ERR("failed to read op features off disk: %s", cpp_strerror(r
).c_str());
1556 encode(op_features
, *out
);
1561 * Set the operation-based image features
1564 * @param op_features image op features
1565 * @param mask image op feature mask
1570 * @returns 0 on success, negative error code upon failure
1572 int op_features_set(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1574 uint64_t op_features
;
1576 auto iter
= in
->cbegin();
1578 decode(op_features
, iter
);
1580 } catch (const ceph::buffer::error
&err
) {
1584 uint64_t unsupported_op_features
= (mask
& ~RBD_OPERATION_FEATURES_ALL
);
1585 if (unsupported_op_features
!= 0ULL) {
1586 CLS_ERR("unsupported op features: %" PRIu64
, unsupported_op_features
);
1590 return image::set_op_features(hctx
, op_features
, mask
);
1594 * get the current parent, if any
1597 * @param snap_id which snapshot to query, or CEPH_NOSNAP (uint64_t)
1600 * @param pool parent pool id (-1 if parent does not exist)
1601 * @param image parent image id
1602 * @param snapid parent snapid
1603 * @param size portion of parent mapped under the child
1605 * @returns 0 on success or parent does not exist, negative error code on failure
1607 int get_parent(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1611 auto iter
= in
->cbegin();
1613 decode(snap_id
, iter
);
1614 } catch (const ceph::buffer::error
&err
) {
1618 int r
= check_exists(hctx
);
1623 CLS_LOG(20, "get_parent snap_id=%" PRIu64
, snap_id
);
1625 cls_rbd_parent parent
;
1626 r
= image::require_feature(hctx
, RBD_FEATURE_LAYERING
);
1628 r
= read_key(hctx
, "parent", &parent
);
1629 if (r
< 0 && r
!= -ENOENT
) {
1631 } else if (!parent
.pool_namespace
.empty()) {
1635 if (snap_id
!= CEPH_NOSNAP
) {
1637 std::string snapshot_key
;
1638 key_from_snap_id(snap_id
, &snapshot_key
);
1639 r
= read_key(hctx
, snapshot_key
, &snap
);
1640 if (r
< 0 && r
!= -ENOENT
) {
1644 if (snap
.parent
.exists()) {
1645 // legacy format where full parent spec is written within
1646 // each snapshot record
1647 parent
= snap
.parent
;
1648 } else if (snap
.parent_overlap
) {
1649 // normalized parent reference
1650 if (!parent
.exists()) {
1651 CLS_ERR("get_parent: snap_id=%" PRIu64
": invalid parent spec",
1655 parent
.head_overlap
= *snap
.parent_overlap
;
1657 // snapshot doesn't have associated parent
1663 encode(parent
.pool_id
, *out
);
1664 encode(parent
.image_id
, *out
);
1665 encode(parent
.snap_id
, *out
);
1666 encode(parent
.head_overlap
.value_or(0ULL), *out
);
1671 * set the image parent
1674 * @param pool parent pool
1675 * @param id parent image id
1676 * @param snapid parent snapid
1677 * @param size parent size
1679 * @returns 0 on success, or negative error code
1681 int set_parent(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1683 cls_rbd_parent parent
;
1684 auto iter
= in
->cbegin();
1686 decode(parent
.pool_id
, iter
);
1687 decode(parent
.image_id
, iter
);
1688 decode(parent
.snap_id
, iter
);
1691 decode(overlap
, iter
);
1692 parent
.head_overlap
= overlap
;
1693 } catch (const ceph::buffer::error
&err
) {
1694 CLS_LOG(20, "cls_rbd::set_parent: invalid decode");
1698 int r
= image::parent::attach(hctx
, parent
, false);
1708 * remove the parent pointer
1710 * This can only happen on the head, not on a snapshot. No arguments.
1712 * @returns 0 on success, negative error code on failure.
1714 int remove_parent(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1716 int r
= image::parent::detach(hctx
, true);
1729 * @param parent spec (cls::rbd::ParentImageSpec)
1730 * @returns 0 on success, negative error code on failure
1732 int parent_get(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
) {
1733 int r
= check_exists(hctx
);
1738 CLS_LOG(20, "parent_get");
1740 cls_rbd_parent parent
;
1741 r
= image::require_feature(hctx
, RBD_FEATURE_LAYERING
);
1743 r
= read_key(hctx
, "parent", &parent
);
1744 if (r
< 0 && r
!= -ENOENT
) {
1746 } else if (r
== -ENOENT
) {
1747 // examine oldest snapshot to see if it has a denormalized parent
1748 auto parent_lambda
= [&parent
](const cls_rbd_snap
& snap_meta
) {
1749 if (snap_meta
.parent
.exists()) {
1750 parent
= snap_meta
.parent
;
1755 r
= image::snapshot::iterate(hctx
, parent_lambda
);
1762 cls::rbd::ParentImageSpec parent_image_spec
{
1763 parent
.pool_id
, parent
.pool_namespace
, parent
.image_id
,
1765 encode(parent_image_spec
, *out
);
1771 * @param snap id (uint64_t) parent snapshot id
1774 * @param byte overlap of parent image (std::optional<uint64_t>)
1775 * @returns 0 on success, negative error code on failure
1777 int parent_overlap_get(cls_method_context_t hctx
, bufferlist
*in
,
1780 auto iter
= in
->cbegin();
1782 decode(snap_id
, iter
);
1783 } catch (const ceph::buffer::error
&err
) {
1787 int r
= check_exists(hctx
);
1788 CLS_LOG(20, "parent_overlap_get");
1790 std::optional
<uint64_t> parent_overlap
= std::nullopt
;
1791 r
= image::require_feature(hctx
, RBD_FEATURE_LAYERING
);
1793 if (snap_id
== CEPH_NOSNAP
) {
1794 cls_rbd_parent parent
;
1795 r
= read_key(hctx
, "parent", &parent
);
1796 if (r
< 0 && r
!= -ENOENT
) {
1798 } else if (r
== 0) {
1799 parent_overlap
= parent
.head_overlap
;
1803 std::string snapshot_key
;
1804 key_from_snap_id(snap_id
, &snapshot_key
);
1805 r
= read_key(hctx
, snapshot_key
, &snap
);
1810 if (snap
.parent_overlap
) {
1811 parent_overlap
= snap
.parent_overlap
;
1812 } else if (snap
.parent
.exists()) {
1813 // legacy format where full parent spec is written within
1814 // each snapshot record
1815 parent_overlap
= snap
.parent
.head_overlap
;
1820 encode(parent_overlap
, *out
);
1826 * @param parent spec (cls::rbd::ParentImageSpec)
1827 * @param size parent size (uint64_t)
1830 * @returns 0 on success, negative error code on failure
1832 int parent_attach(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
) {
1833 cls::rbd::ParentImageSpec parent_image_spec
;
1834 uint64_t parent_overlap
;
1835 bool reattach
= false;
1837 auto iter
= in
->cbegin();
1839 decode(parent_image_spec
, iter
);
1840 decode(parent_overlap
, iter
);
1842 decode(reattach
, iter
);
1844 } catch (const ceph::buffer::error
&err
) {
1845 CLS_LOG(20, "cls_rbd::parent_attach: invalid decode");
1849 int r
= image::parent::attach(hctx
, {parent_image_spec
, parent_overlap
},
1863 * @returns 0 on success, negative error code on failure
1865 int parent_detach(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
) {
1866 int r
= image::parent::detach(hctx
, false);
1876 * methods for dealing with rbd_children object
1879 static int decode_parent_common(bufferlist::const_iterator
& it
, uint64_t *pool_id
,
1880 string
*image_id
, snapid_t
*snap_id
)
1883 decode(*pool_id
, it
);
1884 decode(*image_id
, it
);
1885 decode(*snap_id
, it
);
1886 } catch (const ceph::buffer::error
&err
) {
1887 CLS_ERR("error decoding parent spec");
1893 static int decode_parent(bufferlist
*in
, uint64_t *pool_id
,
1894 string
*image_id
, snapid_t
*snap_id
)
1896 auto it
= in
->cbegin();
1897 return decode_parent_common(it
, pool_id
, image_id
, snap_id
);
1900 static int decode_parent_and_child(bufferlist
*in
, uint64_t *pool_id
,
1901 string
*image_id
, snapid_t
*snap_id
,
1904 auto it
= in
->cbegin();
1905 int r
= decode_parent_common(it
, pool_id
, image_id
, snap_id
);
1909 decode(*c_image_id
, it
);
1910 } catch (const ceph::buffer::error
&err
) {
1911 CLS_ERR("error decoding child image id");
1917 static string
parent_key(uint64_t pool_id
, string image_id
, snapid_t snap_id
)
1920 encode(pool_id
, key_bl
);
1921 encode(image_id
, key_bl
);
1922 encode(snap_id
, key_bl
);
1923 return string(key_bl
.c_str(), key_bl
.length());
1927 * add child to rbd_children directory object
1929 * rbd_children is a map of (p_pool_id, p_image_id, p_snap_id) to
1930 * [c_image_id, [c_image_id ... ]]
1933 * @param p_pool_id parent pool id
1934 * @param p_image_id parent image oid
1935 * @param p_snap_id parent snapshot id
1936 * @param c_image_id new child image oid to add
1938 * @returns 0 on success, negative error on failure
1941 int add_child(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
1947 string p_image_id
, c_image_id
;
1948 // Use set for ease of erase() for remove_child()
1949 std::set
<string
> children
;
1951 r
= decode_parent_and_child(in
, &p_pool_id
, &p_image_id
, &p_snap_id
,
1956 CLS_LOG(20, "add_child %s to (%" PRIu64
", %s, %" PRIu64
")", c_image_id
.c_str(),
1957 p_pool_id
, p_image_id
.c_str(), p_snap_id
.val
);
1959 string key
= parent_key(p_pool_id
, p_image_id
, p_snap_id
);
1961 // get current child list for parent, if any
1962 r
= read_key(hctx
, key
, &children
);
1963 if ((r
< 0) && (r
!= -ENOENT
)) {
1964 CLS_LOG(20, "add_child: omap read failed: %s", cpp_strerror(r
).c_str());
1968 if (children
.find(c_image_id
) != children
.end()) {
1969 CLS_LOG(20, "add_child: child already exists: %s", c_image_id
.c_str());
1973 children
.insert(c_image_id
);
1977 encode(children
, childbl
);
1978 r
= cls_cxx_map_set_val(hctx
, key
, &childbl
);
1980 CLS_LOG(20, "add_child: omap write failed: %s", cpp_strerror(r
).c_str());
1985 * remove child from rbd_children directory object
1988 * @param p_pool_id parent pool id
1989 * @param p_image_id parent image oid
1990 * @param p_snap_id parent snapshot id
1991 * @param c_image_id new child image oid to add
1993 * @returns 0 on success, negative error on failure
1996 int remove_child(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2002 string p_image_id
, c_image_id
;
2003 std::set
<string
> children
;
2005 r
= decode_parent_and_child(in
, &p_pool_id
, &p_image_id
, &p_snap_id
,
2010 CLS_LOG(20, "remove_child %s from (%" PRIu64
", %s, %" PRIu64
")",
2011 c_image_id
.c_str(), p_pool_id
, p_image_id
.c_str(),
2014 string key
= parent_key(p_pool_id
, p_image_id
, p_snap_id
);
2016 // get current child list for parent. Unlike add_child(), an empty list
2017 // is an error (how can we remove something that doesn't exist?)
2018 r
= read_key(hctx
, key
, &children
);
2020 CLS_LOG(20, "remove_child: read omap failed: %s", cpp_strerror(r
).c_str());
2024 if (children
.find(c_image_id
) == children
.end()) {
2025 CLS_LOG(20, "remove_child: child not found: %s", c_image_id
.c_str());
2028 // find and remove child
2029 children
.erase(c_image_id
);
2031 // now empty? remove key altogether
2032 if (children
.empty()) {
2033 r
= cls_cxx_map_remove_key(hctx
, key
);
2035 CLS_LOG(20, "remove_child: remove key failed: %s", cpp_strerror(r
).c_str());
2037 // write back shortened children list
2039 encode(children
, childbl
);
2040 r
= cls_cxx_map_set_val(hctx
, key
, &childbl
);
2042 CLS_LOG(20, "remove_child: write omap failed: %s", cpp_strerror(r
).c_str());
2049 * @param p_pool_id parent pool id
2050 * @param p_image_id parent image oid
2051 * @param p_snap_id parent snapshot id
2052 * @param c_image_id new child image oid to add
2055 * @param children set<string> of children
2057 * @returns 0 on success, negative error on failure
2059 int get_children(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2065 std::set
<string
> children
;
2067 r
= decode_parent(in
, &p_pool_id
, &p_image_id
, &p_snap_id
);
2071 CLS_LOG(20, "get_children of (%" PRIu64
", %s, %" PRIu64
")",
2072 p_pool_id
, p_image_id
.c_str(), p_snap_id
.val
);
2074 string key
= parent_key(p_pool_id
, p_image_id
, p_snap_id
);
2076 r
= read_key(hctx
, key
, &children
);
2079 CLS_LOG(20, "get_children: read omap failed: %s", cpp_strerror(r
).c_str());
2082 encode(children
, *out
);
2088 * Get the information needed to create a rados snap context for doing
2089 * I/O to the data objects. This must include all snapshots.
2092 * @param snap_seq the highest snapshot id ever associated with the image (uint64_t)
2093 * @param snap_ids existing snapshot ids in descending order (vector<uint64_t>)
2094 * @returns 0 on success, negative error code on failure
2096 int get_snapcontext(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2098 CLS_LOG(20, "get_snapcontext");
2101 int max_read
= RBD_MAX_KEYS_READ
;
2102 vector
<snapid_t
> snap_ids
;
2103 string last_read
= RBD_SNAP_KEY_PREFIX
;
2108 r
= cls_cxx_map_get_keys(hctx
, last_read
, max_read
, &keys
, &more
);
2112 for (auto it
= keys
.begin(); it
!= keys
.end(); ++it
) {
2113 if ((*it
).find(RBD_SNAP_KEY_PREFIX
) != 0)
2115 snapid_t snap_id
= snap_id_from_key(*it
);
2116 snap_ids
.push_back(snap_id
);
2119 last_read
= *(keys
.rbegin());
2123 r
= read_key(hctx
, "snap_seq", &snap_seq
);
2125 CLS_ERR("could not read the image's snap_seq off disk: %s", cpp_strerror(r
).c_str());
2129 // snap_ids must be descending in a snap context
2130 std::reverse(snap_ids
.begin(), snap_ids
.end());
2132 encode(snap_seq
, *out
);
2133 encode(snap_ids
, *out
);
2140 * @param object_prefix prefix for data object names (string)
2141 * @returns 0 on success, negative error code on failure
2143 int get_object_prefix(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2145 CLS_LOG(20, "get_object_prefix");
2147 string object_prefix
;
2148 int r
= read_key(hctx
, "object_prefix", &object_prefix
);
2150 CLS_ERR("failed to read the image's object prefix off of disk: %s",
2151 cpp_strerror(r
).c_str());
2155 encode(object_prefix
, *out
);
2165 * @param pool_id (int64_t) of data pool or -1 if none
2166 * @returns 0 on success, negative error code on failure
2168 int get_data_pool(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2170 CLS_LOG(20, "get_data_pool");
2172 int64_t data_pool_id
= -1;
2173 int r
= read_key(hctx
, "data_pool_id", &data_pool_id
);
2177 CLS_ERR("error reading image data pool id: %s", cpp_strerror(r
).c_str());
2181 encode(data_pool_id
, *out
);
2187 * @param snap_id which snapshot to query
2190 * @param name (string) of the snapshot
2191 * @returns 0 on success, negative error code on failure
2193 int get_snapshot_name(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2197 auto iter
= in
->cbegin();
2199 decode(snap_id
, iter
);
2200 } catch (const ceph::buffer::error
&err
) {
2204 CLS_LOG(20, "get_snapshot_name snap_id=%llu", (unsigned long long)snap_id
);
2206 if (snap_id
== CEPH_NOSNAP
)
2210 string snapshot_key
;
2211 key_from_snap_id(snap_id
, &snapshot_key
);
2212 int r
= read_key(hctx
, snapshot_key
, &snap
);
2216 encode(snap
.name
, *out
);
2223 * @param snap_id which snapshot to query
2226 * @param timestamp (utime_t) of the snapshot
2227 * @returns 0 on success, negative error code on failure
2229 * NOTE: deprecated - remove this method after Luminous is unsupported
2231 int get_snapshot_timestamp(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2235 auto iter
= in
->cbegin();
2237 decode(snap_id
, iter
);
2238 } catch (const ceph::buffer::error
&err
) {
2242 CLS_LOG(20, "get_snapshot_timestamp snap_id=%llu", (unsigned long long)snap_id
);
2244 if (snap_id
== CEPH_NOSNAP
) {
2249 string snapshot_key
;
2250 key_from_snap_id(snap_id
, &snapshot_key
);
2251 int r
= read_key(hctx
, snapshot_key
, &snap
);
2256 encode(snap
.timestamp
, *out
);
2262 * @param snap_id which snapshot to query
2265 * @param snapshot (cls::rbd::SnapshotInfo)
2266 * @returns 0 on success, negative error code on failure
2268 int snapshot_get(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2272 auto iter
= in
->cbegin();
2274 decode(snap_id
, iter
);
2275 } catch (const ceph::buffer::error
&err
) {
2279 CLS_LOG(20, "snapshot_get snap_id=%llu", (unsigned long long)snap_id
);
2280 if (snap_id
== CEPH_NOSNAP
) {
2285 string snapshot_key
;
2286 key_from_snap_id(snap_id
, &snapshot_key
);
2287 int r
= read_key(hctx
, snapshot_key
, &snap
);
2292 cls::rbd::SnapshotInfo snapshot_info
{snap
.id
, snap
.snapshot_namespace
,
2293 snap
.name
, snap
.image_size
,
2294 snap
.timestamp
, snap
.child_count
};
2295 encode(snapshot_info
, *out
);
2300 * Adds a snapshot to an rbd header. Ensures the id and name are unique.
2303 * @param snap_name name of the snapshot (string)
2304 * @param snap_id id of the snapshot (uint64_t)
2305 * @param snap_namespace namespace of the snapshot (cls::rbd::SnapshotNamespace)
2308 * @returns 0 on success, negative error code on failure.
2309 * @returns -ESTALE if the input snap_id is less than the image's snap_seq
2310 * @returns -EEXIST if the id or name are already used by another snapshot
2312 int snapshot_add(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2314 bufferlist snap_namebl
, snap_idbl
;
2315 cls_rbd_snap snap_meta
;
2316 uint64_t snap_limit
;
2319 auto iter
= in
->cbegin();
2320 decode(snap_meta
.name
, iter
);
2321 decode(snap_meta
.id
, iter
);
2323 decode(snap_meta
.snapshot_namespace
, iter
);
2325 } catch (const ceph::buffer::error
&err
) {
2329 if (boost::get
<cls::rbd::UnknownSnapshotNamespace
>(
2330 &snap_meta
.snapshot_namespace
) != nullptr) {
2331 CLS_ERR("Unknown snapshot namespace provided");
2335 CLS_LOG(20, "snapshot_add name=%s id=%llu", snap_meta
.name
.c_str(),
2336 (unsigned long long)snap_meta
.id
.val
);
2338 if (snap_meta
.id
> CEPH_MAXSNAP
)
2341 uint64_t cur_snap_seq
;
2342 int r
= read_key(hctx
, "snap_seq", &cur_snap_seq
);
2344 CLS_ERR("Could not read image's snap_seq off disk: %s", cpp_strerror(r
).c_str());
2348 // client lost a race with another snapshot creation.
2349 // snap_seq must be monotonically increasing.
2350 if (snap_meta
.id
< cur_snap_seq
)
2353 r
= read_key(hctx
, "size", &snap_meta
.image_size
);
2355 CLS_ERR("Could not read image's size off disk: %s", cpp_strerror(r
).c_str());
2358 r
= read_key(hctx
, "flags", &snap_meta
.flags
);
2359 if (r
< 0 && r
!= -ENOENT
) {
2360 CLS_ERR("Could not read image's flags off disk: %s", cpp_strerror(r
).c_str());
2364 r
= read_key(hctx
, "snap_limit", &snap_limit
);
2366 snap_limit
= UINT64_MAX
;
2368 CLS_ERR("Could not read snapshot limit off disk: %s", cpp_strerror(r
).c_str());
2372 snap_meta
.timestamp
= ceph_clock_now();
2374 uint64_t total_read
= 0;
2375 auto pre_check_lambda
=
2376 [&snap_meta
, &total_read
, snap_limit
](const cls_rbd_snap
& old_meta
) {
2378 if (total_read
>= snap_limit
) {
2379 CLS_ERR("Attempt to create snapshot over limit of %" PRIu64
,
2384 if ((snap_meta
.name
== old_meta
.name
&&
2385 snap_meta
.snapshot_namespace
== old_meta
.snapshot_namespace
) ||
2386 snap_meta
.id
== old_meta
.id
) {
2387 CLS_LOG(20, "snap_name %s or snap_id %" PRIu64
" matches existing snap "
2388 "%s %" PRIu64
, snap_meta
.name
.c_str(), snap_meta
.id
.val
,
2389 old_meta
.name
.c_str(), old_meta
.id
.val
);
2395 r
= image::snapshot::iterate(hctx
, pre_check_lambda
);
2400 // snapshot inherits parent, if any
2401 cls_rbd_parent parent
;
2402 r
= read_key(hctx
, "parent", &parent
);
2403 if (r
< 0 && r
!= -ENOENT
) {
2407 // write helper method will convert to normalized format if required
2408 snap_meta
.parent
= parent
;
2411 if (cls::rbd::get_snap_namespace_type(snap_meta
.snapshot_namespace
) ==
2412 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH
) {
2413 // add snap_trash feature bit if not already enabled
2414 r
= image::set_op_features(hctx
, RBD_OPERATION_FEATURE_SNAP_TRASH
,
2415 RBD_OPERATION_FEATURE_SNAP_TRASH
);
2421 r
= write_key(hctx
, "snap_seq", snap_meta
.id
);
2426 std::string snapshot_key
;
2427 key_from_snap_id(snap_meta
.id
, &snapshot_key
);
2428 r
= image::snapshot::write(hctx
, snapshot_key
, std::move(snap_meta
));
2440 * @param src_snap_id old snap id of the snapshot (snapid_t)
2441 * @param dst_snap_name new name of the snapshot (string)
2444 * @returns 0 on success, negative error code on failure.
2446 int snapshot_rename(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2448 bufferlist snap_namebl
, snap_idbl
;
2449 snapid_t src_snap_id
;
2450 string dst_snap_name
;
2451 cls_rbd_snap snap_meta
;
2455 auto iter
= in
->cbegin();
2456 decode(src_snap_id
, iter
);
2457 decode(dst_snap_name
, iter
);
2458 } catch (const ceph::buffer::error
&err
) {
2462 CLS_LOG(20, "snapshot_rename id=%" PRIu64
", dst_name=%s",
2463 src_snap_id
.val
, dst_snap_name
.c_str());
2465 auto duplicate_name_lambda
= [&dst_snap_name
](const cls_rbd_snap
& snap_meta
) {
2466 if (cls::rbd::get_snap_namespace_type(snap_meta
.snapshot_namespace
) ==
2467 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER
&&
2468 snap_meta
.name
== dst_snap_name
) {
2469 CLS_LOG(20, "snap_name %s matches existing snap with snap id %" PRIu64
,
2470 dst_snap_name
.c_str(), snap_meta
.id
.val
);
2475 r
= image::snapshot::iterate(hctx
, duplicate_name_lambda
);
2480 std::string src_snap_key
;
2481 key_from_snap_id(src_snap_id
, &src_snap_key
);
2482 r
= read_key(hctx
, src_snap_key
, &snap_meta
);
2484 CLS_LOG(20, "cannot find existing snap with snap id = %" PRIu64
,
2489 if (cls::rbd::get_snap_namespace_type(snap_meta
.snapshot_namespace
) !=
2490 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_USER
) {
2491 // can only rename user snapshots
2495 snap_meta
.name
= dst_snap_name
;
2496 r
= image::snapshot::write(hctx
, src_snap_key
, std::move(snap_meta
));
2505 * Removes a snapshot from an rbd header.
2508 * @param snap_id the id of the snapshot to remove (uint64_t)
2511 * @returns 0 on success, negative error code on failure
2513 int snapshot_remove(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2518 auto iter
= in
->cbegin();
2519 decode(snap_id
, iter
);
2520 } catch (const ceph::buffer::error
&err
) {
2524 CLS_LOG(20, "snapshot_remove id=%llu", (unsigned long long)snap_id
.val
);
2526 // check if the key exists. we can't rely on remove_key doing this for
2527 // us, since OMAPRMKEYS returns success if the key is not there.
2528 // bug or feature? sounds like a bug, since tmap did not have this
2529 // behavior, but cls_rgw may rely on it...
2531 string snapshot_key
;
2532 key_from_snap_id(snap_id
, &snapshot_key
);
2533 int r
= read_key(hctx
, snapshot_key
, &snap
);
2538 if (snap
.protection_status
!= RBD_PROTECTION_STATUS_UNPROTECTED
) {
2542 // snapshot is in-use by clone v2 child
2543 if (snap
.child_count
> 0) {
2547 r
= remove_key(hctx
, snapshot_key
);
2552 bool has_child_snaps
= false;
2553 bool has_trash_snaps
= false;
2554 auto remove_lambda
= [snap_id
, &has_child_snaps
, &has_trash_snaps
](
2555 const cls_rbd_snap
& snap_meta
) {
2556 if (snap_meta
.id
!= snap_id
) {
2557 if (snap_meta
.parent
.pool_id
!= -1 || snap_meta
.parent_overlap
) {
2558 has_child_snaps
= true;
2561 if (cls::rbd::get_snap_namespace_type(snap_meta
.snapshot_namespace
) ==
2562 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH
) {
2563 has_trash_snaps
= true;
2569 r
= image::snapshot::iterate(hctx
, remove_lambda
);
2574 cls_rbd_parent parent
;
2575 r
= read_key(hctx
, "parent", &parent
);
2576 if (r
< 0 && r
!= -ENOENT
) {
2580 bool has_parent
= (r
>= 0 && parent
.exists());
2581 bool is_head_child
= (has_parent
&& parent
.head_overlap
);
2582 ceph_release_t require_osd_release
= cls_get_required_osd_release(hctx
);
2583 if (has_parent
&& !is_head_child
&& !has_child_snaps
&&
2584 require_osd_release
>= ceph_release_t::nautilus
) {
2585 // remove the unused parent image spec
2586 r
= remove_key(hctx
, "parent");
2587 if (r
< 0 && r
!= -ENOENT
) {
2592 uint64_t op_features_mask
= 0ULL;
2593 if (!has_child_snaps
&& !is_head_child
) {
2594 // disable clone child op feature if no longer associated
2595 op_features_mask
|= RBD_OPERATION_FEATURE_CLONE_CHILD
;
2597 if (!has_trash_snaps
) {
2598 // remove the snap_trash op feature if not in-use by any other snapshots
2599 op_features_mask
|= RBD_OPERATION_FEATURE_SNAP_TRASH
;
2602 if (op_features_mask
!= 0ULL) {
2603 r
= image::set_op_features(hctx
, 0, op_features_mask
);
2613 * Moves a snapshot to the trash namespace.
2616 * @param snap_id the id of the snapshot to move to the trash (uint64_t)
2619 * @returns 0 on success, negative error code on failure
2621 int snapshot_trash_add(cls_method_context_t hctx
, bufferlist
*in
,
2627 auto iter
= in
->cbegin();
2628 decode(snap_id
, iter
);
2629 } catch (const ceph::buffer::error
&err
) {
2633 CLS_LOG(20, "snapshot_trash_add id=%" PRIu64
, snap_id
.val
);
2636 std::string snapshot_key
;
2637 key_from_snap_id(snap_id
, &snapshot_key
);
2638 int r
= read_key(hctx
, snapshot_key
, &snap
);
2643 if (snap
.protection_status
!= RBD_PROTECTION_STATUS_UNPROTECTED
) {
2647 auto snap_type
= cls::rbd::get_snap_namespace_type(snap
.snapshot_namespace
);
2648 if (snap_type
== cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH
) {
2652 // add snap_trash feature bit if not already enabled
2653 r
= image::set_op_features(hctx
, RBD_OPERATION_FEATURE_SNAP_TRASH
,
2654 RBD_OPERATION_FEATURE_SNAP_TRASH
);
2659 snap
.snapshot_namespace
= cls::rbd::TrashSnapshotNamespace
{snap_type
,
2662 uuid_gen
.generate_random();
2663 snap
.name
= uuid_gen
.to_string();
2665 r
= image::snapshot::write(hctx
, snapshot_key
, std::move(snap
));
2674 * Returns a uint64_t of all the features supported by this class.
2676 int get_all_features(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2678 uint64_t all_features
= RBD_FEATURES_ALL
;
2679 encode(all_features
, *out
);
2684 * "Copy up" data from the parent of a clone to the clone's object(s).
2685 * Used for implementing copy-on-write for a clone image. Client
2686 * will pass down a chunk of data that fits completely within one
2687 * clone block (one object), and is aligned (starts at beginning of block),
2688 * but may be shorter (for non-full parent blocks). The class method
2689 * can't know the object size to validate the requested length,
2690 * so it just writes the data as given if the child object doesn't
2691 * already exist, and returns success if it does.
2694 * @param in bufferlist of data to write
2697 * @returns 0 on success, or if block already exists in child
2698 * negative error code on other error
2701 int copyup(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2703 // check for existence; if child object exists, just return success
2704 if (cls_cxx_stat(hctx
, NULL
, NULL
) == 0)
2706 CLS_LOG(20, "copyup: writing length %d\n", in
->length());
2707 return cls_cxx_write(hctx
, 0, in
->length(), in
);
2712 * @param extent_map map of extents to write
2713 * @param data bufferlist of data to write
2716 * @returns 0 on success, or if block already exists in child
2717 * negative error code on other error
2720 int sparse_copyup(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2722 std::map
<uint64_t, uint64_t> extent_map
;
2726 auto iter
= in
->cbegin();
2727 decode(extent_map
, iter
);
2729 } catch (const ceph::buffer::error
&err
) {
2730 CLS_LOG(20, "sparse_copyup: invalid decode");
2734 int r
= check_exists(hctx
);
2739 if (extent_map
.empty()) {
2740 CLS_LOG(20, "sparse_copyup: create empty object");
2741 r
= cls_cxx_create(hctx
, true);
2745 uint64_t data_offset
= 0;
2746 for (auto &it
: extent_map
) {
2747 auto off
= it
.first
;
2748 auto len
= it
.second
;
2752 tmpbl
.substr_of(data
, data_offset
, len
);
2753 } catch (const ceph::buffer::error
&err
) {
2754 CLS_LOG(20, "sparse_copyup: invalid data");
2759 CLS_LOG(20, "sparse_copyup: writing extent %" PRIu64
"~%" PRIu64
"\n", off
,
2761 int r
= cls_cxx_write(hctx
, off
, len
, &tmpbl
);
2763 CLS_ERR("sparse_copyup: error writing extent %" PRIu64
"~%" PRIu64
": %s",
2764 off
, len
, cpp_strerror(r
).c_str());
2772 /************************ rbd_id object methods **************************/
2779 * @param id the id stored in the object
2780 * @returns 0 on success, negative error code on failure
2782 int get_id(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2785 int r
= cls_cxx_stat(hctx
, &size
, NULL
);
2793 r
= cls_cxx_read(hctx
, 0, size
, &read_bl
);
2795 CLS_ERR("get_id: could not read id: %s", cpp_strerror(r
).c_str());
2801 auto iter
= read_bl
.cbegin();
2803 } catch (const ceph::buffer::error
&err
) {
2812 * Set the id of an image. The object must already exist.
2815 * @param id the id of the image, as an alpha-numeric string
2818 * @returns 0 on success, -EEXIST if the atomic create fails,
2819 * negative error code on other error
2821 int set_id(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2823 int r
= check_exists(hctx
);
2829 auto iter
= in
->cbegin();
2831 } catch (const ceph::buffer::error
&err
) {
2835 if (!is_valid_id(id
)) {
2836 CLS_ERR("set_id: invalid id '%s'", id
.c_str());
2841 r
= cls_cxx_stat(hctx
, &size
, NULL
);
2847 CLS_LOG(20, "set_id: id=%s", id
.c_str());
2849 bufferlist write_bl
;
2850 encode(id
, write_bl
);
2851 return cls_cxx_write(hctx
, 0, write_bl
.length(), &write_bl
);
2855 * Update the access timestamp of an image
2861 * @returns 0 on success, negative error code on other error
2863 int set_access_timestamp(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2865 int r
= check_exists(hctx
);
2869 utime_t timestamp
= ceph_clock_now();
2870 r
= write_key(hctx
, "access_timestamp", timestamp
);
2872 CLS_ERR("error setting access_timestamp");
2880 * Update the modify timestamp of an image
2886 * @returns 0 on success, negative error code on other error
2889 int set_modify_timestamp(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
2891 int r
= check_exists(hctx
);
2895 utime_t timestamp
= ceph_clock_now();
2896 r
= write_key(hctx
, "modify_timestamp", timestamp
);
2898 CLS_ERR("error setting modify_timestamp");
2907 /*********************** methods for rbd_directory ***********************/
2909 static const string
dir_key_for_id(const string
&id
)
2911 return RBD_DIR_ID_KEY_PREFIX
+ id
;
2914 static const string
dir_key_for_name(const string
&name
)
2916 return RBD_DIR_NAME_KEY_PREFIX
+ name
;
2919 static const string
dir_name_from_key(const string
&key
)
2921 return key
.substr(strlen(RBD_DIR_NAME_KEY_PREFIX
));
2924 static int dir_add_image_helper(cls_method_context_t hctx
,
2925 const string
&name
, const string
&id
,
2926 bool check_for_unique_id
)
2928 if (!name
.size() || !is_valid_id(id
)) {
2929 CLS_ERR("dir_add_image_helper: invalid name '%s' or id '%s'",
2930 name
.c_str(), id
.c_str());
2934 CLS_LOG(20, "dir_add_image_helper name=%s id=%s", name
.c_str(), id
.c_str());
2937 string name_key
= dir_key_for_name(name
);
2938 string id_key
= dir_key_for_id(id
);
2939 int r
= read_key(hctx
, name_key
, &tmp
);
2941 CLS_LOG(10, "name already exists");
2944 r
= read_key(hctx
, id_key
, &tmp
);
2945 if (r
!= -ENOENT
&& check_for_unique_id
) {
2946 CLS_LOG(10, "id already exists");
2949 bufferlist id_bl
, name_bl
;
2951 encode(name
, name_bl
);
2952 map
<string
, bufferlist
> omap_vals
;
2953 omap_vals
[name_key
] = id_bl
;
2954 omap_vals
[id_key
] = name_bl
;
2955 return cls_cxx_map_set_vals(hctx
, &omap_vals
);
2958 static int dir_remove_image_helper(cls_method_context_t hctx
,
2959 const string
&name
, const string
&id
)
2961 CLS_LOG(20, "dir_remove_image_helper name=%s id=%s",
2962 name
.c_str(), id
.c_str());
2964 string stored_name
, stored_id
;
2965 string name_key
= dir_key_for_name(name
);
2966 string id_key
= dir_key_for_id(id
);
2967 int r
= read_key(hctx
, name_key
, &stored_id
);
2970 CLS_ERR("error reading name to id mapping: %s", cpp_strerror(r
).c_str());
2973 r
= read_key(hctx
, id_key
, &stored_name
);
2975 CLS_ERR("error reading id to name mapping: %s", cpp_strerror(r
).c_str());
2979 // check if this op raced with a rename
2980 if (stored_name
!= name
|| stored_id
!= id
) {
2981 CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'",
2982 stored_name
.c_str(), stored_id
.c_str(), name
.c_str(), id
.c_str());
2986 r
= cls_cxx_map_remove_key(hctx
, name_key
);
2988 CLS_ERR("error removing name: %s", cpp_strerror(r
).c_str());
2992 r
= cls_cxx_map_remove_key(hctx
, id_key
);
2994 CLS_ERR("error removing id: %s", cpp_strerror(r
).c_str());
3002 * Rename an image in the directory, updating both indexes
3003 * atomically. This can't be done from the client calling
3004 * dir_add_image and dir_remove_image in one transaction because the
3005 * results of the first method are not visibale to later steps.
3008 * @param src original name of the image
3009 * @param dest new name of the image
3010 * @param id the id of the image
3013 * @returns -ESTALE if src and id do not map to each other
3014 * @returns -ENOENT if src or id are not in the directory
3015 * @returns -EEXIST if dest already exists
3016 * @returns 0 on success, negative error code on failure
3018 int dir_rename_image(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3020 string src
, dest
, id
;
3022 auto iter
= in
->cbegin();
3026 } catch (const ceph::buffer::error
&err
) {
3030 int r
= dir_remove_image_helper(hctx
, src
, id
);
3033 // ignore duplicate id because the result of
3034 // remove_image_helper is not visible yet
3035 return dir_add_image_helper(hctx
, dest
, id
, false);
3039 * Get the id of an image given its name.
3042 * @param name the name of the image
3045 * @param id the id of the image
3046 * @returns 0 on success, negative error code on failure
3048 int dir_get_id(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3053 auto iter
= in
->cbegin();
3055 } catch (const ceph::buffer::error
&err
) {
3059 CLS_LOG(20, "dir_get_id: name=%s", name
.c_str());
3062 int r
= read_key(hctx
, dir_key_for_name(name
), &id
);
3065 CLS_ERR("error reading id for name '%s': %s", name
.c_str(), cpp_strerror(r
).c_str());
3073 * Get the name of an image given its id.
3076 * @param id the id of the image
3079 * @param name the name of the image
3080 * @returns 0 on success, negative error code on failure
3082 int dir_get_name(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3087 auto iter
= in
->cbegin();
3089 } catch (const ceph::buffer::error
&err
) {
3093 CLS_LOG(20, "dir_get_name: id=%s", id
.c_str());
3096 int r
= read_key(hctx
, dir_key_for_id(id
), &name
);
3099 CLS_ERR("error reading name for id '%s': %s", id
.c_str(),
3100 cpp_strerror(r
).c_str());
3109 * List the names and ids of the images in the directory, sorted by
3113 * @param start_after which name to begin listing after
3114 * (use the empty string to start at the beginning)
3115 * @param max_return the maximum number of names to list
3118 * @param images map from name to id of up to max_return images
3119 * @returns 0 on success, negative error code on failure
3121 int dir_list(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3124 uint64_t max_return
;
3127 auto iter
= in
->cbegin();
3128 decode(start_after
, iter
);
3129 decode(max_return
, iter
);
3130 } catch (const ceph::buffer::error
&err
) {
3134 int max_read
= RBD_MAX_KEYS_READ
;
3135 map
<string
, string
> images
;
3136 string last_read
= dir_key_for_name(start_after
);
3139 while (more
&& images
.size() < max_return
) {
3140 map
<string
, bufferlist
> vals
;
3141 CLS_LOG(20, "last_read = '%s'", last_read
.c_str());
3142 int r
= cls_cxx_map_get_vals(hctx
, last_read
, RBD_DIR_NAME_KEY_PREFIX
,
3143 max_read
, &vals
, &more
);
3146 CLS_ERR("error reading directory by name: %s", cpp_strerror(r
).c_str());
3151 for (auto it
= vals
.begin(); it
!= vals
.end(); ++it
) {
3153 auto iter
= it
->second
.cbegin();
3156 } catch (const ceph::buffer::error
&err
) {
3157 CLS_ERR("could not decode id of image '%s'", it
->first
.c_str());
3160 CLS_LOG(20, "adding '%s' -> '%s'", dir_name_from_key(it
->first
).c_str(), id
.c_str());
3161 images
[dir_name_from_key(it
->first
)] = id
;
3162 if (images
.size() >= max_return
)
3165 if (!vals
.empty()) {
3166 last_read
= dir_key_for_name(images
.rbegin()->first
);
3170 encode(images
, *out
);
3176 * Add an image to the rbd directory. Creates the directory object if
3177 * needed, and updates the index from id to name and name to id.
3180 * @param name the name of the image
3181 * @param id the id of the image
3184 * @returns -EEXIST if the image name is already in the directory
3185 * @returns -EBADF if the image id is already in the directory
3186 * @returns 0 on success, negative error code on failure
3188 int dir_add_image(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3190 int r
= cls_cxx_create(hctx
, false);
3192 CLS_ERR("could not create directory: %s", cpp_strerror(r
).c_str());
3198 auto iter
= in
->cbegin();
3201 } catch (const ceph::buffer::error
&err
) {
3205 return dir_add_image_helper(hctx
, name
, id
, true);
3209 * Remove an image from the rbd directory.
3212 * @param name the name of the image
3213 * @param id the id of the image
3216 * @returns -ESTALE if the name and id do not map to each other
3217 * @returns 0 on success, negative error code on failure
3219 int dir_remove_image(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3223 auto iter
= in
->cbegin();
3226 } catch (const ceph::buffer::error
&err
) {
3230 return dir_remove_image_helper(hctx
, name
, id
);
3234 * Verify the current state of the directory
3237 * @param state the DirectoryState of the directory
3240 * @returns -ENOENT if the state does not match
3241 * @returns 0 on success, negative error code on failure
3243 int dir_state_assert(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3245 cls::rbd::DirectoryState directory_state
= cls::rbd::DIRECTORY_STATE_READY
;
3247 auto iter
= in
->cbegin();
3248 decode(directory_state
, iter
);
3249 } catch (const ceph::buffer::error
&err
) {
3253 cls::rbd::DirectoryState on_disk_directory_state
= directory_state
;
3254 int r
= read_key(hctx
, "state", &on_disk_directory_state
);
3259 if (directory_state
!= on_disk_directory_state
) {
3266 * Set the current state of the directory
3269 * @param state the DirectoryState of the directory
3272 * @returns -ENOENT if the state does not match
3273 * @returns 0 on success, negative error code on failure
3275 int dir_state_set(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3277 cls::rbd::DirectoryState directory_state
;
3279 auto iter
= in
->cbegin();
3280 decode(directory_state
, iter
);
3281 } catch (const ceph::buffer::error
&err
) {
3285 int r
= check_exists(hctx
);
3286 if (r
< 0 && r
!= -ENOENT
) {
3290 switch (directory_state
) {
3291 case cls::rbd::DIRECTORY_STATE_READY
:
3293 case cls::rbd::DIRECTORY_STATE_ADD_DISABLED
:
3299 // verify that the directory is empty
3300 std::map
<std::string
, bufferlist
> vals
;
3302 r
= cls_cxx_map_get_vals(hctx
, RBD_DIR_NAME_KEY_PREFIX
,
3303 RBD_DIR_NAME_KEY_PREFIX
, 1, &vals
, &more
);
3306 } else if (!vals
.empty()) {
3315 r
= write_key(hctx
, "state", directory_state
);
3323 int object_map_read(cls_method_context_t hctx
, BitVector
<2> &object_map
)
3326 int r
= cls_cxx_stat(hctx
, &size
, NULL
);
3335 r
= cls_cxx_read(hctx
, 0, size
, &bl
);
3341 auto iter
= bl
.cbegin();
3342 decode(object_map
, iter
);
3343 } catch (const ceph::buffer::error
&err
) {
3344 CLS_ERR("failed to decode object map: %s", err
.what());
3351 * Load an rbd image's object map
3357 * @param object map bit vector
3358 * @returns 0 on success, negative error code on failure
3360 int object_map_load(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3362 BitVector
<2> object_map
;
3363 int r
= object_map_read(hctx
, object_map
);
3368 object_map
.set_crc_enabled(false);
3369 encode(object_map
, *out
);
3374 * Save an rbd image's object map
3377 * @param object map bit vector
3380 * @returns 0 on success, negative error code on failure
3382 int object_map_save(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3384 BitVector
<2> object_map
;
3386 auto iter
= in
->cbegin();
3387 decode(object_map
, iter
);
3388 } catch (const ceph::buffer::error
&err
) {
3392 object_map
.set_crc_enabled(true);
3395 encode(object_map
, bl
);
3396 CLS_LOG(20, "object_map_save: object size=%" PRIu64
", byte size=%u",
3397 object_map
.size(), bl
.length());
3398 return cls_cxx_write_full(hctx
, &bl
);
3402 * Resize an rbd image's object map
3405 * @param object_count the max number of objects in the image
3406 * @param default_state the default state of newly created objects
3409 * @returns 0 on success, negative error code on failure
3411 int object_map_resize(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3413 uint64_t object_count
;
3414 uint8_t default_state
;
3416 auto iter
= in
->cbegin();
3417 decode(object_count
, iter
);
3418 decode(default_state
, iter
);
3419 } catch (const ceph::buffer::error
&err
) {
3423 // protect against excessive memory requirements
3424 if (object_count
> cls::rbd::MAX_OBJECT_MAP_OBJECT_COUNT
) {
3425 CLS_ERR("object map too large: %" PRIu64
, object_count
);
3429 BitVector
<2> object_map
;
3430 int r
= object_map_read(hctx
, object_map
);
3431 if ((r
< 0) && (r
!= -ENOENT
)) {
3435 size_t orig_object_map_size
= object_map
.size();
3436 if (object_count
< orig_object_map_size
) {
3437 auto it
= object_map
.begin() + object_count
;
3438 auto end_it
= object_map
.end() ;
3439 uint64_t i
= object_count
;
3440 for (; it
!= end_it
; ++it
, ++i
) {
3441 if (*it
!= default_state
) {
3442 CLS_ERR("object map indicates object still exists: %" PRIu64
, i
);
3446 object_map
.resize(object_count
);
3447 } else if (object_count
> orig_object_map_size
) {
3448 object_map
.resize(object_count
);
3449 auto it
= object_map
.begin() + orig_object_map_size
;
3450 auto end_it
= object_map
.end();
3451 for (; it
!= end_it
; ++it
) {
3452 *it
= default_state
;
3457 encode(object_map
, map
);
3458 CLS_LOG(20, "object_map_resize: object size=%" PRIu64
", byte size=%u",
3459 object_count
, map
.length());
3460 return cls_cxx_write_full(hctx
, &map
);
3464 * Update an rbd image's object map
3467 * @param start_object_no the start object iterator
3468 * @param end_object_no the end object iterator
3469 * @param new_object_state the new object state
3470 * @param current_object_state optional current object state filter
3473 * @returns 0 on success, negative error code on failure
3475 int object_map_update(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3477 uint64_t start_object_no
;
3478 uint64_t end_object_no
;
3479 uint8_t new_object_state
;
3480 boost::optional
<uint8_t> current_object_state
;
3482 auto iter
= in
->cbegin();
3483 decode(start_object_no
, iter
);
3484 decode(end_object_no
, iter
);
3485 decode(new_object_state
, iter
);
3486 decode(current_object_state
, iter
);
3487 } catch (const ceph::buffer::error
&err
) {
3488 CLS_ERR("failed to decode message");
3493 int r
= cls_cxx_stat(hctx
, &size
, NULL
);
3498 BitVector
<2> object_map
;
3499 bufferlist header_bl
;
3500 r
= cls_cxx_read2(hctx
, 0, object_map
.get_header_length(), &header_bl
,
3501 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED
);
3503 CLS_ERR("object map header read failed");
3508 auto it
= header_bl
.cbegin();
3509 object_map
.decode_header(it
);
3510 } catch (const ceph::buffer::error
&err
) {
3511 CLS_ERR("failed to decode object map header: %s", err
.what());
3515 uint64_t object_byte_offset
;
3516 uint64_t byte_length
;
3517 object_map
.get_header_crc_extents(&object_byte_offset
, &byte_length
);
3519 bufferlist footer_bl
;
3520 r
= cls_cxx_read2(hctx
, object_byte_offset
, byte_length
, &footer_bl
,
3521 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED
);
3523 CLS_ERR("object map footer read header CRC failed");
3528 auto it
= footer_bl
.cbegin();
3529 object_map
.decode_header_crc(it
);
3530 } catch (const ceph::buffer::error
&err
) {
3531 CLS_ERR("failed to decode object map header CRC: %s", err
.what());
3534 if (start_object_no
>= end_object_no
|| end_object_no
> object_map
.size()) {
3538 uint64_t object_count
= end_object_no
- start_object_no
;
3539 object_map
.get_data_crcs_extents(start_object_no
, object_count
,
3540 &object_byte_offset
, &byte_length
);
3541 const auto footer_object_offset
= object_byte_offset
;
3544 r
= cls_cxx_read2(hctx
, object_byte_offset
, byte_length
, &footer_bl
,
3545 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED
);
3547 CLS_ERR("object map footer read data CRCs failed");
3552 auto it
= footer_bl
.cbegin();
3553 object_map
.decode_data_crcs(it
, start_object_no
);
3554 } catch (const ceph::buffer::error
&err
) {
3555 CLS_ERR("failed to decode object map data CRCs: %s", err
.what());
3558 uint64_t data_byte_offset
;
3559 object_map
.get_data_extents(start_object_no
, object_count
,
3560 &data_byte_offset
, &object_byte_offset
,
3564 r
= cls_cxx_read2(hctx
, object_byte_offset
, byte_length
, &data_bl
,
3565 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED
);
3567 CLS_ERR("object map data read failed");
3572 auto it
= data_bl
.cbegin();
3573 object_map
.decode_data(it
, data_byte_offset
);
3574 } catch (const ceph::buffer::error
&err
) {
3575 CLS_ERR("failed to decode data chunk [%" PRIu64
"]: %s",
3576 data_byte_offset
, err
.what());
3580 bool updated
= false;
3581 auto it
= object_map
.begin() + start_object_no
;
3582 auto end_it
= object_map
.begin() + end_object_no
;
3583 for (; it
!= end_it
; ++it
) {
3584 uint8_t state
= *it
;
3585 if ((!current_object_state
|| state
== *current_object_state
||
3586 (*current_object_state
== OBJECT_EXISTS
&&
3587 state
== OBJECT_EXISTS_CLEAN
)) && state
!= new_object_state
) {
3588 *it
= new_object_state
;
3594 CLS_LOG(20, "object_map_update: %" PRIu64
"~%" PRIu64
" -> %" PRIu64
,
3595 data_byte_offset
, byte_length
, object_byte_offset
);
3598 object_map
.encode_data(data_bl
, data_byte_offset
, byte_length
);
3599 r
= cls_cxx_write2(hctx
, object_byte_offset
, data_bl
.length(), &data_bl
,
3600 CEPH_OSD_OP_FLAG_FADVISE_WILLNEED
);
3602 CLS_ERR("failed to write object map header: %s", cpp_strerror(r
).c_str());
3607 object_map
.encode_data_crcs(footer_bl
, start_object_no
, object_count
);
3608 r
= cls_cxx_write2(hctx
, footer_object_offset
, footer_bl
.length(),
3609 &footer_bl
, CEPH_OSD_OP_FLAG_FADVISE_WILLNEED
);
3611 CLS_ERR("failed to write object map footer: %s", cpp_strerror(r
).c_str());
3615 CLS_LOG(20, "object_map_update: no update necessary");
3622 * Mark all _EXISTS objects as _EXISTS_CLEAN so future writes to the
3623 * image HEAD can be tracked.
3629 * @returns 0 on success, negative error code on failure
3631 int object_map_snap_add(cls_method_context_t hctx
, bufferlist
*in
,
3634 BitVector
<2> object_map
;
3635 int r
= object_map_read(hctx
, object_map
);
3640 bool updated
= false;
3641 auto it
= object_map
.begin();
3642 auto end_it
= object_map
.end();
3643 for (; it
!= end_it
; ++it
) {
3644 if (*it
== OBJECT_EXISTS
) {
3645 *it
= OBJECT_EXISTS_CLEAN
;
3652 encode(object_map
, bl
);
3653 r
= cls_cxx_write_full(hctx
, &bl
);
3659 * Mark all _EXISTS_CLEAN objects as _EXISTS in the current object map
3660 * if the provided snapshot object map object is marked as _EXISTS.
3663 * @param snapshot object map bit vector
3666 * @returns 0 on success, negative error code on failure
3668 int object_map_snap_remove(cls_method_context_t hctx
, bufferlist
*in
,
3671 BitVector
<2> src_object_map
;
3673 auto iter
= in
->cbegin();
3674 decode(src_object_map
, iter
);
3675 } catch (const ceph::buffer::error
&err
) {
3679 BitVector
<2> dst_object_map
;
3680 int r
= object_map_read(hctx
, dst_object_map
);
3685 bool updated
= false;
3686 auto src_it
= src_object_map
.begin();
3687 auto dst_it
= dst_object_map
.begin();
3688 auto dst_it_end
= dst_object_map
.end();
3690 for (; dst_it
!= dst_it_end
; ++dst_it
) {
3691 if (*dst_it
== OBJECT_EXISTS_CLEAN
&&
3692 (i
>= src_object_map
.size() || *src_it
== OBJECT_EXISTS
)) {
3693 *dst_it
= OBJECT_EXISTS
;
3696 if (i
< src_object_map
.size())
3703 encode(dst_object_map
, bl
);
3704 r
= cls_cxx_write_full(hctx
, &bl
);
3709 static const string
metadata_key_for_name(const string
&name
)
3711 return RBD_METADATA_KEY_PREFIX
+ name
;
3714 static const string
metadata_name_from_key(const string
&key
)
3716 return key
.substr(strlen(RBD_METADATA_KEY_PREFIX
));
3721 * @param start_after which name to begin listing after
3722 * (use the empty string to start at the beginning)
3723 * @param max_return the maximum number of names to list
3727 * @returns 0 on success, negative error code on failure
3729 int metadata_list(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3732 uint64_t max_return
;
3735 auto iter
= in
->cbegin();
3736 decode(start_after
, iter
);
3737 decode(max_return
, iter
);
3738 } catch (const ceph::buffer::error
&err
) {
3742 // TODO remove implicit support for zero during the N-release
3743 if (max_return
== 0) {
3744 max_return
= RBD_MAX_KEYS_READ
;
3747 map
<string
, bufferlist
> data
;
3748 string last_read
= metadata_key_for_name(start_after
);
3751 while (more
&& data
.size() < max_return
) {
3752 map
<string
, bufferlist
> raw_data
;
3753 int max_read
= std::min
<uint64_t>(RBD_MAX_KEYS_READ
, max_return
- data
.size());
3754 int r
= cls_cxx_map_get_vals(hctx
, last_read
, RBD_METADATA_KEY_PREFIX
,
3755 max_read
, &raw_data
, &more
);
3758 CLS_ERR("failed to read the vals off of disk: %s",
3759 cpp_strerror(r
).c_str());
3764 for (auto& kv
: raw_data
) {
3765 data
[metadata_name_from_key(kv
.first
)].swap(kv
.second
);
3768 if (!raw_data
.empty()) {
3769 last_read
= raw_data
.rbegin()->first
;
3779 * @param data <map(key, value)>
3782 * @returns 0 on success, negative error code on failure
3784 int metadata_set(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3786 map
<string
, bufferlist
> data
, raw_data
;
3788 auto iter
= in
->cbegin();
3791 } catch (const ceph::buffer::error
&err
) {
3795 for (auto it
= data
.begin(); it
!= data
.end(); ++it
) {
3796 CLS_LOG(20, "metadata_set key=%s value=%.*s", it
->first
.c_str(),
3797 it
->second
.length(), it
->second
.c_str());
3798 raw_data
[metadata_key_for_name(it
->first
)].swap(it
->second
);
3800 int r
= cls_cxx_map_set_vals(hctx
, &raw_data
);
3802 CLS_ERR("error writing metadata: %s", cpp_strerror(r
).c_str());
3814 * @returns 0 on success, negative error code on failure
3816 int metadata_remove(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3820 auto iter
= in
->cbegin();
3823 } catch (const ceph::buffer::error
&err
) {
3827 CLS_LOG(20, "metadata_remove key=%s", key
.c_str());
3829 int r
= cls_cxx_map_remove_key(hctx
, metadata_key_for_name(key
));
3831 CLS_ERR("error removing metadata: %s", cpp_strerror(r
).c_str());
3843 * @param metadata value associated with the key
3844 * @returns 0 on success, negative error code on failure
3846 int metadata_get(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3851 auto iter
= in
->cbegin();
3854 } catch (const ceph::buffer::error
&err
) {
3858 CLS_LOG(20, "metadata_get key=%s", key
.c_str());
3860 int r
= cls_cxx_map_get_val(hctx
, metadata_key_for_name(key
), &value
);
3863 CLS_ERR("error getting metadata: %s", cpp_strerror(r
).c_str());
3867 encode(value
, *out
);
3871 int snapshot_get_limit(cls_method_context_t hctx
, bufferlist
*in
,
3874 uint64_t snap_limit
;
3875 int r
= read_key(hctx
, "snap_limit", &snap_limit
);
3877 snap_limit
= UINT64_MAX
;
3879 CLS_ERR("error retrieving snapshot limit: %s", cpp_strerror(r
).c_str());
3883 CLS_LOG(20, "read snapshot limit %" PRIu64
, snap_limit
);
3884 encode(snap_limit
, *out
);
3889 int snapshot_set_limit(cls_method_context_t hctx
, bufferlist
*in
,
3895 size_t snap_count
= 0;
3898 auto iter
= in
->cbegin();
3899 decode(new_limit
, iter
);
3900 } catch (const ceph::buffer::error
&err
) {
3904 if (new_limit
== UINT64_MAX
) {
3905 CLS_LOG(20, "remove snapshot limit\n");
3906 rc
= cls_cxx_map_remove_key(hctx
, "snap_limit");
3910 //try to read header as v1 format
3911 rc
= snap_read_header(hctx
, bl
);
3913 // error when reading header
3914 if (rc
< 0 && rc
!= -EINVAL
) {
3916 } else if (rc
>= 0) {
3917 // success, the image is v1 format
3918 struct rbd_obj_header_ondisk
*header
;
3919 header
= (struct rbd_obj_header_ondisk
*)bl
.c_str();
3920 snap_count
= header
->snap_count
;
3922 // else, the image is v2 format
3923 int max_read
= RBD_MAX_KEYS_READ
;
3924 string last_read
= RBD_SNAP_KEY_PREFIX
;
3929 rc
= cls_cxx_map_get_keys(hctx
, last_read
, max_read
, &keys
, &more
);
3931 CLS_ERR("error retrieving snapshots: %s", cpp_strerror(rc
).c_str());
3934 for (auto& key
: keys
) {
3935 if (key
.find(RBD_SNAP_KEY_PREFIX
) != 0)
3940 last_read
= *(keys
.rbegin());
3944 if (new_limit
< snap_count
) {
3946 CLS_LOG(10, "snapshot limit is less than the number of snapshots.\n");
3948 CLS_LOG(20, "set snapshot limit to %" PRIu64
"\n", new_limit
);
3950 encode(new_limit
, bl
);
3951 rc
= cls_cxx_map_set_val(hctx
, "snap_limit", &bl
);
3960 * @param snap id (uint64_t) parent snapshot id
3961 * @param child spec (cls::rbd::ChildImageSpec) child image
3964 * @returns 0 on success, negative error code on failure
3966 int child_attach(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
3969 cls::rbd::ChildImageSpec child_image
;
3971 auto it
= in
->cbegin();
3972 decode(snap_id
, it
);
3973 decode(child_image
, it
);
3974 } catch (const ceph::buffer::error
&err
) {
3978 CLS_LOG(20, "child_attach snap_id=%" PRIu64
", child_pool_id=%" PRIi64
", "
3979 "child_image_id=%s", snap_id
, child_image
.pool_id
,
3980 child_image
.image_id
.c_str());
3983 std::string snapshot_key
;
3984 key_from_snap_id(snap_id
, &snapshot_key
);
3985 int r
= read_key(hctx
, snapshot_key
, &snap
);
3990 if (cls::rbd::get_snap_namespace_type(snap
.snapshot_namespace
) ==
3991 cls::rbd::SNAPSHOT_NAMESPACE_TYPE_TRASH
) {
3992 // cannot attach to a deleted snapshot
3996 auto children_key
= image::snap_children_key_from_snap_id(snap_id
);
3997 cls::rbd::ChildImageSpecs child_images
;
3998 r
= read_key(hctx
, children_key
, &child_images
);
3999 if (r
< 0 && r
!= -ENOENT
) {
4000 CLS_ERR("error reading snapshot children: %s", cpp_strerror(r
).c_str());
4004 auto it
= child_images
.insert(child_image
);
4006 // child already attached to the snapshot
4010 r
= write_key(hctx
, children_key
, child_images
);
4012 CLS_ERR("error writing snapshot children: %s", cpp_strerror(r
).c_str());
4017 r
= image::snapshot::write(hctx
, snapshot_key
, std::move(snap
));
4022 r
= image::set_op_features(hctx
, RBD_OPERATION_FEATURE_CLONE_PARENT
,
4023 RBD_OPERATION_FEATURE_CLONE_PARENT
);
4033 * @param snap id (uint64_t) parent snapshot id
4034 * @param child spec (cls::rbd::ChildImageSpec) child image
4037 * @returns 0 on success, negative error code on failure
4039 int child_detach(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
4042 cls::rbd::ChildImageSpec child_image
;
4044 auto it
= in
->cbegin();
4045 decode(snap_id
, it
);
4046 decode(child_image
, it
);
4047 } catch (const ceph::buffer::error
&err
) {
4051 CLS_LOG(20, "child_detach snap_id=%" PRIu64
", child_pool_id=%" PRIi64
", "
4052 "child_image_id=%s", snap_id
, child_image
.pool_id
,
4053 child_image
.image_id
.c_str());
4056 std::string snapshot_key
;
4057 key_from_snap_id(snap_id
, &snapshot_key
);
4058 int r
= read_key(hctx
, snapshot_key
, &snap
);
4063 auto children_key
= image::snap_children_key_from_snap_id(snap_id
);
4064 cls::rbd::ChildImageSpecs child_images
;
4065 r
= read_key(hctx
, children_key
, &child_images
);
4066 if (r
< 0 && r
!= -ENOENT
) {
4067 CLS_ERR("error reading snapshot children: %s", cpp_strerror(r
).c_str());
4071 if (snap
.child_count
!= child_images
.size()) {
4072 // children and reference count don't match
4073 CLS_ERR("children reference count mismatch: %" PRIu64
, snap_id
);
4077 if (child_images
.erase(child_image
) == 0) {
4078 // child not attached to the snapshot
4082 if (child_images
.empty()) {
4083 r
= remove_key(hctx
, children_key
);
4085 r
= write_key(hctx
, children_key
, child_images
);
4087 CLS_ERR("error writing snapshot children: %s", cpp_strerror(r
).c_str());
4093 r
= image::snapshot::write(hctx
, snapshot_key
, std::move(snap
));
4098 if (snap
.child_count
== 0) {
4099 auto clone_in_use_lambda
= [snap_id
](const cls_rbd_snap
& snap_meta
) {
4100 if (snap_meta
.id
!= snap_id
&& snap_meta
.child_count
> 0) {
4106 r
= image::snapshot::iterate(hctx
, clone_in_use_lambda
);
4107 if (r
< 0 && r
!= -EEXIST
) {
4112 // remove the clone_v2 op feature if not in-use by any other snapshots
4113 r
= image::set_op_features(hctx
, 0, RBD_OPERATION_FEATURE_CLONE_PARENT
);
4125 * @param snap id (uint64_t) parent snapshot id
4128 * @param (cls::rbd::ChildImageSpecs) child images
4129 * @returns 0 on success, negative error code on failure
4131 int children_list(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
4135 auto it
= in
->cbegin();
4136 decode(snap_id
, it
);
4137 } catch (const ceph::buffer::error
&err
) {
4141 CLS_LOG(20, "child_detach snap_id=%" PRIu64
, snap_id
);
4144 std::string snapshot_key
;
4145 key_from_snap_id(snap_id
, &snapshot_key
);
4146 int r
= read_key(hctx
, snapshot_key
, &snap
);
4151 auto children_key
= image::snap_children_key_from_snap_id(snap_id
);
4152 cls::rbd::ChildImageSpecs child_images
;
4153 r
= read_key(hctx
, children_key
, &child_images
);
4157 CLS_ERR("error reading snapshot children: %s", cpp_strerror(r
).c_str());
4161 encode(child_images
, *out
);
4166 * Set image migration.
4169 * @param migration_spec (cls::rbd::MigrationSpec) image migration spec
4173 * @returns 0 on success, negative error code on failure
4175 int migration_set(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
) {
4176 cls::rbd::MigrationSpec migration_spec
;
4178 auto it
= in
->cbegin();
4179 decode(migration_spec
, it
);
4180 } catch (const ceph::buffer::error
&err
) {
4184 int r
= image::set_migration(hctx
, migration_spec
, true);
4193 * Set image migration state.
4196 * @param state (cls::rbd::MigrationState) migration state
4197 * @param description (std::string) migration state description
4201 * @returns 0 on success, negative error code on failure
4203 int migration_set_state(cls_method_context_t hctx
, bufferlist
*in
,
4205 cls::rbd::MigrationState state
;
4206 std::string description
;
4208 auto it
= in
->cbegin();
4210 decode(description
, it
);
4211 } catch (const ceph::buffer::error
&err
) {
4215 cls::rbd::MigrationSpec migration_spec
;
4216 int r
= image::read_migration(hctx
, &migration_spec
);
4221 migration_spec
.state
= state
;
4222 migration_spec
.state_description
= description
;
4224 r
= image::set_migration(hctx
, migration_spec
, false);
4233 * Get image migration spec.
4238 * @param migration_spec (cls::rbd::MigrationSpec) image migration spec
4240 * @returns 0 on success, negative error code on failure
4242 int migration_get(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
) {
4243 cls::rbd::MigrationSpec migration_spec
;
4244 int r
= image::read_migration(hctx
, &migration_spec
);
4249 encode(migration_spec
, *out
);
4255 * Remove image migration spec.
4261 * @returns 0 on success, negative error code on failure
4263 int migration_remove(cls_method_context_t hctx
, bufferlist
*in
,
4265 int r
= image::remove_migration(hctx
);
4274 * Ensure writer snapc state
4277 * @param snap id (uint64_t) snap context sequence id
4278 * @param state (cls::rbd::AssertSnapcSeqState) snap context state
4281 * @returns -ERANGE if assertion fails
4282 * @returns 0 on success, negative error code on failure
4284 int assert_snapc_seq(cls_method_context_t hctx
, bufferlist
*in
,
4288 cls::rbd::AssertSnapcSeqState state
;
4290 auto it
= in
->cbegin();
4291 decode(snapc_seq
, it
);
4293 } catch (const ceph::buffer::error
&err
) {
4297 uint64_t snapset_seq
;
4298 int r
= cls_get_snapset_seq(hctx
, &snapset_seq
);
4299 if (r
< 0 && r
!= -ENOENT
) {
4304 case cls::rbd::ASSERT_SNAPC_SEQ_GT_SNAPSET_SEQ
:
4305 return (r
== -ENOENT
|| snapc_seq
> snapset_seq
) ? 0 : -ERANGE
;
4306 case cls::rbd::ASSERT_SNAPC_SEQ_LE_SNAPSET_SEQ
:
4307 return (r
== -ENOENT
|| snapc_seq
> snapset_seq
) ? -ERANGE
: 0;
4313 /****************************** Old format *******************************/
4315 int old_snapshots_list(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
4318 struct rbd_obj_header_ondisk
*header
;
4319 int rc
= snap_read_header(hctx
, bl
);
4323 header
= (struct rbd_obj_header_ondisk
*)bl
.c_str();
4324 bufferptr
p(header
->snap_names_len
);
4325 char *buf
= (char *)header
;
4326 char *name
= buf
+ sizeof(*header
) + header
->snap_count
* sizeof(struct rbd_obj_snap_ondisk
);
4327 char *end
= name
+ header
->snap_names_len
;
4329 buf
+ sizeof(*header
) + header
->snap_count
* sizeof(struct rbd_obj_snap_ondisk
),
4330 header
->snap_names_len
);
4332 encode(header
->snap_seq
, *out
);
4333 encode(header
->snap_count
, *out
);
4335 for (unsigned i
= 0; i
< header
->snap_count
; i
++) {
4337 encode(header
->snaps
[i
].id
, *out
);
4338 encode(header
->snaps
[i
].image_size
, *out
);
4341 name
+= strlen(name
) + 1;
4349 int old_snapshot_add(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
4352 struct rbd_obj_header_ondisk
*header
;
4354 bufferptr
header_bp(sizeof(*header
));
4355 struct rbd_obj_snap_ondisk
*new_snaps
;
4357 int rc
= snap_read_header(hctx
, bl
);
4361 header
= (struct rbd_obj_header_ondisk
*)bl
.c_str();
4363 int snaps_id_ofs
= sizeof(*header
);
4364 int names_ofs
= snaps_id_ofs
+ sizeof(*new_snaps
) * header
->snap_count
;
4365 const char *snap_name
;
4366 const char *snap_names
= ((char *)header
) + names_ofs
;
4367 const char *end
= snap_names
+ header
->snap_names_len
;
4368 auto iter
= in
->cbegin();
4374 decode(snap_id
, iter
);
4375 } catch (const ceph::buffer::error
&err
) {
4378 snap_name
= s
.c_str();
4380 if (header
->snap_seq
> snap_id
)
4383 uint64_t snap_limit
;
4384 rc
= read_key(hctx
, "snap_limit", &snap_limit
);
4385 if (rc
== -ENOENT
) {
4386 snap_limit
= UINT64_MAX
;
4387 } else if (rc
< 0) {
4391 if (header
->snap_count
>= snap_limit
)
4394 const char *cur_snap_name
;
4395 for (cur_snap_name
= snap_names
; cur_snap_name
< end
; cur_snap_name
+= strlen(cur_snap_name
) + 1) {
4396 if (strncmp(cur_snap_name
, snap_name
, end
- cur_snap_name
) == 0)
4399 if (cur_snap_name
> end
)
4402 int snap_name_len
= strlen(snap_name
);
4404 bufferptr
new_names_bp(header
->snap_names_len
+ snap_name_len
+ 1);
4405 bufferptr
new_snaps_bp(sizeof(*new_snaps
) * (header
->snap_count
+ 1));
4407 /* copy snap names and append to new snap name */
4408 char *new_snap_names
= new_names_bp
.c_str();
4409 strcpy(new_snap_names
, snap_name
);
4410 memcpy(new_snap_names
+ snap_name_len
+ 1, snap_names
, header
->snap_names_len
);
4412 /* append new snap id */
4413 new_snaps
= (struct rbd_obj_snap_ondisk
*)new_snaps_bp
.c_str();
4414 memcpy(new_snaps
+ 1, header
->snaps
, sizeof(*new_snaps
) * header
->snap_count
);
4416 header
->snap_count
= header
->snap_count
+ 1;
4417 header
->snap_names_len
= header
->snap_names_len
+ snap_name_len
+ 1;
4418 header
->snap_seq
= snap_id
;
4420 new_snaps
[0].id
= snap_id
;
4421 new_snaps
[0].image_size
= header
->image_size
;
4423 memcpy(header_bp
.c_str(), header
, sizeof(*header
));
4425 newbl
.push_back(header_bp
);
4426 newbl
.push_back(new_snaps_bp
);
4427 newbl
.push_back(new_names_bp
);
4429 rc
= cls_cxx_write_full(hctx
, &newbl
);
4436 int old_snapshot_remove(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
4439 struct rbd_obj_header_ondisk
*header
;
4441 bufferptr
header_bp(sizeof(*header
));
4443 int rc
= snap_read_header(hctx
, bl
);
4447 header
= (struct rbd_obj_header_ondisk
*)bl
.c_str();
4449 int snaps_id_ofs
= sizeof(*header
);
4450 int names_ofs
= snaps_id_ofs
+ sizeof(struct rbd_obj_snap_ondisk
) * header
->snap_count
;
4451 const char *snap_name
;
4452 const char *snap_names
= ((char *)header
) + names_ofs
;
4453 const char *orig_names
= snap_names
;
4454 const char *end
= snap_names
+ header
->snap_names_len
;
4455 auto iter
= in
->cbegin();
4459 struct rbd_obj_snap_ondisk snap
;
4463 } catch (const ceph::buffer::error
&err
) {
4466 snap_name
= s
.c_str();
4468 for (i
= 0; snap_names
< end
; i
++) {
4469 if (strcmp(snap_names
, snap_name
) == 0) {
4470 snap
= header
->snaps
[i
];
4474 snap_names
+= strlen(snap_names
) + 1;
4477 CLS_ERR("couldn't find snap %s\n", snap_name
);
4481 header
->snap_names_len
= header
->snap_names_len
- (s
.length() + 1);
4482 header
->snap_count
= header
->snap_count
- 1;
4484 bufferptr
new_names_bp(header
->snap_names_len
);
4485 bufferptr
new_snaps_bp(sizeof(header
->snaps
[0]) * header
->snap_count
);
4487 memcpy(header_bp
.c_str(), header
, sizeof(*header
));
4488 newbl
.push_back(header_bp
);
4490 if (header
->snap_count
) {
4493 CLS_LOG(20, "i=%u\n", i
);
4495 snaps_len
= sizeof(header
->snaps
[0]) * i
;
4496 names_len
= snap_names
- orig_names
;
4497 memcpy(new_snaps_bp
.c_str(), header
->snaps
, snaps_len
);
4498 memcpy(new_names_bp
.c_str(), orig_names
, names_len
);
4500 snap_names
+= s
.length() + 1;
4502 if (i
< header
->snap_count
) {
4503 memcpy(new_snaps_bp
.c_str() + snaps_len
,
4504 header
->snaps
+ i
+ 1,
4505 sizeof(header
->snaps
[0]) * (header
->snap_count
- i
));
4506 memcpy(new_names_bp
.c_str() + names_len
, snap_names
, end
- snap_names
);
4508 newbl
.push_back(new_snaps_bp
);
4509 newbl
.push_back(new_names_bp
);
4512 rc
= cls_cxx_write_full(hctx
, &newbl
);
4520 * rename snapshot of old format.
4523 * @param src_snap_id old snap id of the snapshot (snapid_t)
4524 * @param dst_snap_name new name of the snapshot (string)
4527 * @returns 0 on success, negative error code on failure.
4529 int old_snapshot_rename(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
4532 struct rbd_obj_header_ondisk
*header
;
4534 bufferptr
header_bp(sizeof(*header
));
4535 snapid_t src_snap_id
;
4536 const char *dst_snap_name
;
4539 int rc
= snap_read_header(hctx
, bl
);
4543 header
= (struct rbd_obj_header_ondisk
*)bl
.c_str();
4545 int snaps_id_ofs
= sizeof(*header
);
4546 int names_ofs
= snaps_id_ofs
+ sizeof(rbd_obj_snap_ondisk
) * header
->snap_count
;
4547 const char *snap_names
= ((char *)header
) + names_ofs
;
4548 const char *orig_names
= snap_names
;
4549 const char *end
= snap_names
+ header
->snap_names_len
;
4550 auto iter
= in
->cbegin();
4555 decode(src_snap_id
, iter
);
4557 } catch (const ceph::buffer::error
&err
) {
4560 dst_snap_name
= dst
.c_str();
4562 const char *cur_snap_name
;
4563 for (cur_snap_name
= snap_names
; cur_snap_name
< end
;
4564 cur_snap_name
+= strlen(cur_snap_name
) + 1) {
4565 if (strcmp(cur_snap_name
, dst_snap_name
) == 0)
4568 if (cur_snap_name
> end
)
4570 for (i
= 0; i
< header
->snap_count
; i
++) {
4571 if (src_snap_id
== header
->snaps
[i
].id
) {
4575 snap_names
+= strlen(snap_names
) + 1;
4578 CLS_ERR("couldn't find snap %llu\n", (unsigned long long)src_snap_id
.val
);
4582 CLS_LOG(20, "rename snap with snap id %llu to dest name %s", (unsigned long long)src_snap_id
.val
, dst_snap_name
);
4583 header
->snap_names_len
= header
->snap_names_len
- strlen(snap_names
) + dst
.length();
4585 bufferptr
new_names_bp(header
->snap_names_len
);
4586 bufferptr
new_snaps_bp(sizeof(header
->snaps
[0]) * header
->snap_count
);
4588 if (header
->snap_count
) {
4590 CLS_LOG(20, "i=%u\n", i
);
4592 names_len
= snap_names
- orig_names
;
4593 memcpy(new_names_bp
.c_str(), orig_names
, names_len
);
4595 strcpy(new_names_bp
.c_str() + names_len
, dst_snap_name
);
4596 names_len
+= strlen(dst_snap_name
) + 1;
4597 snap_names
+= strlen(snap_names
) + 1;
4598 if (i
< header
->snap_count
) {
4599 memcpy(new_names_bp
.c_str() + names_len
, snap_names
, end
- snap_names
);
4601 memcpy(new_snaps_bp
.c_str(), header
->snaps
, sizeof(header
->snaps
[0]) * header
->snap_count
);
4604 memcpy(header_bp
.c_str(), header
, sizeof(*header
));
4605 newbl
.push_back(header_bp
);
4606 newbl
.push_back(new_snaps_bp
);
4607 newbl
.push_back(new_names_bp
);
4609 rc
= cls_cxx_write_full(hctx
, &newbl
);
4618 static const std::string
UUID("mirror_uuid");
4619 static const std::string
MODE("mirror_mode");
4620 static const std::string
PEER_KEY_PREFIX("mirror_peer_");
4621 static const std::string
IMAGE_KEY_PREFIX("image_");
4622 static const std::string
GLOBAL_KEY_PREFIX("global_");
4623 static const std::string
STATUS_GLOBAL_KEY_PREFIX("status_global_");
4624 static const std::string
REMOTE_STATUS_GLOBAL_KEY_PREFIX("remote_status_global_");
4625 static const std::string
INSTANCE_KEY_PREFIX("instance_");
4626 static const std::string
MIRROR_IMAGE_MAP_KEY_PREFIX("image_map_");
4628 std::string
peer_key(const std::string
&uuid
) {
4629 return PEER_KEY_PREFIX
+ uuid
;
4632 std::string
image_key(const string
&image_id
) {
4633 return IMAGE_KEY_PREFIX
+ image_id
;
4636 std::string
global_key(const string
&global_id
) {
4637 return GLOBAL_KEY_PREFIX
+ global_id
;
4640 std::string
remote_status_global_key(const std::string
& global_id
,
4641 const std::string
& mirror_uuid
) {
4642 return REMOTE_STATUS_GLOBAL_KEY_PREFIX
+ global_id
+ "_" + mirror_uuid
;
4645 std::string
status_global_key(const std::string
& global_id
,
4646 const std::string
& mirror_uuid
) {
4647 if (mirror_uuid
== cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID
) {
4648 return STATUS_GLOBAL_KEY_PREFIX
+ global_id
;
4650 return remote_status_global_key(global_id
, mirror_uuid
);
4654 std::string
instance_key(const string
&instance_id
) {
4655 return INSTANCE_KEY_PREFIX
+ instance_id
;
4658 std::string
mirror_image_map_key(const string
& global_image_id
) {
4659 return MIRROR_IMAGE_MAP_KEY_PREFIX
+ global_image_id
;
4662 int uuid_get(cls_method_context_t hctx
, std::string
*mirror_uuid
) {
4663 bufferlist mirror_uuid_bl
;
4664 int r
= cls_cxx_map_get_val(hctx
, mirror::UUID
, &mirror_uuid_bl
);
4667 CLS_ERR("error reading mirror uuid: %s", cpp_strerror(r
).c_str());
4672 *mirror_uuid
= std::string(mirror_uuid_bl
.c_str(), mirror_uuid_bl
.length());
4676 int list_watchers(cls_method_context_t hctx
,
4677 std::set
<entity_inst_t
> *entities
) {
4678 obj_list_watch_response_t watchers
;
4679 int r
= cls_cxx_list_watchers(hctx
, &watchers
);
4680 if (r
< 0 && r
!= -ENOENT
) {
4681 CLS_ERR("error listing watchers: '%s'", cpp_strerror(r
).c_str());
4686 for (auto &w
: watchers
.entries
) {
4687 entity_inst_t entity_inst
{w
.name
, w
.addr
};
4688 cls::rbd::sanitize_entity_inst(&entity_inst
);
4690 entities
->insert(entity_inst
);
4695 int read_peers(cls_method_context_t hctx
,
4696 std::vector
<cls::rbd::MirrorPeer
> *peers
) {
4697 std::string last_read
= PEER_KEY_PREFIX
;
4698 int max_read
= RBD_MAX_KEYS_READ
;
4701 std::map
<std::string
, bufferlist
> vals
;
4702 int r
= cls_cxx_map_get_vals(hctx
, last_read
, PEER_KEY_PREFIX
.c_str(),
4703 max_read
, &vals
, &more
);
4706 CLS_ERR("error reading peers: %s", cpp_strerror(r
).c_str());
4711 for (auto &it
: vals
) {
4713 auto bl_it
= it
.second
.cbegin();
4714 cls::rbd::MirrorPeer peer
;
4715 decode(peer
, bl_it
);
4716 peers
->push_back(peer
);
4717 } catch (const ceph::buffer::error
&err
) {
4718 CLS_ERR("could not decode peer '%s'", it
.first
.c_str());
4723 if (!vals
.empty()) {
4724 last_read
= vals
.rbegin()->first
;
4730 int read_peer(cls_method_context_t hctx
, const std::string
&id
,
4731 cls::rbd::MirrorPeer
*peer
) {
4733 int r
= cls_cxx_map_get_val(hctx
, peer_key(id
), &bl
);
4735 CLS_ERR("error reading peer '%s': %s", id
.c_str(),
4736 cpp_strerror(r
).c_str());
4741 auto bl_it
= bl
.cbegin();
4742 decode(*peer
, bl_it
);
4743 } catch (const ceph::buffer::error
&err
) {
4744 CLS_ERR("could not decode peer '%s'", id
.c_str());
4750 int write_peer(cls_method_context_t hctx
, const cls::rbd::MirrorPeer
&peer
) {
4754 int r
= cls_cxx_map_set_val(hctx
, peer_key(peer
.uuid
), &bl
);
4756 CLS_ERR("error writing peer '%s': %s", peer
.uuid
.c_str(),
4757 cpp_strerror(r
).c_str());
4763 int check_mirroring_enabled(cls_method_context_t hctx
) {
4764 uint32_t mirror_mode_decode
;
4765 int r
= read_key(hctx
, mirror::MODE
, &mirror_mode_decode
);
4766 if (r
< 0 && r
!= -ENOENT
) {
4768 } else if (r
== -ENOENT
||
4769 mirror_mode_decode
== cls::rbd::MIRROR_MODE_DISABLED
) {
4770 CLS_ERR("mirroring must be enabled on the pool");
4777 int peer_ping(cls_method_context_t hctx
, const std::string
& site_name
,
4778 const std::string
& mirror_uuid
) {
4779 int r
= check_mirroring_enabled(hctx
);
4784 if (site_name
.empty() || mirror_uuid
.empty()) {
4788 std::vector
<cls::rbd::MirrorPeer
> peers
;
4789 r
= read_peers(hctx
, &peers
);
4790 if (r
< 0 && r
!= -ENOENT
) {
4795 cls::rbd::MirrorPeer mirror_peer
;
4796 auto site_it
= std::find_if(peers
.begin(), peers
.end(),
4797 [&site_name
](auto& peer
) {
4798 return (peer
.site_name
== site_name
);
4801 auto mirror_uuid_it
= peers
.end();
4802 if (site_it
== peers
.end() ||
4803 (!site_it
->mirror_uuid
.empty() && site_it
->mirror_uuid
!= mirror_uuid
)) {
4804 // search for existing peer w/ same mirror_uuid
4805 mirror_uuid_it
= std::find_if(peers
.begin(), peers
.end(),
4806 [&mirror_uuid
](auto& peer
) {
4807 return (peer
.mirror_uuid
== mirror_uuid
);
4811 auto it
= peers
.end();
4812 if (site_it
!= peers
.end() && mirror_uuid_it
!= peers
.end()) {
4813 // implies two peers -- match by mirror_uuid but don't update site name
4814 it
= mirror_uuid_it
;
4815 } else if (mirror_uuid_it
!= peers
.end()) {
4816 // implies site name has been updated in remote
4817 mirror_uuid_it
->site_name
= site_name
;
4818 it
= mirror_uuid_it
;
4819 } else if (site_it
!= peers
.end()) {
4820 // implies empty mirror_uuid in peer
4821 site_it
->mirror_uuid
= mirror_uuid
;
4824 CLS_LOG(10, "auto-generating new TX-only peer: %s", site_name
.c_str());
4828 uuid_gen
.generate_random();
4829 mirror_peer
.uuid
= uuid_gen
.to_string();
4832 r
= cls_cxx_map_get_val(hctx
, peer_key(mirror_peer
.uuid
), &bl
);
4836 CLS_ERR("failed to retrieve mirror peer: %s", cpp_strerror(r
).c_str());
4841 mirror_peer
.mirror_peer_direction
= cls::rbd::MIRROR_PEER_DIRECTION_TX
;
4842 mirror_peer
.site_name
= site_name
;
4843 mirror_peer
.mirror_uuid
= mirror_uuid
;
4846 if (it
!= peers
.end()) {
4849 if (mirror_peer
.mirror_peer_direction
==
4850 cls::rbd::MIRROR_PEER_DIRECTION_RX
) {
4851 CLS_LOG(10, "switching to RX/TX peer: %s", site_name
.c_str());
4852 mirror_peer
.mirror_peer_direction
= cls::rbd::MIRROR_PEER_DIRECTION_RX_TX
;
4856 mirror_peer
.last_seen
= ceph_clock_now();
4858 if (!mirror_peer
.is_valid()) {
4859 CLS_ERR("attempting to update invalid peer: %s", site_name
.c_str());
4863 r
= write_peer(hctx
, mirror_peer
);
4871 int peer_add(cls_method_context_t hctx
, cls::rbd::MirrorPeer mirror_peer
) {
4872 int r
= check_mirroring_enabled(hctx
);
4877 if (!mirror_peer
.is_valid()) {
4878 CLS_ERR("mirror peer is not valid");
4882 std::string mirror_uuid
;
4883 r
= uuid_get(hctx
, &mirror_uuid
);
4885 CLS_ERR("error retrieving mirroring uuid: %s", cpp_strerror(r
).c_str());
4887 } else if (mirror_peer
.uuid
== mirror_uuid
) {
4888 CLS_ERR("peer uuid '%s' matches pool mirroring uuid",
4889 mirror_uuid
.c_str());
4891 } else if (mirror_peer
.mirror_peer_direction
==
4892 cls::rbd::MIRROR_PEER_DIRECTION_TX
) {
4893 CLS_ERR("peer uuid '%s' cannot use TX-only direction",
4894 mirror_peer
.uuid
.c_str());
4898 std::vector
<cls::rbd::MirrorPeer
> peers
;
4899 r
= read_peers(hctx
, &peers
);
4900 if (r
< 0 && r
!= -ENOENT
) {
4904 for (auto const &peer
: peers
) {
4905 if (peer
.uuid
== mirror_peer
.uuid
) {
4906 CLS_ERR("peer uuid '%s' already exists",
4909 } else if (peer
.site_name
== mirror_peer
.site_name
) {
4910 CLS_ERR("peer site name '%s' already exists",
4911 peer
.site_name
.c_str());
4913 } else if (!mirror_peer
.mirror_uuid
.empty() &&
4914 peer
.mirror_uuid
== mirror_peer
.mirror_uuid
) {
4915 CLS_ERR("peer mirror uuid '%s' already exists",
4916 peer
.mirror_uuid
.c_str());
4921 r
= write_peer(hctx
, mirror_peer
);
4928 int peer_remove(cls_method_context_t hctx
, const std::string
& uuid
) {
4929 int r
= cls_cxx_map_remove_key(hctx
, peer_key(uuid
));
4930 if (r
< 0 && r
!= -ENOENT
) {
4931 CLS_ERR("error removing peer: %s", cpp_strerror(r
).c_str());
4937 int image_get(cls_method_context_t hctx
, const string
&image_id
,
4938 cls::rbd::MirrorImage
*mirror_image
) {
4940 int r
= cls_cxx_map_get_val(hctx
, image_key(image_id
), &bl
);
4943 CLS_ERR("error reading mirrored image '%s': '%s'", image_id
.c_str(),
4944 cpp_strerror(r
).c_str());
4950 auto it
= bl
.cbegin();
4951 decode(*mirror_image
, it
);
4952 } catch (const ceph::buffer::error
&err
) {
4953 CLS_ERR("could not decode mirrored image '%s'", image_id
.c_str());
4960 int image_set(cls_method_context_t hctx
, const string
&image_id
,
4961 const cls::rbd::MirrorImage
&mirror_image
) {
4963 encode(mirror_image
, bl
);
4965 cls::rbd::MirrorImage existing_mirror_image
;
4966 int r
= image_get(hctx
, image_id
, &existing_mirror_image
);
4968 // make sure global id doesn't already exist
4969 std::string global_id_key
= global_key(mirror_image
.global_image_id
);
4970 std::string image_id
;
4971 r
= read_key(hctx
, global_id_key
, &image_id
);
4974 } else if (r
!= -ENOENT
) {
4975 CLS_ERR("error reading global image id: '%s': '%s'", image_id
.c_str(),
4976 cpp_strerror(r
).c_str());
4980 // make sure this was not a race for disabling
4981 if (mirror_image
.state
== cls::rbd::MIRROR_IMAGE_STATE_DISABLING
) {
4982 CLS_ERR("image '%s' is already disabled", image_id
.c_str());
4986 CLS_ERR("error reading mirrored image '%s': '%s'", image_id
.c_str(),
4987 cpp_strerror(r
).c_str());
4989 } else if (existing_mirror_image
.global_image_id
!=
4990 mirror_image
.global_image_id
) {
4991 // cannot change the global id
4995 r
= cls_cxx_map_set_val(hctx
, image_key(image_id
), &bl
);
4997 CLS_ERR("error adding mirrored image '%s': %s", image_id
.c_str(),
4998 cpp_strerror(r
).c_str());
5002 bufferlist image_id_bl
;
5003 encode(image_id
, image_id_bl
);
5004 r
= cls_cxx_map_set_val(hctx
, global_key(mirror_image
.global_image_id
),
5007 CLS_ERR("error adding global id for image '%s': %s", image_id
.c_str(),
5008 cpp_strerror(r
).c_str());
5014 int image_status_remove(cls_method_context_t hctx
,
5015 const string
&global_image_id
);
5017 int image_remove(cls_method_context_t hctx
, const string
&image_id
) {
5019 cls::rbd::MirrorImage mirror_image
;
5020 int r
= image_get(hctx
, image_id
, &mirror_image
);
5023 CLS_ERR("error reading mirrored image '%s': '%s'", image_id
.c_str(),
5024 cpp_strerror(r
).c_str());
5029 if (mirror_image
.state
!= cls::rbd::MIRROR_IMAGE_STATE_DISABLING
) {
5033 r
= cls_cxx_map_remove_key(hctx
, image_key(image_id
));
5035 CLS_ERR("error removing mirrored image '%s': %s", image_id
.c_str(),
5036 cpp_strerror(r
).c_str());
5040 r
= cls_cxx_map_remove_key(hctx
, global_key(mirror_image
.global_image_id
));
5041 if (r
< 0 && r
!= -ENOENT
) {
5042 CLS_ERR("error removing global id for image '%s': %s", image_id
.c_str(),
5043 cpp_strerror(r
).c_str());
5047 r
= image_status_remove(hctx
, mirror_image
.global_image_id
);
5055 int image_status_set(cls_method_context_t hctx
, const string
&global_image_id
,
5056 const cls::rbd::MirrorImageSiteStatus
&status
) {
5057 cls::rbd::MirrorImageSiteStatusOnDisk
ondisk_status(status
);
5058 ondisk_status
.mirror_uuid
= ""; // mirror_uuid stored in key
5059 ondisk_status
.up
= false;
5060 ondisk_status
.last_update
= ceph_clock_now();
5062 int r
= cls_get_request_origin(hctx
, &ondisk_status
.origin
);
5063 ceph_assert(r
== 0);
5066 encode(ondisk_status
, bl
, cls_get_features(hctx
));
5068 r
= cls_cxx_map_set_val(hctx
, status_global_key(global_image_id
,
5069 status
.mirror_uuid
), &bl
);
5071 CLS_ERR("error setting status for mirrored image, global id '%s', "
5072 "site '%s': %s", global_image_id
.c_str(),
5073 status
.mirror_uuid
.c_str(),
5074 cpp_strerror(r
).c_str());
5080 int get_remote_image_status_mirror_uuids(cls_method_context_t hctx
,
5081 const std::string
& global_image_id
,
5082 std::set
<std::string
>* mirror_uuids
) {
5083 std::string filter
= remote_status_global_key(global_image_id
, "");
5084 std::string last_read
= filter
;
5085 int max_read
= 4; // we don't expect lots of peers
5089 std::set
<std::string
> keys
;
5090 int r
= cls_cxx_map_get_keys(hctx
, last_read
, max_read
, &keys
, &more
);
5095 for (auto& key
: keys
) {
5096 if (!boost::starts_with(key
, filter
)) {
5101 mirror_uuids
->insert(key
.substr(filter
.length()));
5104 if (!keys
.empty()) {
5105 last_read
= *keys
.rbegin();
5112 int image_status_remove(cls_method_context_t hctx
,
5113 const string
&global_image_id
) {
5114 // remove all local/remote image statuses
5115 std::set
<std::string
> mirror_uuids
;
5116 int r
= get_remote_image_status_mirror_uuids(hctx
, global_image_id
,
5118 if (r
< 0 && r
!= -ENOENT
) {
5122 mirror_uuids
.insert(cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID
);
5123 for (auto& mirror_uuid
: mirror_uuids
) {
5124 CLS_LOG(20, "removing status object for mirror_uuid %s",
5125 mirror_uuid
.c_str());
5126 auto key
= status_global_key(global_image_id
, mirror_uuid
);
5127 r
= cls_cxx_map_remove_key(hctx
, key
);
5128 if (r
< 0 && r
!= -ENOENT
) {
5129 CLS_ERR("error removing stale status for key '%s': %s",
5130 key
.c_str(), cpp_strerror(r
).c_str());
5138 int image_status_get(cls_method_context_t hctx
, const string
&global_image_id
,
5139 const std::string
& mirror_uuid
, const bufferlist
& bl
,
5140 const std::set
<entity_inst_t
> &watchers
,
5141 cls::rbd::MirrorImageStatus
* status
) {
5142 cls::rbd::MirrorImageSiteStatusOnDisk ondisk_status
;
5144 auto it
= bl
.cbegin();
5145 decode(ondisk_status
, it
);
5146 } catch (const ceph::buffer::error
&err
) {
5147 CLS_ERR("could not decode status for mirrored image, global id '%s', "
5149 global_image_id
.c_str(), mirror_uuid
.c_str());
5153 auto site_status
= static_cast<cls::rbd::MirrorImageSiteStatus
>(
5155 site_status
.up
= (watchers
.find(ondisk_status
.origin
) != watchers
.end());
5156 site_status
.mirror_uuid
= mirror_uuid
;
5157 status
->mirror_image_site_statuses
.push_back(site_status
);
5161 int image_status_get_local(cls_method_context_t hctx
,
5162 const string
&global_image_id
,
5163 const std::set
<entity_inst_t
> &watchers
,
5164 cls::rbd::MirrorImageStatus
*status
) {
5166 int r
= cls_cxx_map_get_val(
5167 hctx
, status_global_key(global_image_id
,
5168 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID
),
5173 CLS_ERR("error reading status for mirrored image, global id '%s', "
5175 global_image_id
.c_str(),
5176 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID
.c_str(),
5177 cpp_strerror(r
).c_str());
5181 return image_status_get(hctx
, global_image_id
,
5182 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID
,
5183 bl
, watchers
, status
);
5186 int image_status_get_remote(cls_method_context_t hctx
,
5187 const string
&global_image_id
,
5188 const std::set
<entity_inst_t
> &watchers
,
5189 cls::rbd::MirrorImageStatus
*status
) {
5190 std::string filter
= remote_status_global_key(global_image_id
, "");
5191 std::string last_read
= filter
;
5192 int max_read
= RBD_MAX_KEYS_READ
;
5196 std::map
<std::string
, bufferlist
> vals
;
5197 CLS_LOG(20, "last_read = '%s'", last_read
.c_str());
5198 int r
= cls_cxx_map_get_vals(hctx
, last_read
, filter
, max_read
, &vals
,
5206 for (auto& it
: vals
) {
5207 auto mirror_uuid
= it
.first
.substr(filter
.length());
5208 CLS_LOG(20, "mirror_uuid = '%s'", mirror_uuid
.c_str());
5209 r
= image_status_get(hctx
, global_image_id
, mirror_uuid
, it
.second
,
5216 if (!vals
.empty()) {
5217 last_read
= vals
.rbegin()->first
;
5224 int image_status_get(cls_method_context_t hctx
, const string
&global_image_id
,
5225 const std::set
<entity_inst_t
> &watchers
,
5226 cls::rbd::MirrorImageStatus
*status
) {
5227 status
->mirror_image_site_statuses
.clear();
5229 // collect local site status
5230 int r
= image_status_get_local(hctx
, global_image_id
, watchers
, status
);
5235 // collect remote site status (TX to peer)
5236 r
= image_status_get_remote(hctx
, global_image_id
, watchers
, status
);
5241 if (status
->mirror_image_site_statuses
.empty()) {
5248 int image_status_list(cls_method_context_t hctx
,
5249 const std::string
&start_after
, uint64_t max_return
,
5250 map
<std::string
, cls::rbd::MirrorImage
> *mirror_images
,
5251 map
<std::string
, cls::rbd::MirrorImageStatus
> *mirror_statuses
) {
5252 std::string last_read
= image_key(start_after
);
5253 int max_read
= RBD_MAX_KEYS_READ
;
5256 std::set
<entity_inst_t
> watchers
;
5257 int r
= list_watchers(hctx
, &watchers
);
5262 while (more
&& mirror_images
->size() < max_return
) {
5263 std::map
<std::string
, bufferlist
> vals
;
5264 CLS_LOG(20, "last_read = '%s'", last_read
.c_str());
5265 r
= cls_cxx_map_get_vals(hctx
, last_read
, IMAGE_KEY_PREFIX
, max_read
, &vals
,
5269 CLS_ERR("error reading mirror image directory by name: %s",
5270 cpp_strerror(r
).c_str());
5275 for (auto it
= vals
.begin(); it
!= vals
.end() &&
5276 mirror_images
->size() < max_return
; ++it
) {
5277 const std::string
&image_id
= it
->first
.substr(IMAGE_KEY_PREFIX
.size());
5278 cls::rbd::MirrorImage mirror_image
;
5279 auto iter
= it
->second
.cbegin();
5281 decode(mirror_image
, iter
);
5282 } catch (const ceph::buffer::error
&err
) {
5283 CLS_ERR("could not decode mirror image payload of image '%s'",
5288 (*mirror_images
)[image_id
] = mirror_image
;
5290 cls::rbd::MirrorImageStatus status
;
5291 int r1
= image_status_get(hctx
, mirror_image
.global_image_id
, watchers
,
5297 (*mirror_statuses
)[image_id
] = status
;
5299 if (!vals
.empty()) {
5300 last_read
= image_key(mirror_images
->rbegin()->first
);
5307 cls::rbd::MirrorImageStatusState
compute_image_status_summary_state(
5308 cls::rbd::MirrorPeerDirection mirror_peer_direction
,
5309 const std::set
<std::string
>& tx_peer_mirror_uuids
,
5310 const cls::rbd::MirrorImageStatus
& status
) {
5311 std::optional
<cls::rbd::MirrorImageStatusState
> state
= {};
5313 cls::rbd::MirrorImageSiteStatus local_status
;
5314 status
.get_local_mirror_image_site_status(&local_status
);
5316 uint64_t unmatched_tx_peers
= 0;
5317 switch (mirror_peer_direction
) {
5318 case cls::rbd::MIRROR_PEER_DIRECTION_RX
:
5319 // if we are RX-only, summary is based on our local status
5320 if (local_status
.up
) {
5321 state
= local_status
.state
;
5324 case cls::rbd::MIRROR_PEER_DIRECTION_RX_TX
:
5325 // if we are RX/TX, combine all statuses
5326 if (local_status
.up
) {
5327 state
= local_status
.state
;
5330 case cls::rbd::MIRROR_PEER_DIRECTION_TX
:
5331 // if we are TX-only, summary is based on remote status
5332 unmatched_tx_peers
= tx_peer_mirror_uuids
.size();
5333 for (auto& remote_status
: status
.mirror_image_site_statuses
) {
5334 if (remote_status
.mirror_uuid
==
5335 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID
) {
5339 if (unmatched_tx_peers
> 0 &&
5340 tx_peer_mirror_uuids
.count(remote_status
.mirror_uuid
) > 0) {
5341 --unmatched_tx_peers
;
5344 auto remote_state
= (remote_status
.up
?
5345 remote_status
.state
: cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN
);
5346 if (remote_status
.state
== cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR
) {
5347 state
= remote_status
.state
;
5348 } else if (!state
) {
5349 state
= remote_state
;
5350 } else if (*state
!= cls::rbd::MIRROR_IMAGE_STATUS_STATE_ERROR
) {
5351 state
= std::min(*state
, remote_state
);
5359 if (!state
|| unmatched_tx_peers
> 0) {
5360 state
= cls::rbd::MIRROR_IMAGE_STATUS_STATE_UNKNOWN
;
5365 int image_status_get_summary(
5366 cls_method_context_t hctx
,
5367 cls::rbd::MirrorPeerDirection mirror_peer_direction
,
5368 const std::set
<std::string
>& tx_peer_mirror_uuids
,
5369 std::map
<cls::rbd::MirrorImageStatusState
, int32_t> *states
) {
5370 std::set
<entity_inst_t
> watchers
;
5371 int r
= list_watchers(hctx
, &watchers
);
5378 string last_read
= IMAGE_KEY_PREFIX
;
5379 int max_read
= RBD_MAX_KEYS_READ
;
5382 map
<string
, bufferlist
> vals
;
5383 r
= cls_cxx_map_get_vals(hctx
, last_read
, IMAGE_KEY_PREFIX
,
5384 max_read
, &vals
, &more
);
5387 CLS_ERR("error reading mirrored images: %s", cpp_strerror(r
).c_str());
5392 for (auto &list_it
: vals
) {
5393 const string
&key
= list_it
.first
;
5395 if (0 != key
.compare(0, IMAGE_KEY_PREFIX
.size(), IMAGE_KEY_PREFIX
)) {
5399 cls::rbd::MirrorImage mirror_image
;
5400 auto iter
= list_it
.second
.cbegin();
5402 decode(mirror_image
, iter
);
5403 } catch (const ceph::buffer::error
&err
) {
5404 CLS_ERR("could not decode mirror image payload for key '%s'",
5409 cls::rbd::MirrorImageStatus status
;
5410 r
= image_status_get(hctx
, mirror_image
.global_image_id
, watchers
,
5412 if (r
< 0 && r
!= -ENOENT
) {
5416 auto state
= compute_image_status_summary_state(
5417 mirror_peer_direction
, tx_peer_mirror_uuids
, status
);
5421 if (!vals
.empty()) {
5422 last_read
= vals
.rbegin()->first
;
5429 int image_status_remove_down(cls_method_context_t hctx
) {
5430 std::set
<entity_inst_t
> watchers
;
5431 int r
= list_watchers(hctx
, &watchers
);
5436 std::vector
<std::string
> prefixes
= {
5437 STATUS_GLOBAL_KEY_PREFIX
, REMOTE_STATUS_GLOBAL_KEY_PREFIX
};
5438 for (auto& prefix
: prefixes
) {
5439 std::string last_read
= prefix
;
5440 int max_read
= RBD_MAX_KEYS_READ
;
5443 std::map
<std::string
, bufferlist
> vals
;
5444 r
= cls_cxx_map_get_vals(hctx
, last_read
, prefix
, max_read
, &vals
, &more
);
5447 CLS_ERR("error reading mirrored images: %s", cpp_strerror(r
).c_str());
5452 for (auto &list_it
: vals
) {
5453 const std::string
&key
= list_it
.first
;
5455 if (0 != key
.compare(0, prefix
.size(), prefix
)) {
5459 cls::rbd::MirrorImageSiteStatusOnDisk status
;
5461 auto it
= list_it
.second
.cbegin();
5462 status
.decode_meta(it
);
5463 } catch (const ceph::buffer::error
&err
) {
5464 CLS_ERR("could not decode status metadata for mirrored image '%s'",
5469 if (watchers
.find(status
.origin
) == watchers
.end()) {
5470 CLS_LOG(20, "removing stale status object for key %s",
5472 int r1
= cls_cxx_map_remove_key(hctx
, key
);
5474 CLS_ERR("error removing stale status for key '%s': %s",
5475 key
.c_str(), cpp_strerror(r1
).c_str());
5481 if (!vals
.empty()) {
5482 last_read
= vals
.rbegin()->first
;
5490 int image_instance_get(cls_method_context_t hctx
,
5491 const string
&global_image_id
,
5492 const std::set
<entity_inst_t
> &watchers
,
5493 entity_inst_t
*instance
) {
5494 // instance details only available for local site
5496 int r
= cls_cxx_map_get_val(
5497 hctx
, status_global_key(global_image_id
,
5498 cls::rbd::MirrorImageSiteStatus::LOCAL_MIRROR_UUID
),
5502 CLS_ERR("error reading status for mirrored image, global id '%s': '%s'",
5503 global_image_id
.c_str(), cpp_strerror(r
).c_str());
5508 cls::rbd::MirrorImageSiteStatusOnDisk ondisk_status
;
5510 auto it
= bl
.cbegin();
5511 decode(ondisk_status
, it
);
5512 } catch (const ceph::buffer::error
&err
) {
5513 CLS_ERR("could not decode status for mirrored image, global id '%s'",
5514 global_image_id
.c_str());
5518 if (watchers
.find(ondisk_status
.origin
) == watchers
.end()) {
5522 *instance
= ondisk_status
.origin
;
5526 int image_instance_list(cls_method_context_t hctx
,
5527 const std::string
&start_after
,
5528 uint64_t max_return
,
5529 map
<std::string
, entity_inst_t
> *instances
) {
5530 std::string last_read
= image_key(start_after
);
5531 int max_read
= RBD_MAX_KEYS_READ
;
5534 std::set
<entity_inst_t
> watchers
;
5535 int r
= list_watchers(hctx
, &watchers
);
5540 while (more
&& instances
->size() < max_return
) {
5541 std::map
<std::string
, bufferlist
> vals
;
5542 CLS_LOG(20, "last_read = '%s'", last_read
.c_str());
5543 r
= cls_cxx_map_get_vals(hctx
, last_read
, IMAGE_KEY_PREFIX
, max_read
, &vals
,
5547 CLS_ERR("error reading mirror image directory by name: %s",
5548 cpp_strerror(r
).c_str());
5553 for (auto it
= vals
.begin(); it
!= vals
.end() &&
5554 instances
->size() < max_return
; ++it
) {
5555 const std::string
&image_id
= it
->first
.substr(IMAGE_KEY_PREFIX
.size());
5556 cls::rbd::MirrorImage mirror_image
;
5557 auto iter
= it
->second
.cbegin();
5559 decode(mirror_image
, iter
);
5560 } catch (const ceph::buffer::error
&err
) {
5561 CLS_ERR("could not decode mirror image payload of image '%s'",
5566 entity_inst_t instance
;
5567 r
= image_instance_get(hctx
, mirror_image
.global_image_id
, watchers
,
5573 (*instances
)[image_id
] = instance
;
5575 if (!vals
.empty()) {
5576 last_read
= vals
.rbegin()->first
;
5583 int instances_list(cls_method_context_t hctx
,
5584 std::vector
<std::string
> *instance_ids
) {
5585 std::string last_read
= INSTANCE_KEY_PREFIX
;
5586 int max_read
= RBD_MAX_KEYS_READ
;
5589 std::map
<std::string
, bufferlist
> vals
;
5590 int r
= cls_cxx_map_get_vals(hctx
, last_read
, INSTANCE_KEY_PREFIX
.c_str(),
5591 max_read
, &vals
, &more
);
5594 CLS_ERR("error reading mirror instances: %s", cpp_strerror(r
).c_str());
5599 for (auto &it
: vals
) {
5600 instance_ids
->push_back(it
.first
.substr(INSTANCE_KEY_PREFIX
.size()));
5603 if (!vals
.empty()) {
5604 last_read
= vals
.rbegin()->first
;
5610 int instances_add(cls_method_context_t hctx
, const string
&instance_id
) {
5613 int r
= cls_cxx_map_set_val(hctx
, instance_key(instance_id
), &bl
);
5615 CLS_ERR("error setting mirror instance %s: %s", instance_id
.c_str(),
5616 cpp_strerror(r
).c_str());
5622 int instances_remove(cls_method_context_t hctx
, const string
&instance_id
) {
5624 int r
= cls_cxx_map_remove_key(hctx
, instance_key(instance_id
));
5626 CLS_ERR("error removing mirror instance %s: %s", instance_id
.c_str(),
5627 cpp_strerror(r
).c_str());
5633 int mirror_image_map_list(cls_method_context_t hctx
,
5634 const std::string
&start_after
,
5635 uint64_t max_return
,
5636 std::map
<std::string
, cls::rbd::MirrorImageMap
> *image_mapping
) {
5638 std::string last_read
= mirror_image_map_key(start_after
);
5640 while (more
&& image_mapping
->size() < max_return
) {
5641 std::map
<std::string
, bufferlist
> vals
;
5642 CLS_LOG(20, "last read: '%s'", last_read
.c_str());
5644 int max_read
= std::min
<uint64_t>(RBD_MAX_KEYS_READ
, max_return
- image_mapping
->size());
5645 int r
= cls_cxx_map_get_vals(hctx
, last_read
, MIRROR_IMAGE_MAP_KEY_PREFIX
,
5646 max_read
, &vals
, &more
);
5648 CLS_ERR("error reading image map: %s", cpp_strerror(r
).c_str());
5656 for (auto it
= vals
.begin(); it
!= vals
.end(); ++it
) {
5657 const std::string
&global_image_id
=
5658 it
->first
.substr(MIRROR_IMAGE_MAP_KEY_PREFIX
.size());
5660 cls::rbd::MirrorImageMap mirror_image_map
;
5661 auto iter
= it
->second
.cbegin();
5663 decode(mirror_image_map
, iter
);
5664 } catch (const ceph::buffer::error
&err
) {
5665 CLS_ERR("could not decode image map payload: %s",
5666 cpp_strerror(r
).c_str());
5670 image_mapping
->insert(std::make_pair(global_image_id
, mirror_image_map
));
5673 if (!vals
.empty()) {
5674 last_read
= vals
.rbegin()->first
;
5681 int image_snapshot_unlink_peer(cls_method_context_t hctx
,
5683 std::string mirror_peer_uuid
) {
5685 std::string snap_key
;
5686 key_from_snap_id(snap_id
, &snap_key
);
5687 int r
= read_key(hctx
, snap_key
, &snap
);
5690 CLS_ERR("Could not read snapshot meta off disk: %s",
5691 cpp_strerror(r
).c_str());
5696 auto mirror_ns
= boost::get
<cls::rbd::MirrorSnapshotNamespace
>(
5697 &snap
.snapshot_namespace
);
5698 if (mirror_ns
== nullptr) {
5699 CLS_LOG(5, "mirror_image_snapshot_unlink_peer " \
5700 "not mirroring snapshot snap_id=%" PRIu64
, snap_id
);
5704 if (mirror_ns
->mirror_peer_uuids
.count(mirror_peer_uuid
) == 0) {
5708 if (mirror_ns
->mirror_peer_uuids
.size() == 1) {
5709 // if this is the last peer to unlink and we have at least one additional
5710 // newer mirror snapshot, return a special error to inform the caller it
5711 // should remove the snapshot instead.
5712 auto search_lambda
= [snap_id
](const cls_rbd_snap
& snap_meta
) {
5713 if (snap_meta
.id
> snap_id
&&
5714 boost::get
<cls::rbd::MirrorSnapshotNamespace
>(
5715 &snap_meta
.snapshot_namespace
) != nullptr) {
5720 r
= image::snapshot::iterate(hctx
, search_lambda
);
5726 mirror_ns
->mirror_peer_uuids
.erase(mirror_peer_uuid
);
5728 r
= image::snapshot::write(hctx
, snap_key
, std::move(snap
));
5736 int image_snapshot_set_copy_progress(cls_method_context_t hctx
,
5737 uint64_t snap_id
, bool complete
,
5738 uint64_t last_copied_object_number
) {
5740 std::string snap_key
;
5741 key_from_snap_id(snap_id
, &snap_key
);
5742 int r
= read_key(hctx
, snap_key
, &snap
);
5745 CLS_ERR("Could not read snapshot meta off disk: %s",
5746 cpp_strerror(r
).c_str());
5751 auto mirror_ns
= boost::get
<cls::rbd::MirrorSnapshotNamespace
>(
5752 &snap
.snapshot_namespace
);
5753 if (mirror_ns
== nullptr) {
5754 CLS_LOG(5, "mirror_image_snapshot_set_copy_progress " \
5755 "not mirroring snapshot snap_id=%" PRIu64
, snap_id
);
5759 mirror_ns
->complete
= complete
;
5760 if (mirror_ns
->state
== cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY
||
5761 mirror_ns
->state
== cls::rbd::MIRROR_SNAPSHOT_STATE_NON_PRIMARY_DEMOTED
) {
5762 mirror_ns
->last_copied_object_number
= last_copied_object_number
;
5765 r
= image::snapshot::write(hctx
, snap_key
, std::move(snap
));
5773 } // namespace mirror
5780 * @param uuid (std::string)
5781 * @returns 0 on success, negative error code on failure
5783 int mirror_uuid_get(cls_method_context_t hctx
, bufferlist
*in
,
5785 std::string mirror_uuid
;
5786 int r
= mirror::uuid_get(hctx
, &mirror_uuid
);
5791 encode(mirror_uuid
, *out
);
5797 * @param mirror_uuid (std::string)
5800 * @returns 0 on success, negative error code on failure
5802 int mirror_uuid_set(cls_method_context_t hctx
, bufferlist
*in
,
5804 std::string mirror_uuid
;
5806 auto bl_it
= in
->cbegin();
5807 decode(mirror_uuid
, bl_it
);
5808 } catch (const ceph::buffer::error
&err
) {
5812 if (mirror_uuid
.empty()) {
5813 CLS_ERR("cannot set empty mirror uuid");
5817 uint32_t mirror_mode
;
5818 int r
= read_key(hctx
, mirror::MODE
, &mirror_mode
);
5819 if (r
< 0 && r
!= -ENOENT
) {
5821 } else if (r
== 0 && mirror_mode
!= cls::rbd::MIRROR_MODE_DISABLED
) {
5822 CLS_ERR("cannot set mirror uuid while mirroring enabled");
5826 bufferlist mirror_uuid_bl
;
5827 mirror_uuid_bl
.append(mirror_uuid
);
5828 r
= cls_cxx_map_set_val(hctx
, mirror::UUID
, &mirror_uuid_bl
);
5830 CLS_ERR("failed to set mirror uuid");
5841 * @param cls::rbd::MirrorMode (uint32_t)
5842 * @returns 0 on success, negative error code on failure
5844 int mirror_mode_get(cls_method_context_t hctx
, bufferlist
*in
,
5846 uint32_t mirror_mode_decode
;
5847 int r
= read_key(hctx
, mirror::MODE
, &mirror_mode_decode
);
5852 encode(mirror_mode_decode
, *out
);
5858 * @param mirror_mode (cls::rbd::MirrorMode) (uint32_t)
5861 * @returns 0 on success, negative error code on failure
5863 int mirror_mode_set(cls_method_context_t hctx
, bufferlist
*in
,
5865 uint32_t mirror_mode_decode
;
5867 auto bl_it
= in
->cbegin();
5868 decode(mirror_mode_decode
, bl_it
);
5869 } catch (const ceph::buffer::error
&err
) {
5874 switch (static_cast<cls::rbd::MirrorMode
>(mirror_mode_decode
)) {
5875 case cls::rbd::MIRROR_MODE_DISABLED
:
5878 case cls::rbd::MIRROR_MODE_IMAGE
:
5879 case cls::rbd::MIRROR_MODE_POOL
:
5883 CLS_ERR("invalid mirror mode: %d", mirror_mode_decode
);
5889 std::string mirror_uuid
;
5890 r
= mirror::uuid_get(hctx
, &mirror_uuid
);
5898 encode(mirror_mode_decode
, bl
);
5900 r
= cls_cxx_map_set_val(hctx
, mirror::MODE
, &bl
);
5902 CLS_ERR("error enabling mirroring: %s", cpp_strerror(r
).c_str());
5906 std::vector
<cls::rbd::MirrorPeer
> peers
;
5907 r
= mirror::read_peers(hctx
, &peers
);
5908 if (r
< 0 && r
!= -ENOENT
) {
5912 if (!peers
.empty()) {
5913 CLS_ERR("mirroring peers still registered");
5917 r
= remove_key(hctx
, mirror::MODE
);
5922 r
= remove_key(hctx
, mirror::UUID
);
5932 * @param unique peer site name (std::string)
5933 * @param mirror_uuid (std::string)
5934 * @param direction (MirrorPeerDirection) -- future use
5937 * @returns 0 on success, negative error code on failure
5939 int mirror_peer_ping(cls_method_context_t hctx
, bufferlist
*in
,
5941 std::string site_name
;
5942 std::string mirror_uuid
;
5943 cls::rbd::MirrorPeerDirection mirror_peer_direction
;
5945 auto it
= in
->cbegin();
5946 decode(site_name
, it
);
5947 decode(mirror_uuid
, it
);
5950 decode(direction
, it
);
5951 mirror_peer_direction
= static_cast<cls::rbd::MirrorPeerDirection
>(
5953 } catch (const ceph::buffer::error
&err
) {
5957 if (mirror_peer_direction
!= cls::rbd::MIRROR_PEER_DIRECTION_TX
) {
5961 int r
= mirror::peer_ping(hctx
, site_name
, mirror_uuid
);
5974 * @param std::vector<cls::rbd::MirrorPeer>: collection of peers
5975 * @returns 0 on success, negative error code on failure
5977 int mirror_peer_list(cls_method_context_t hctx
, bufferlist
*in
,
5979 std::vector
<cls::rbd::MirrorPeer
> peers
;
5980 int r
= mirror::read_peers(hctx
, &peers
);
5981 if (r
< 0 && r
!= -ENOENT
) {
5985 encode(peers
, *out
);
5991 * @param mirror_peer (cls::rbd::MirrorPeer)
5994 * @returns 0 on success, negative error code on failure
5996 int mirror_peer_add(cls_method_context_t hctx
, bufferlist
*in
,
5998 cls::rbd::MirrorPeer mirror_peer
;
6000 auto it
= in
->cbegin();
6001 decode(mirror_peer
, it
);
6002 } catch (const ceph::buffer::error
&err
) {
6006 int r
= mirror::peer_add(hctx
, mirror_peer
);
6016 * @param uuid (std::string)
6019 * @returns 0 on success, negative error code on failure
6021 int mirror_peer_remove(cls_method_context_t hctx
, bufferlist
*in
,
6025 auto it
= in
->cbegin();
6027 } catch (const ceph::buffer::error
&err
) {
6031 int r
= mirror::peer_remove(hctx
, uuid
);
6040 * @param uuid (std::string)
6041 * @param client_name (std::string)
6044 * @returns 0 on success, negative error code on failure
6046 int mirror_peer_set_client(cls_method_context_t hctx
, bufferlist
*in
,
6049 std::string client_name
;
6051 auto it
= in
->cbegin();
6053 decode(client_name
, it
);
6054 } catch (const ceph::buffer::error
&err
) {
6058 cls::rbd::MirrorPeer peer
;
6059 int r
= mirror::read_peer(hctx
, uuid
, &peer
);
6064 peer
.client_name
= client_name
;
6065 r
= mirror::write_peer(hctx
, peer
);
6074 * @param uuid (std::string)
6075 * @param site_name (std::string)
6078 * @returns 0 on success, negative error code on failure
6080 int mirror_peer_set_cluster(cls_method_context_t hctx
, bufferlist
*in
,
6083 std::string site_name
;
6085 auto it
= in
->cbegin();
6087 decode(site_name
, it
);
6088 } catch (const ceph::buffer::error
&err
) {
6092 cls::rbd::MirrorPeer
* peer
= nullptr;
6093 std::vector
<cls::rbd::MirrorPeer
> peers
;
6094 int r
= mirror::read_peers(hctx
, &peers
);
6095 if (r
< 0 && r
!= -ENOENT
) {
6099 for (auto& p
: peers
) {
6100 if (p
.uuid
== uuid
) {
6102 } else if (p
.site_name
== site_name
) {
6107 if (peer
== nullptr) {
6111 peer
->site_name
= site_name
;
6112 r
= mirror::write_peer(hctx
, *peer
);
6121 * @param uuid (std::string)
6122 * @param direction (uint8_t)
6125 * @returns 0 on success, negative error code on failure
6127 int mirror_peer_set_direction(cls_method_context_t hctx
, bufferlist
*in
,
6130 cls::rbd::MirrorPeerDirection mirror_peer_direction
;
6132 auto it
= in
->cbegin();
6135 decode(direction
, it
);
6136 mirror_peer_direction
= static_cast<cls::rbd::MirrorPeerDirection
>(
6138 } catch (const ceph::buffer::error
&err
) {
6142 cls::rbd::MirrorPeer peer
;
6143 int r
= mirror::read_peer(hctx
, uuid
, &peer
);
6148 peer
.mirror_peer_direction
= mirror_peer_direction
;
6149 r
= mirror::write_peer(hctx
, peer
);
6158 * @param start_after which name to begin listing after
6159 * (use the empty string to start at the beginning)
6160 * @param max_return the maximum number of names to list
6163 * @param std::map<std::string, std::string>: local id to global id map
6164 * @returns 0 on success, negative error code on failure
6166 int mirror_image_list(cls_method_context_t hctx
, bufferlist
*in
,
6168 std::string start_after
;
6169 uint64_t max_return
;
6171 auto iter
= in
->cbegin();
6172 decode(start_after
, iter
);
6173 decode(max_return
, iter
);
6174 } catch (const ceph::buffer::error
&err
) {
6178 int max_read
= RBD_MAX_KEYS_READ
;
6180 std::map
<std::string
, std::string
> mirror_images
;
6181 std::string last_read
= mirror::image_key(start_after
);
6183 while (more
&& mirror_images
.size() < max_return
) {
6184 std::map
<std::string
, bufferlist
> vals
;
6185 CLS_LOG(20, "last_read = '%s'", last_read
.c_str());
6186 int r
= cls_cxx_map_get_vals(hctx
, last_read
, mirror::IMAGE_KEY_PREFIX
,
6187 max_read
, &vals
, &more
);
6190 CLS_ERR("error reading mirror image directory by name: %s",
6191 cpp_strerror(r
).c_str());
6196 for (auto it
= vals
.begin(); it
!= vals
.end(); ++it
) {
6197 const std::string
&image_id
=
6198 it
->first
.substr(mirror::IMAGE_KEY_PREFIX
.size());
6199 cls::rbd::MirrorImage mirror_image
;
6200 auto iter
= it
->second
.cbegin();
6202 decode(mirror_image
, iter
);
6203 } catch (const ceph::buffer::error
&err
) {
6204 CLS_ERR("could not decode mirror image payload of image '%s'",
6209 mirror_images
[image_id
] = mirror_image
.global_image_id
;
6210 if (mirror_images
.size() >= max_return
) {
6214 if (!vals
.empty()) {
6215 last_read
= mirror::image_key(mirror_images
.rbegin()->first
);
6219 encode(mirror_images
, *out
);
6225 * @param global_id (std::string)
6228 * @param std::string - image id
6229 * @returns 0 on success, negative error code on failure
6231 int mirror_image_get_image_id(cls_method_context_t hctx
, bufferlist
*in
,
6233 std::string global_id
;
6235 auto it
= in
->cbegin();
6236 decode(global_id
, it
);
6237 } catch (const ceph::buffer::error
&err
) {
6241 std::string image_id
;
6242 int r
= read_key(hctx
, mirror::global_key(global_id
), &image_id
);
6245 CLS_ERR("error retrieving image id for global id '%s': %s",
6246 global_id
.c_str(), cpp_strerror(r
).c_str());
6251 encode(image_id
, *out
);
6257 * @param image_id (std::string)
6260 * @param cls::rbd::MirrorImage - metadata associated with the image_id
6261 * @returns 0 on success, negative error code on failure
6263 int mirror_image_get(cls_method_context_t hctx
, bufferlist
*in
,
6267 auto it
= in
->cbegin();
6268 decode(image_id
, it
);
6269 } catch (const ceph::buffer::error
&err
) {
6273 cls::rbd::MirrorImage mirror_image
;
6274 int r
= mirror::image_get(hctx
, image_id
, &mirror_image
);
6279 encode(mirror_image
, *out
);
6285 * @param image_id (std::string)
6286 * @param mirror_image (cls::rbd::MirrorImage)
6289 * @returns 0 on success, negative error code on failure
6290 * @returns -EEXIST if there's an existing image_id with a different global_image_id
6292 int mirror_image_set(cls_method_context_t hctx
, bufferlist
*in
,
6295 cls::rbd::MirrorImage mirror_image
;
6297 auto it
= in
->cbegin();
6298 decode(image_id
, it
);
6299 decode(mirror_image
, it
);
6300 } catch (const ceph::buffer::error
&err
) {
6304 int r
= mirror::image_set(hctx
, image_id
, mirror_image
);
6313 * @param image_id (std::string)
6316 * @returns 0 on success, negative error code on failure
6318 int mirror_image_remove(cls_method_context_t hctx
, bufferlist
*in
,
6322 auto it
= in
->cbegin();
6323 decode(image_id
, it
);
6324 } catch (const ceph::buffer::error
&err
) {
6328 int r
= mirror::image_remove(hctx
, image_id
);
6337 * @param global_image_id (std::string)
6338 * @param status (cls::rbd::MirrorImageSiteStatus)
6341 * @returns 0 on success, negative error code on failure
6343 int mirror_image_status_set(cls_method_context_t hctx
, bufferlist
*in
,
6345 string global_image_id
;
6346 cls::rbd::MirrorImageSiteStatus status
;
6348 auto it
= in
->cbegin();
6349 decode(global_image_id
, it
);
6351 } catch (const ceph::buffer::error
&err
) {
6355 int r
= mirror::image_status_set(hctx
, global_image_id
, status
);
6364 * @param global_image_id (std::string)
6367 * @returns 0 on success, negative error code on failure
6369 * NOTE: deprecated - remove this method after Octopus is unsupported
6371 int mirror_image_status_remove(cls_method_context_t hctx
, bufferlist
*in
,
6373 string global_image_id
;
6375 auto it
= in
->cbegin();
6376 decode(global_image_id
, it
);
6377 } catch (const ceph::buffer::error
&err
) {
6381 int r
= mirror::image_status_remove(hctx
, global_image_id
);
6390 * @param global_image_id (std::string)
6393 * @param cls::rbd::MirrorImageStatus - metadata associated with the global_image_id
6394 * @returns 0 on success, negative error code on failure
6396 int mirror_image_status_get(cls_method_context_t hctx
, bufferlist
*in
,
6398 string global_image_id
;
6400 auto it
= in
->cbegin();
6401 decode(global_image_id
, it
);
6402 } catch (const ceph::buffer::error
&err
) {
6406 std::set
<entity_inst_t
> watchers
;
6407 int r
= mirror::list_watchers(hctx
, &watchers
);
6412 cls::rbd::MirrorImageStatus status
;
6413 r
= mirror::image_status_get(hctx
, global_image_id
, watchers
, &status
);
6418 encode(status
, *out
);
6424 * @param start_after which name to begin listing after
6425 * (use the empty string to start at the beginning)
6426 * @param max_return the maximum number of names to list
6429 * @param std::map<std::string, cls::rbd::MirrorImage>: image id to image map
6430 * @param std::map<std::string, cls::rbd::MirrorImageStatus>: image it to status map
6431 * @returns 0 on success, negative error code on failure
6433 int mirror_image_status_list(cls_method_context_t hctx
, bufferlist
*in
,
6435 std::string start_after
;
6436 uint64_t max_return
;
6438 auto iter
= in
->cbegin();
6439 decode(start_after
, iter
);
6440 decode(max_return
, iter
);
6441 } catch (const ceph::buffer::error
&err
) {
6445 map
<std::string
, cls::rbd::MirrorImage
> images
;
6446 map
<std::string
, cls::rbd::MirrorImageStatus
> statuses
;
6447 int r
= mirror::image_status_list(hctx
, start_after
, max_return
, &images
,
6453 encode(images
, *out
);
6454 encode(statuses
, *out
);
6460 * @param std::vector<cls::rbd::MirrorPeer> - optional peers (backwards compatibility)
6463 * @param std::map<cls::rbd::MirrorImageStatusState, int32_t>: states counts
6464 * @returns 0 on success, negative error code on failure
6466 int mirror_image_status_get_summary(cls_method_context_t hctx
, bufferlist
*in
,
6468 std::vector
<cls::rbd::MirrorPeer
> peers
;
6470 auto iter
= in
->cbegin();
6472 decode(peers
, iter
);
6474 } catch (const ceph::buffer::error
&err
) {
6478 auto mirror_peer_direction
= cls::rbd::MIRROR_PEER_DIRECTION_RX
;
6479 if (!peers
.empty()) {
6480 mirror_peer_direction
= peers
.begin()->mirror_peer_direction
;
6483 std::set
<std::string
> tx_peer_mirror_uuids
;
6484 for (auto& peer
: peers
) {
6485 if (peer
.mirror_peer_direction
== cls::rbd::MIRROR_PEER_DIRECTION_RX
) {
6489 tx_peer_mirror_uuids
.insert(peer
.mirror_uuid
);
6490 if (mirror_peer_direction
!= cls::rbd::MIRROR_PEER_DIRECTION_RX_TX
&&
6491 mirror_peer_direction
!= peer
.mirror_peer_direction
) {
6492 mirror_peer_direction
= cls::rbd::MIRROR_PEER_DIRECTION_RX_TX
;
6496 std::map
<cls::rbd::MirrorImageStatusState
, int32_t> states
;
6497 int r
= mirror::image_status_get_summary(hctx
, mirror_peer_direction
,
6498 tx_peer_mirror_uuids
, &states
);
6503 encode(states
, *out
);
6512 * @returns 0 on success, negative error code on failure
6514 int mirror_image_status_remove_down(cls_method_context_t hctx
, bufferlist
*in
,
6516 int r
= mirror::image_status_remove_down(hctx
);
6525 * @param global_image_id (std::string)
6528 * @param entity_inst_t - instance
6529 * @returns 0 on success, negative error code on failure
6531 int mirror_image_instance_get(cls_method_context_t hctx
, bufferlist
*in
,
6533 string global_image_id
;
6535 auto it
= in
->cbegin();
6536 decode(global_image_id
, it
);
6537 } catch (const ceph::buffer::error
&err
) {
6541 std::set
<entity_inst_t
> watchers
;
6542 int r
= mirror::list_watchers(hctx
, &watchers
);
6547 entity_inst_t instance
;
6548 r
= mirror::image_instance_get(hctx
, global_image_id
, watchers
, &instance
);
6553 encode(instance
, *out
, cls_get_features(hctx
));
6559 * @param start_after which name to begin listing after
6560 * (use the empty string to start at the beginning)
6561 * @param max_return the maximum number of names to list
6564 * @param std::map<std::string, entity_inst_t>: image id to instance map
6565 * @returns 0 on success, negative error code on failure
6567 int mirror_image_instance_list(cls_method_context_t hctx
, bufferlist
*in
,
6569 std::string start_after
;
6570 uint64_t max_return
;
6572 auto iter
= in
->cbegin();
6573 decode(start_after
, iter
);
6574 decode(max_return
, iter
);
6575 } catch (const ceph::buffer::error
&err
) {
6579 map
<std::string
, entity_inst_t
> instances
;
6580 int r
= mirror::image_instance_list(hctx
, start_after
, max_return
,
6586 encode(instances
, *out
, cls_get_features(hctx
));
6595 * @param std::vector<std::string>: instance ids
6596 * @returns 0 on success, negative error code on failure
6598 int mirror_instances_list(cls_method_context_t hctx
, bufferlist
*in
,
6600 std::vector
<std::string
> instance_ids
;
6602 int r
= mirror::instances_list(hctx
, &instance_ids
);
6607 encode(instance_ids
, *out
);
6613 * @param instance_id (std::string)
6616 * @returns 0 on success, negative error code on failure
6618 int mirror_instances_add(cls_method_context_t hctx
, bufferlist
*in
,
6620 std::string instance_id
;
6622 auto iter
= in
->cbegin();
6623 decode(instance_id
, iter
);
6624 } catch (const ceph::buffer::error
&err
) {
6628 int r
= mirror::instances_add(hctx
, instance_id
);
6637 * @param instance_id (std::string)
6640 * @returns 0 on success, negative error code on failure
6642 int mirror_instances_remove(cls_method_context_t hctx
, bufferlist
*in
,
6644 std::string instance_id
;
6646 auto iter
= in
->cbegin();
6647 decode(instance_id
, iter
);
6648 } catch (const ceph::buffer::error
&err
) {
6652 int r
= mirror::instances_remove(hctx
, instance_id
);
6661 * @param start_after: key to start after
6662 * @param max_return: max return items
6665 * @param std::map<std::string, cls::rbd::MirrorImageMap>: image mapping
6666 * @returns 0 on success, negative error code on failure
6668 int mirror_image_map_list(cls_method_context_t hctx
, bufferlist
*in
,
6670 std::string start_after
;
6671 uint64_t max_return
;
6673 auto it
= in
->cbegin();
6674 decode(start_after
, it
);
6675 decode(max_return
, it
);
6676 } catch (const ceph::buffer::error
&err
) {
6680 std::map
<std::string
, cls::rbd::MirrorImageMap
> image_mapping
;
6681 int r
= mirror::mirror_image_map_list(hctx
, start_after
, max_return
, &image_mapping
);
6686 encode(image_mapping
, *out
);
6692 * @param global_image_id: global image id
6693 * @param image_map: image map
6696 * @returns 0 on success, negative error code on failure
6698 int mirror_image_map_update(cls_method_context_t hctx
, bufferlist
*in
,
6700 std::string global_image_id
;
6701 cls::rbd::MirrorImageMap image_map
;
6704 auto it
= in
->cbegin();
6705 decode(global_image_id
, it
);
6706 decode(image_map
, it
);
6707 } catch (const ceph::buffer::error
&err
) {
6712 encode(image_map
, bl
);
6714 const std::string key
= mirror::mirror_image_map_key(global_image_id
);
6715 int r
= cls_cxx_map_set_val(hctx
, key
, &bl
);
6717 CLS_ERR("error updating image map %s: %s", key
.c_str(),
6718 cpp_strerror(r
).c_str());
6727 * @param global_image_id: global image id
6730 * @returns 0 on success, negative error code on failure
6732 int mirror_image_map_remove(cls_method_context_t hctx
, bufferlist
*in
,
6734 std::string global_image_id
;
6737 auto it
= in
->cbegin();
6738 decode(global_image_id
, it
);
6739 } catch (const ceph::buffer::error
&err
) {
6743 const std::string key
= mirror::mirror_image_map_key(global_image_id
);
6744 int r
= cls_cxx_map_remove_key(hctx
, key
);
6745 if (r
< 0 && r
!= -ENOENT
) {
6746 CLS_ERR("error removing image map %s: %s", key
.c_str(),
6747 cpp_strerror(r
).c_str());
6757 * @param snap_id: snapshot id
6758 * @param mirror_peer_uuid: mirror peer uuid
6761 * @returns 0 on success, negative error code on failure
6763 int mirror_image_snapshot_unlink_peer(cls_method_context_t hctx
, bufferlist
*in
,
6766 std::string mirror_peer_uuid
;
6768 auto iter
= in
->cbegin();
6769 decode(snap_id
, iter
);
6770 decode(mirror_peer_uuid
, iter
);
6771 } catch (const ceph::buffer::error
&err
) {
6776 "mirror_image_snapshot_unlink_peer snap_id=%" PRIu64
" peer_uuid=%s",
6777 snap_id
, mirror_peer_uuid
.c_str());
6779 int r
= mirror::image_snapshot_unlink_peer(hctx
, snap_id
, mirror_peer_uuid
);
6788 * @param snap_id: snapshot id
6789 * @param complete: true if shapshot fully copied/complete
6790 * @param last_copied_object_number: last copied object number
6793 * @returns 0 on success, negative error code on failure
6795 int mirror_image_snapshot_set_copy_progress(cls_method_context_t hctx
,
6800 uint64_t last_copied_object_number
;
6802 auto iter
= in
->cbegin();
6803 decode(snap_id
, iter
);
6804 decode(complete
, iter
);
6805 decode(last_copied_object_number
, iter
);
6806 } catch (const ceph::buffer::error
&err
) {
6810 CLS_LOG(20, "mirror_image_snapshot_set_copy_progress snap_id=%" PRIu64 \
6811 " complete=%d last_copied_object_number=%" PRIu64
, snap_id
, complete
,
6812 last_copied_object_number
);
6814 int r
= mirror::image_snapshot_set_copy_progress(hctx
, snap_id
, complete
,
6815 last_copied_object_number
);
6824 /********************** methods for rbd_group_directory ***********************/
6826 int dir_add(cls_method_context_t hctx
,
6827 const string
&name
, const string
&id
,
6828 bool check_for_unique_id
)
6830 if (!name
.size() || !is_valid_id(id
)) {
6831 CLS_ERR("invalid group name '%s' or id '%s'",
6832 name
.c_str(), id
.c_str());
6836 CLS_LOG(20, "dir_add name=%s id=%s", name
.c_str(), id
.c_str());
6838 string name_key
= dir_key_for_name(name
);
6839 string id_key
= dir_key_for_id(id
);
6841 int r
= read_key(hctx
, name_key
, &tmp
);
6843 CLS_LOG(10, "name already exists");
6846 r
= read_key(hctx
, id_key
, &tmp
);
6847 if (r
!= -ENOENT
&& check_for_unique_id
) {
6848 CLS_LOG(10, "id already exists");
6851 bufferlist id_bl
, name_bl
;
6853 encode(name
, name_bl
);
6854 map
<string
, bufferlist
> omap_vals
;
6855 omap_vals
[name_key
] = id_bl
;
6856 omap_vals
[id_key
] = name_bl
;
6857 return cls_cxx_map_set_vals(hctx
, &omap_vals
);
6860 int dir_remove(cls_method_context_t hctx
,
6861 const string
&name
, const string
&id
)
6863 CLS_LOG(20, "dir_remove name=%s id=%s", name
.c_str(), id
.c_str());
6865 string name_key
= dir_key_for_name(name
);
6866 string id_key
= dir_key_for_id(id
);
6867 string stored_name
, stored_id
;
6869 int r
= read_key(hctx
, name_key
, &stored_id
);
6872 CLS_ERR("error reading name to id mapping: %s", cpp_strerror(r
).c_str());
6875 r
= read_key(hctx
, id_key
, &stored_name
);
6878 CLS_ERR("error reading id to name mapping: %s", cpp_strerror(r
).c_str());
6882 // check if this op raced with a rename
6883 if (stored_name
!= name
|| stored_id
!= id
) {
6884 CLS_ERR("stored name '%s' and id '%s' do not match args '%s' and '%s'",
6885 stored_name
.c_str(), stored_id
.c_str(), name
.c_str(), id
.c_str());
6889 r
= cls_cxx_map_remove_key(hctx
, name_key
);
6891 CLS_ERR("error removing name: %s", cpp_strerror(r
).c_str());
6895 r
= cls_cxx_map_remove_key(hctx
, id_key
);
6897 CLS_ERR("error removing id: %s", cpp_strerror(r
).c_str());
6904 static const string RBD_GROUP_SNAP_KEY_PREFIX
= "snapshot_";
6906 std::string
snap_key(const std::string
&snap_id
) {
6908 oss
<< RBD_GROUP_SNAP_KEY_PREFIX
<< snap_id
;
6912 int snap_list(cls_method_context_t hctx
, cls::rbd::GroupSnapshot start_after
,
6913 uint64_t max_return
,
6914 std::vector
<cls::rbd::GroupSnapshot
> *group_snaps
)
6916 int max_read
= RBD_MAX_KEYS_READ
;
6917 std::map
<string
, bufferlist
> vals
;
6918 string last_read
= snap_key(start_after
.id
);
6920 group_snaps
->clear();
6924 int r
= cls_cxx_map_get_vals(hctx
, last_read
,
6925 RBD_GROUP_SNAP_KEY_PREFIX
,
6926 max_read
, &vals
, &more
);
6930 for (auto it
= vals
.begin(); it
!= vals
.end() && group_snaps
->size() < max_return
; ++it
) {
6932 auto iter
= it
->second
.cbegin();
6933 cls::rbd::GroupSnapshot snap
;
6936 } catch (const ceph::buffer::error
&err
) {
6937 CLS_ERR("error decoding snapshot: %s", it
->first
.c_str());
6940 CLS_LOG(20, "Discovered snapshot %s %s",
6943 group_snaps
->push_back(snap
);
6946 } while (more
&& (group_snaps
->size() < max_return
));
6951 static int check_duplicate_snap_name(cls_method_context_t hctx
,
6952 const std::string
&snap_name
,
6953 const std::string
&snap_id
)
6955 const int max_read
= 1024;
6956 cls::rbd::GroupSnapshot snap_last
;
6957 std::vector
<cls::rbd::GroupSnapshot
> page
;
6960 int r
= snap_list(hctx
, snap_last
, max_read
, &page
);
6964 for (auto& snap
: page
) {
6965 if (snap
.name
== snap_name
&& snap
.id
!= snap_id
) {
6970 if (page
.size() < max_read
) {
6974 snap_last
= *page
.rbegin();
6980 } // namespace group
6983 * List groups from the directory.
6986 * @param start_after (std::string)
6987 * @param max_return (int64_t)
6990 * @param map of groups (name, id)
6991 * @return 0 on success, negative error code on failure
6993 int group_dir_list(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
6996 uint64_t max_return
;
6999 auto iter
= in
->cbegin();
7000 decode(start_after
, iter
);
7001 decode(max_return
, iter
);
7002 } catch (const ceph::buffer::error
&err
) {
7006 int max_read
= RBD_MAX_KEYS_READ
;
7008 map
<string
, string
> groups
;
7009 string last_read
= dir_key_for_name(start_after
);
7011 while (more
&& groups
.size() < max_return
) {
7012 map
<string
, bufferlist
> vals
;
7013 CLS_LOG(20, "last_read = '%s'", last_read
.c_str());
7014 int r
= cls_cxx_map_get_vals(hctx
, last_read
, RBD_DIR_NAME_KEY_PREFIX
,
7015 max_read
, &vals
, &more
);
7018 CLS_ERR("error reading directory by name: %s", cpp_strerror(r
).c_str());
7023 for (auto val
: vals
) {
7025 auto iter
= val
.second
.cbegin();
7028 } catch (const ceph::buffer::error
&err
) {
7029 CLS_ERR("could not decode id of group '%s'", val
.first
.c_str());
7032 CLS_LOG(20, "adding '%s' -> '%s'", dir_name_from_key(val
.first
).c_str(), id
.c_str());
7033 groups
[dir_name_from_key(val
.first
)] = id
;
7034 if (groups
.size() >= max_return
)
7037 if (!vals
.empty()) {
7038 last_read
= dir_key_for_name(groups
.rbegin()->first
);
7042 encode(groups
, *out
);
7048 * Add a group to the directory.
7051 * @param name (std::string)
7052 * @param id (std::string)
7055 * @return 0 on success, negative error code on failure
7057 int group_dir_add(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7059 int r
= cls_cxx_create(hctx
, false);
7062 CLS_ERR("could not create group directory: %s",
7063 cpp_strerror(r
).c_str());
7069 auto iter
= in
->cbegin();
7072 } catch (const ceph::buffer::error
&err
) {
7076 return group::dir_add(hctx
, name
, id
, true);
7080 * Rename a group to the directory.
7083 * @param src original name of the group (std::string)
7084 * @param dest new name of the group (std::string)
7085 * @param id the id of the group (std::string)
7088 * @return 0 on success, negative error code on failure
7090 int group_dir_rename(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7092 string src
, dest
, id
;
7094 auto iter
= in
->cbegin();
7098 } catch (const ceph::buffer::error
&err
) {
7102 int r
= group::dir_remove(hctx
, src
, id
);
7106 return group::dir_add(hctx
, dest
, id
, false);
7110 * Remove a group from the directory.
7113 * @param name (std::string)
7114 * @param id (std::string)
7117 * @return 0 on success, negative error code on failure
7119 int group_dir_remove(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7123 auto iter
= in
->cbegin();
7126 } catch (const ceph::buffer::error
&err
) {
7130 return group::dir_remove(hctx
, name
, id
);
7134 * Set state of an image in the group.
7137 * @param image_status (cls::rbd::GroupImageStatus)
7140 * @return 0 on success, negative error code on failure
7142 int group_image_set(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7144 CLS_LOG(20, "group_image_set");
7146 cls::rbd::GroupImageStatus st
;
7148 auto iter
= in
->cbegin();
7150 } catch (const ceph::buffer::error
&err
) {
7154 string image_key
= st
.spec
.image_key();
7156 bufferlist image_val_bl
;
7157 encode(st
.state
, image_val_bl
);
7158 int r
= cls_cxx_map_set_val(hctx
, image_key
, &image_val_bl
);
7167 * Remove reference to an image from the group.
7170 * @param spec (cls::rbd::GroupImageSpec)
7173 * @return 0 on success, negative error code on failure
7175 int group_image_remove(cls_method_context_t hctx
,
7176 bufferlist
*in
, bufferlist
*out
)
7178 CLS_LOG(20, "group_image_remove");
7179 cls::rbd::GroupImageSpec spec
;
7181 auto iter
= in
->cbegin();
7183 } catch (const ceph::buffer::error
&err
) {
7187 string image_key
= spec
.image_key();
7189 int r
= cls_cxx_map_remove_key(hctx
, image_key
);
7191 CLS_ERR("error removing image from group: %s", cpp_strerror(r
).c_str());
7199 * List images in the group.
7202 * @param start_after which name to begin listing after
7203 * (use the empty string to start at the beginning)
7204 * @param max_return the maximum number of names to list
7207 * @param tuples of descriptions of the images: image_id, pool_id, image reference state.
7208 * @return 0 on success, negative error code on failure
7210 int group_image_list(cls_method_context_t hctx
,
7211 bufferlist
*in
, bufferlist
*out
)
7213 CLS_LOG(20, "group_image_list");
7214 cls::rbd::GroupImageSpec start_after
;
7215 uint64_t max_return
;
7217 auto iter
= in
->cbegin();
7218 decode(start_after
, iter
);
7219 decode(max_return
, iter
);
7220 } catch (const ceph::buffer::error
&err
) {
7224 int max_read
= RBD_MAX_KEYS_READ
;
7225 std::map
<string
, bufferlist
> vals
;
7226 string last_read
= start_after
.image_key();
7227 std::vector
<cls::rbd::GroupImageStatus
> res
;
7230 int r
= cls_cxx_map_get_vals(hctx
, last_read
,
7231 cls::rbd::RBD_GROUP_IMAGE_KEY_PREFIX
,
7232 max_read
, &vals
, &more
);
7236 for (auto it
= vals
.begin(); it
!= vals
.end() && res
.size() < max_return
; ++it
) {
7238 auto iter
= it
->second
.cbegin();
7239 cls::rbd::GroupImageLinkState state
;
7241 decode(state
, iter
);
7242 } catch (const ceph::buffer::error
&err
) {
7243 CLS_ERR("error decoding state for image: %s", it
->first
.c_str());
7246 cls::rbd::GroupImageSpec spec
;
7247 int r
= cls::rbd::GroupImageSpec::from_key(it
->first
, &spec
);
7251 CLS_LOG(20, "Discovered image %s %" PRId64
" %d", spec
.image_id
.c_str(),
7254 res
.push_back(cls::rbd::GroupImageStatus(spec
, state
));
7256 if (res
.size() > 0) {
7257 last_read
= res
.rbegin()->spec
.image_key();
7260 } while (more
&& (res
.size() < max_return
));
7267 * Reference the group this image belongs to.
7270 * @param group_id (std::string)
7271 * @param pool_id (int64_t)
7274 * @return 0 on success, negative error code on failure
7276 int image_group_add(cls_method_context_t hctx
,
7277 bufferlist
*in
, bufferlist
*out
)
7279 CLS_LOG(20, "image_group_add");
7280 cls::rbd::GroupSpec new_group
;
7282 auto iter
= in
->cbegin();
7283 decode(new_group
, iter
);
7284 } catch (const ceph::buffer::error
&err
) {
7288 bufferlist existing_refbl
;
7290 int r
= cls_cxx_map_get_val(hctx
, RBD_GROUP_REF
, &existing_refbl
);
7292 // If we are trying to link this image to the same group then return
7293 // success. If this image already belongs to another group then abort.
7294 cls::rbd::GroupSpec old_group
;
7296 auto iter
= existing_refbl
.cbegin();
7297 decode(old_group
, iter
);
7298 } catch (const ceph::buffer::error
&err
) {
7302 if ((old_group
.group_id
!= new_group
.group_id
) ||
7303 (old_group
.pool_id
!= new_group
.pool_id
)) {
7306 return 0; // In this case the values are already correct
7308 } else if (r
< 0 && r
!= -ENOENT
) {
7309 // No entry means this image is not a member of any group.
7313 r
= image::set_op_features(hctx
, RBD_OPERATION_FEATURE_GROUP
,
7314 RBD_OPERATION_FEATURE_GROUP
);
7320 encode(new_group
, refbl
);
7321 r
= cls_cxx_map_set_val(hctx
, RBD_GROUP_REF
, &refbl
);
7330 * Remove image's pointer to the group.
7333 * @param cg_id (std::string)
7334 * @param pool_id (int64_t)
7337 * @return 0 on success, negative error code on failure
7339 int image_group_remove(cls_method_context_t hctx
,
7343 CLS_LOG(20, "image_group_remove");
7344 cls::rbd::GroupSpec spec
;
7346 auto iter
= in
->cbegin();
7348 } catch (const ceph::buffer::error
&err
) {
7353 int r
= cls_cxx_map_get_val(hctx
, RBD_GROUP_REF
, &refbl
);
7358 cls::rbd::GroupSpec ref_spec
;
7359 auto iter
= refbl
.cbegin();
7361 decode(ref_spec
, iter
);
7362 } catch (const ceph::buffer::error
&err
) {
7366 if (ref_spec
.pool_id
!= spec
.pool_id
|| ref_spec
.group_id
!= spec
.group_id
) {
7370 r
= cls_cxx_map_remove_key(hctx
, RBD_GROUP_REF
);
7375 r
= image::set_op_features(hctx
, 0, RBD_OPERATION_FEATURE_GROUP
);
7384 * Retrieve the id and pool of the group this image belongs to.
7391 * @return 0 on success, negative error code on failure
7393 int image_group_get(cls_method_context_t hctx
,
7394 bufferlist
*in
, bufferlist
*out
)
7396 CLS_LOG(20, "image_group_get");
7398 int r
= cls_cxx_map_get_val(hctx
, RBD_GROUP_REF
, &refbl
);
7399 if (r
< 0 && r
!= -ENOENT
) {
7403 cls::rbd::GroupSpec spec
;
7406 auto iter
= refbl
.cbegin();
7409 } catch (const ceph::buffer::error
&err
) {
7419 * Save initial snapshot record.
7422 * @param GroupSnapshot
7425 * @return 0 on success, negative error code on failure
7427 int group_snap_set(cls_method_context_t hctx
,
7428 bufferlist
*in
, bufferlist
*out
)
7430 CLS_LOG(20, "group_snap_set");
7431 cls::rbd::GroupSnapshot group_snap
;
7433 auto iter
= in
->cbegin();
7434 decode(group_snap
, iter
);
7435 } catch (const ceph::buffer::error
&err
) {
7439 if (group_snap
.name
.empty()) {
7440 CLS_ERR("group snapshot name is empty");
7443 if (group_snap
.id
.empty()) {
7444 CLS_ERR("group snapshot id is empty");
7448 int r
= group::check_duplicate_snap_name(hctx
, group_snap
.name
,
7454 std::string key
= group::snap_key(group_snap
.id
);
7455 if (group_snap
.state
== cls::rbd::GROUP_SNAPSHOT_STATE_INCOMPLETE
) {
7457 r
= cls_cxx_map_get_val(hctx
, key
, &snap_bl
);
7458 if (r
< 0 && r
!= -ENOENT
) {
7460 } else if (r
>= 0) {
7466 encode(group_snap
, obl
);
7467 r
= cls_cxx_map_set_val(hctx
, key
, &obl
);
7472 * Remove snapshot record.
7475 * @param id Snapshot id
7478 * @return 0 on success, negative error code on failure
7480 int group_snap_remove(cls_method_context_t hctx
,
7481 bufferlist
*in
, bufferlist
*out
)
7483 CLS_LOG(20, "group_snap_remove");
7484 std::string snap_id
;
7486 auto iter
= in
->cbegin();
7487 decode(snap_id
, iter
);
7488 } catch (const ceph::buffer::error
&err
) {
7492 std::string snap_key
= group::snap_key(snap_id
);
7494 CLS_LOG(20, "removing snapshot with key %s", snap_key
.c_str());
7495 int r
= cls_cxx_map_remove_key(hctx
, snap_key
);
7500 * Get group's snapshot by id.
7503 * @param snapshot_id the id of the snapshot to look for.
7506 * @param GroupSnapshot the requested snapshot
7507 * @return 0 on success, negative error code on failure
7509 int group_snap_get_by_id(cls_method_context_t hctx
,
7510 bufferlist
*in
, bufferlist
*out
)
7512 CLS_LOG(20, "group_snap_get_by_id");
7514 std::string snap_id
;
7516 auto iter
= in
->cbegin();
7517 decode(snap_id
, iter
);
7518 } catch (const ceph::buffer::error
&err
) {
7524 int r
= cls_cxx_map_get_val(hctx
, group::snap_key(snap_id
), &snapbl
);
7529 cls::rbd::GroupSnapshot group_snap
;
7530 auto iter
= snapbl
.cbegin();
7532 decode(group_snap
, iter
);
7533 } catch (const ceph::buffer::error
&err
) {
7534 CLS_ERR("error decoding snapshot: %s", snap_id
.c_str());
7538 encode(group_snap
, *out
);
7544 * List group's snapshots.
7547 * @param start_after which name to begin listing after
7548 * (use the empty string to start at the beginning)
7549 * @param max_return the maximum number of snapshots to list
7552 * @param list of snapshots
7553 * @return 0 on success, negative error code on failure
7555 int group_snap_list(cls_method_context_t hctx
,
7556 bufferlist
*in
, bufferlist
*out
)
7558 CLS_LOG(20, "group_snap_list");
7560 cls::rbd::GroupSnapshot start_after
;
7561 uint64_t max_return
;
7563 auto iter
= in
->cbegin();
7564 decode(start_after
, iter
);
7565 decode(max_return
, iter
);
7566 } catch (const ceph::buffer::error
&err
) {
7569 std::vector
<cls::rbd::GroupSnapshot
> group_snaps
;
7570 group::snap_list(hctx
, start_after
, max_return
, &group_snaps
);
7572 encode(group_snaps
, *out
);
7579 static const std::string
IMAGE_KEY_PREFIX("id_");
7581 std::string
image_key(const std::string
&image_id
) {
7582 return IMAGE_KEY_PREFIX
+ image_id
;
7585 std::string
image_id_from_key(const std::string
&key
) {
7586 return key
.substr(IMAGE_KEY_PREFIX
.size());
7589 } // namespace trash
7592 * Add an image entry to the rbd trash. Creates the trash object if
7593 * needed, and stores the trash spec information of the deleted image.
7596 * @param id the id of the image
7597 * @param trash_spec the spec info of the deleted image
7600 * @returns -EEXIST if the image id is already in the trash
7601 * @returns 0 on success, negative error code on failure
7603 int trash_add(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7605 int r
= cls_cxx_create(hctx
, false);
7607 CLS_ERR("could not create trash: %s", cpp_strerror(r
).c_str());
7612 cls::rbd::TrashImageSpec trash_spec
;
7614 auto iter
= in
->cbegin();
7616 decode(trash_spec
, iter
);
7617 } catch (const ceph::buffer::error
&err
) {
7621 if (!is_valid_id(id
)) {
7622 CLS_ERR("trash_add: invalid id '%s'", id
.c_str());
7626 CLS_LOG(20, "trash_add id=%s", id
.c_str());
7628 string key
= trash::image_key(id
);
7629 cls::rbd::TrashImageSpec tmp
;
7630 r
= read_key(hctx
, key
, &tmp
);
7631 if (r
< 0 && r
!= -ENOENT
) {
7632 CLS_ERR("could not read key %s entry from trash: %s", key
.c_str(),
7633 cpp_strerror(r
).c_str());
7635 } else if (r
== 0) {
7636 CLS_LOG(10, "id already exists");
7640 map
<string
, bufferlist
> omap_vals
;
7641 encode(trash_spec
, omap_vals
[key
]);
7642 return cls_cxx_map_set_vals(hctx
, &omap_vals
);
7646 * Removes an image entry from the rbd trash object.
7650 * @param id the id of the image
7653 * @returns -ENOENT if the image id does not exist in the trash
7654 * @returns 0 on success, negative error code on failure
7656 int trash_remove(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7660 auto iter
= in
->cbegin();
7662 } catch (const ceph::buffer::error
&err
) {
7666 CLS_LOG(20, "trash_remove id=%s", id
.c_str());
7668 string key
= trash::image_key(id
);
7670 int r
= cls_cxx_map_get_val(hctx
, key
, &tmp
);
7673 CLS_ERR("error reading entry key %s: %s", key
.c_str(), cpp_strerror(r
).c_str());
7678 r
= cls_cxx_map_remove_key(hctx
, key
);
7680 CLS_ERR("error removing entry: %s", cpp_strerror(r
).c_str());
7688 * Returns the list of trash spec entries registered in the rbd_trash
7692 * @param start_after which name to begin listing after
7693 * (use the empty string to start at the beginning)
7694 * @param max_return the maximum number of names to list
7697 * @param data the map between image id and trash spec info
7699 * @returns 0 on success, negative error code on failure
7701 int trash_list(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7704 uint64_t max_return
;
7707 auto iter
= in
->cbegin();
7708 decode(start_after
, iter
);
7709 decode(max_return
, iter
);
7710 } catch (const ceph::buffer::error
&err
) {
7714 map
<string
, cls::rbd::TrashImageSpec
> data
;
7715 string last_read
= trash::image_key(start_after
);
7718 CLS_LOG(20, "trash_get_images");
7719 while (data
.size() < max_return
) {
7720 map
<string
, bufferlist
> raw_data
;
7721 int max_read
= std::min
<int32_t>(RBD_MAX_KEYS_READ
,
7722 max_return
- data
.size());
7723 int r
= cls_cxx_map_get_vals(hctx
, last_read
, trash::IMAGE_KEY_PREFIX
,
7724 max_read
, &raw_data
, &more
);
7727 CLS_ERR("failed to read the vals off of disk: %s",
7728 cpp_strerror(r
).c_str());
7732 if (raw_data
.empty()) {
7736 for (auto it
= raw_data
.begin(); it
!= raw_data
.end(); ++it
) {
7737 decode(data
[trash::image_id_from_key(it
->first
)], it
->second
);
7744 last_read
= raw_data
.rbegin()->first
;
7752 * Returns the trash spec entry of an image registered in the rbd_trash
7756 * @param id the id of the image
7759 * @param out the trash spec entry
7761 * @returns 0 on success, negative error code on failure
7763 int trash_get(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7767 auto iter
= in
->cbegin();
7769 } catch (const ceph::buffer::error
&err
) {
7773 CLS_LOG(20, "trash_get_image id=%s", id
.c_str());
7776 string key
= trash::image_key(id
);
7778 int r
= cls_cxx_map_get_val(hctx
, key
, out
);
7779 if (r
< 0 && r
!= -ENOENT
) {
7780 CLS_ERR("error reading image from trash '%s': '%s'", id
.c_str(),
7781 cpp_strerror(r
).c_str());
7787 * Set state of an image in the rbd_trash object.
7790 * @param id the id of the image
7791 * @param trash_state the state of the image to be set
7792 * @param expect_state the expected state of the image
7795 * @returns 0 on success, negative error code on failure
7797 int trash_state_set(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7800 cls::rbd::TrashImageState trash_state
;
7801 cls::rbd::TrashImageState expect_state
;
7803 auto iter
= in
->cbegin();
7805 decode(trash_state
, iter
);
7806 decode(expect_state
, iter
);
7807 } catch (const ceph::buffer::error
&err
) {
7811 CLS_LOG(20, "trash_state_set id=%s", id
.c_str());
7813 string key
= trash::image_key(id
);
7814 cls::rbd::TrashImageSpec trash_spec
;
7815 int r
= read_key(hctx
, key
, &trash_spec
);
7818 CLS_ERR("Could not read trash image spec off disk: %s",
7819 cpp_strerror(r
).c_str());
7824 if (trash_spec
.state
== expect_state
) {
7825 trash_spec
.state
= trash_state
;
7826 r
= write_key(hctx
, key
, trash_spec
);
7828 CLS_ERR("error setting trash image state: %s", cpp_strerror(r
).c_str());
7833 } else if (trash_spec
.state
== trash_state
) {
7836 CLS_ERR("Current trash state: %d do not match expected: %d or set: %d",
7837 trash_spec
.state
, expect_state
, trash_state
);
7844 const std::string
NAME_KEY_PREFIX("name_");
7846 std::string
key_for_name(const std::string
& name
) {
7847 return NAME_KEY_PREFIX
+ name
;
7850 std::string
name_from_key(const std::string
&key
) {
7851 return key
.substr(NAME_KEY_PREFIX
.size());
7854 } // namespace nspace
7857 * Add a namespace to the namespace directory.
7860 * @param name the name of the namespace
7863 * @returns -EEXIST if the namespace is already exists
7864 * @returns 0 on success, negative error code on failure
7866 int namespace_add(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7870 auto iter
= in
->cbegin();
7872 } catch (const ceph::buffer::error
&err
) {
7876 std::string
key(nspace::key_for_name(name
));
7878 int r
= cls_cxx_map_get_val(hctx
, key
, &value
);
7879 if (r
< 0 && r
!= -ENOENT
) {
7881 } else if (r
== 0) {
7885 r
= cls_cxx_map_set_val(hctx
, key
, &value
);
7887 CLS_ERR("failed to set omap key: %s", key
.c_str());
7895 * Remove a namespace from the namespace directory.
7898 * @param name the name of the namespace
7901 * @returns 0 on success, negative error code on failure
7903 int namespace_remove(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7907 auto iter
= in
->cbegin();
7909 } catch (const ceph::buffer::error
&err
) {
7913 std::string
key(nspace::key_for_name(name
));
7915 int r
= cls_cxx_map_get_val(hctx
, key
, &bl
);
7920 r
= cls_cxx_map_remove_key(hctx
, key
);
7929 * Returns the list of namespaces in the rbd_namespace object
7932 * @param start_after which name to begin listing after
7933 * (use the empty string to start at the beginning)
7934 * @param max_return the maximum number of names to list
7937 * @param data list of namespace names
7938 * @returns 0 on success, negative error code on failure
7940 int namespace_list(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
7943 uint64_t max_return
;
7945 auto iter
= in
->cbegin();
7946 decode(start_after
, iter
);
7947 decode(max_return
, iter
);
7948 } catch (const ceph::buffer::error
&err
) {
7952 std::list
<std::string
> data
;
7953 std::string last_read
= nspace::key_for_name(start_after
);
7956 CLS_LOG(20, "namespace_list");
7957 while (data
.size() < max_return
) {
7958 std::map
<std::string
, bufferlist
> raw_data
;
7959 int max_read
= std::min
<int32_t>(RBD_MAX_KEYS_READ
,
7960 max_return
- data
.size());
7961 int r
= cls_cxx_map_get_vals(hctx
, last_read
, nspace::NAME_KEY_PREFIX
,
7962 max_read
, &raw_data
, &more
);
7965 CLS_ERR("failed to read the vals off of disk: %s",
7966 cpp_strerror(r
).c_str());
7971 for (auto& it
: raw_data
) {
7972 data
.push_back(nspace::name_from_key(it
.first
));
7975 if (raw_data
.empty() || !more
) {
7979 last_read
= raw_data
.rbegin()->first
;
7987 * Reclaim space for zeroed extents
7990 * @param sparse_size minimal zeroed block to sparse
7991 * @param remove_empty boolean, true if the object should be removed if empty
7994 * @returns -ENOENT if the object does not exist or has been removed
7995 * @returns 0 on success, negative error code on failure
7997 int sparsify(cls_method_context_t hctx
, bufferlist
*in
, bufferlist
*out
)
8002 auto iter
= in
->cbegin();
8003 decode(sparse_size
, iter
);
8004 decode(remove_empty
, iter
);
8005 } catch (const ceph::buffer::error
&err
) {
8009 int r
= check_exists(hctx
);
8015 r
= cls_cxx_read(hctx
, 0, 0, &bl
);
8017 CLS_ERR("failed to read data off of disk: %s", cpp_strerror(r
).c_str());
8023 CLS_LOG(20, "remove");
8024 r
= cls_cxx_remove(hctx
);
8026 CLS_ERR("remove failed: %s", cpp_strerror(r
).c_str());
8029 } else if (bl
.length() > 0) {
8030 CLS_LOG(20, "truncate");
8031 bufferlist write_bl
;
8032 r
= cls_cxx_replace(hctx
, 0, 0, &write_bl
);
8034 CLS_ERR("truncate failed: %s", cpp_strerror(r
).c_str());
8038 CLS_LOG(20, "skip empty");
8043 bl
.rebuild(ceph::buffer::ptr_node::create(bl
.length()));
8044 size_t write_offset
= 0;
8045 size_t write_length
= 0;
8047 size_t length
= bl
.length();
8048 const auto& ptr
= bl
.front();
8049 bool replace
= true;
8050 while (offset
< length
) {
8051 if (calc_sparse_extent(ptr
, sparse_size
, length
, &write_offset
,
8052 &write_length
, &offset
)) {
8053 if (write_offset
== 0 && write_length
== length
) {
8054 CLS_LOG(20, "nothing to do");
8057 CLS_LOG(20, "write%s %" PRIu64
"~%" PRIu64
, (replace
? "(replace)" : ""),
8058 write_offset
, write_length
);
8059 bufferlist write_bl
;
8060 write_bl
.push_back(ceph::buffer::ptr_node::create(ptr
, write_offset
,
8063 r
= cls_cxx_replace(hctx
, write_offset
, write_length
, &write_bl
);
8066 r
= cls_cxx_write(hctx
, write_offset
, write_length
, &write_bl
);
8069 CLS_ERR("write failed: %s", cpp_strerror(r
).c_str());
8072 write_offset
= offset
;
8082 CLS_LOG(20, "Loaded rbd class!");
8084 cls_handle_t h_class
;
8085 cls_method_handle_t h_create
;
8086 cls_method_handle_t h_get_features
;
8087 cls_method_handle_t h_set_features
;
8088 cls_method_handle_t h_get_size
;
8089 cls_method_handle_t h_set_size
;
8090 cls_method_handle_t h_get_parent
;
8091 cls_method_handle_t h_set_parent
;
8092 cls_method_handle_t h_remove_parent
;
8093 cls_method_handle_t h_parent_get
;
8094 cls_method_handle_t h_parent_overlap_get
;
8095 cls_method_handle_t h_parent_attach
;
8096 cls_method_handle_t h_parent_detach
;
8097 cls_method_handle_t h_get_protection_status
;
8098 cls_method_handle_t h_set_protection_status
;
8099 cls_method_handle_t h_get_stripe_unit_count
;
8100 cls_method_handle_t h_set_stripe_unit_count
;
8101 cls_method_handle_t h_get_create_timestamp
;
8102 cls_method_handle_t h_get_access_timestamp
;
8103 cls_method_handle_t h_get_modify_timestamp
;
8104 cls_method_handle_t h_get_flags
;
8105 cls_method_handle_t h_set_flags
;
8106 cls_method_handle_t h_op_features_get
;
8107 cls_method_handle_t h_op_features_set
;
8108 cls_method_handle_t h_add_child
;
8109 cls_method_handle_t h_remove_child
;
8110 cls_method_handle_t h_get_children
;
8111 cls_method_handle_t h_get_snapcontext
;
8112 cls_method_handle_t h_get_object_prefix
;
8113 cls_method_handle_t h_get_data_pool
;
8114 cls_method_handle_t h_get_snapshot_name
;
8115 cls_method_handle_t h_get_snapshot_timestamp
;
8116 cls_method_handle_t h_snapshot_get
;
8117 cls_method_handle_t h_snapshot_add
;
8118 cls_method_handle_t h_snapshot_remove
;
8119 cls_method_handle_t h_snapshot_rename
;
8120 cls_method_handle_t h_snapshot_trash_add
;
8121 cls_method_handle_t h_get_all_features
;
8122 cls_method_handle_t h_get_id
;
8123 cls_method_handle_t h_set_id
;
8124 cls_method_handle_t h_set_modify_timestamp
;
8125 cls_method_handle_t h_set_access_timestamp
;
8126 cls_method_handle_t h_dir_get_id
;
8127 cls_method_handle_t h_dir_get_name
;
8128 cls_method_handle_t h_dir_list
;
8129 cls_method_handle_t h_dir_add_image
;
8130 cls_method_handle_t h_dir_remove_image
;
8131 cls_method_handle_t h_dir_rename_image
;
8132 cls_method_handle_t h_dir_state_assert
;
8133 cls_method_handle_t h_dir_state_set
;
8134 cls_method_handle_t h_object_map_load
;
8135 cls_method_handle_t h_object_map_save
;
8136 cls_method_handle_t h_object_map_resize
;
8137 cls_method_handle_t h_object_map_update
;
8138 cls_method_handle_t h_object_map_snap_add
;
8139 cls_method_handle_t h_object_map_snap_remove
;
8140 cls_method_handle_t h_metadata_set
;
8141 cls_method_handle_t h_metadata_remove
;
8142 cls_method_handle_t h_metadata_list
;
8143 cls_method_handle_t h_metadata_get
;
8144 cls_method_handle_t h_snapshot_get_limit
;
8145 cls_method_handle_t h_snapshot_set_limit
;
8146 cls_method_handle_t h_child_attach
;
8147 cls_method_handle_t h_child_detach
;
8148 cls_method_handle_t h_children_list
;
8149 cls_method_handle_t h_migration_set
;
8150 cls_method_handle_t h_migration_set_state
;
8151 cls_method_handle_t h_migration_get
;
8152 cls_method_handle_t h_migration_remove
;
8153 cls_method_handle_t h_old_snapshots_list
;
8154 cls_method_handle_t h_old_snapshot_add
;
8155 cls_method_handle_t h_old_snapshot_remove
;
8156 cls_method_handle_t h_old_snapshot_rename
;
8157 cls_method_handle_t h_mirror_uuid_get
;
8158 cls_method_handle_t h_mirror_uuid_set
;
8159 cls_method_handle_t h_mirror_mode_get
;
8160 cls_method_handle_t h_mirror_mode_set
;
8161 cls_method_handle_t h_mirror_peer_ping
;
8162 cls_method_handle_t h_mirror_peer_list
;
8163 cls_method_handle_t h_mirror_peer_add
;
8164 cls_method_handle_t h_mirror_peer_remove
;
8165 cls_method_handle_t h_mirror_peer_set_client
;
8166 cls_method_handle_t h_mirror_peer_set_cluster
;
8167 cls_method_handle_t h_mirror_peer_set_direction
;
8168 cls_method_handle_t h_mirror_image_list
;
8169 cls_method_handle_t h_mirror_image_get_image_id
;
8170 cls_method_handle_t h_mirror_image_get
;
8171 cls_method_handle_t h_mirror_image_set
;
8172 cls_method_handle_t h_mirror_image_remove
;
8173 cls_method_handle_t h_mirror_image_status_set
;
8174 cls_method_handle_t h_mirror_image_status_remove
;
8175 cls_method_handle_t h_mirror_image_status_get
;
8176 cls_method_handle_t h_mirror_image_status_list
;
8177 cls_method_handle_t h_mirror_image_status_get_summary
;
8178 cls_method_handle_t h_mirror_image_status_remove_down
;
8179 cls_method_handle_t h_mirror_image_instance_get
;
8180 cls_method_handle_t h_mirror_image_instance_list
;
8181 cls_method_handle_t h_mirror_instances_list
;
8182 cls_method_handle_t h_mirror_instances_add
;
8183 cls_method_handle_t h_mirror_instances_remove
;
8184 cls_method_handle_t h_mirror_image_map_list
;
8185 cls_method_handle_t h_mirror_image_map_update
;
8186 cls_method_handle_t h_mirror_image_map_remove
;
8187 cls_method_handle_t h_mirror_image_snapshot_unlink_peer
;
8188 cls_method_handle_t h_mirror_image_snapshot_set_copy_progress
;
8189 cls_method_handle_t h_group_dir_list
;
8190 cls_method_handle_t h_group_dir_add
;
8191 cls_method_handle_t h_group_dir_remove
;
8192 cls_method_handle_t h_group_dir_rename
;
8193 cls_method_handle_t h_group_image_remove
;
8194 cls_method_handle_t h_group_image_list
;
8195 cls_method_handle_t h_group_image_set
;
8196 cls_method_handle_t h_image_group_add
;
8197 cls_method_handle_t h_image_group_remove
;
8198 cls_method_handle_t h_image_group_get
;
8199 cls_method_handle_t h_group_snap_set
;
8200 cls_method_handle_t h_group_snap_remove
;
8201 cls_method_handle_t h_group_snap_get_by_id
;
8202 cls_method_handle_t h_group_snap_list
;
8203 cls_method_handle_t h_trash_add
;
8204 cls_method_handle_t h_trash_remove
;
8205 cls_method_handle_t h_trash_list
;
8206 cls_method_handle_t h_trash_get
;
8207 cls_method_handle_t h_trash_state_set
;
8208 cls_method_handle_t h_namespace_add
;
8209 cls_method_handle_t h_namespace_remove
;
8210 cls_method_handle_t h_namespace_list
;
8211 cls_method_handle_t h_copyup
;
8212 cls_method_handle_t h_sparse_copyup
;
8213 cls_method_handle_t h_assert_snapc_seq
;
8214 cls_method_handle_t h_sparsify
;
8216 cls_register("rbd", &h_class
);
8217 cls_register_cxx_method(h_class
, "create",
8218 CLS_METHOD_RD
| CLS_METHOD_WR
,
8220 cls_register_cxx_method(h_class
, "get_features",
8222 get_features
, &h_get_features
);
8223 cls_register_cxx_method(h_class
, "set_features",
8224 CLS_METHOD_RD
| CLS_METHOD_WR
,
8225 set_features
, &h_set_features
);
8226 cls_register_cxx_method(h_class
, "get_size",
8228 get_size
, &h_get_size
);
8229 cls_register_cxx_method(h_class
, "set_size",
8230 CLS_METHOD_RD
| CLS_METHOD_WR
,
8231 set_size
, &h_set_size
);
8232 cls_register_cxx_method(h_class
, "get_snapcontext",
8234 get_snapcontext
, &h_get_snapcontext
);
8235 cls_register_cxx_method(h_class
, "get_object_prefix",
8237 get_object_prefix
, &h_get_object_prefix
);
8238 cls_register_cxx_method(h_class
, "get_data_pool", CLS_METHOD_RD
,
8239 get_data_pool
, &h_get_data_pool
);
8240 cls_register_cxx_method(h_class
, "get_snapshot_name",
8242 get_snapshot_name
, &h_get_snapshot_name
);
8243 cls_register_cxx_method(h_class
, "get_snapshot_timestamp",
8245 get_snapshot_timestamp
, &h_get_snapshot_timestamp
);
8246 cls_register_cxx_method(h_class
, "snapshot_get",
8248 snapshot_get
, &h_snapshot_get
);
8249 cls_register_cxx_method(h_class
, "snapshot_add",
8250 CLS_METHOD_RD
| CLS_METHOD_WR
,
8251 snapshot_add
, &h_snapshot_add
);
8252 cls_register_cxx_method(h_class
, "snapshot_remove",
8253 CLS_METHOD_RD
| CLS_METHOD_WR
,
8254 snapshot_remove
, &h_snapshot_remove
);
8255 cls_register_cxx_method(h_class
, "snapshot_rename",
8256 CLS_METHOD_RD
| CLS_METHOD_WR
,
8257 snapshot_rename
, &h_snapshot_rename
);
8258 cls_register_cxx_method(h_class
, "snapshot_trash_add",
8259 CLS_METHOD_RD
| CLS_METHOD_WR
,
8260 snapshot_trash_add
, &h_snapshot_trash_add
);
8261 cls_register_cxx_method(h_class
, "get_all_features",
8263 get_all_features
, &h_get_all_features
);
8265 // NOTE: deprecate v1 parent APIs after mimic EOLed
8266 cls_register_cxx_method(h_class
, "get_parent",
8268 get_parent
, &h_get_parent
);
8269 cls_register_cxx_method(h_class
, "set_parent",
8270 CLS_METHOD_RD
| CLS_METHOD_WR
,
8271 set_parent
, &h_set_parent
);
8272 cls_register_cxx_method(h_class
, "remove_parent",
8273 CLS_METHOD_RD
| CLS_METHOD_WR
,
8274 remove_parent
, &h_remove_parent
);
8276 cls_register_cxx_method(h_class
, "parent_get",
8277 CLS_METHOD_RD
, parent_get
, &h_parent_get
);
8278 cls_register_cxx_method(h_class
, "parent_overlap_get",
8279 CLS_METHOD_RD
, parent_overlap_get
,
8280 &h_parent_overlap_get
);
8281 cls_register_cxx_method(h_class
, "parent_attach",
8282 CLS_METHOD_RD
| CLS_METHOD_WR
,
8283 parent_attach
, &h_parent_attach
);
8284 cls_register_cxx_method(h_class
, "parent_detach",
8285 CLS_METHOD_RD
| CLS_METHOD_WR
,
8286 parent_detach
, &h_parent_detach
);
8288 cls_register_cxx_method(h_class
, "set_protection_status",
8289 CLS_METHOD_RD
| CLS_METHOD_WR
,
8290 set_protection_status
, &h_set_protection_status
);
8291 cls_register_cxx_method(h_class
, "get_protection_status",
8293 get_protection_status
, &h_get_protection_status
);
8294 cls_register_cxx_method(h_class
, "get_stripe_unit_count",
8296 get_stripe_unit_count
, &h_get_stripe_unit_count
);
8297 cls_register_cxx_method(h_class
, "set_stripe_unit_count",
8298 CLS_METHOD_RD
| CLS_METHOD_WR
,
8299 set_stripe_unit_count
, &h_set_stripe_unit_count
);
8300 cls_register_cxx_method(h_class
, "get_create_timestamp",
8302 get_create_timestamp
, &h_get_create_timestamp
);
8303 cls_register_cxx_method(h_class
, "get_access_timestamp",
8305 get_access_timestamp
, &h_get_access_timestamp
);
8306 cls_register_cxx_method(h_class
, "get_modify_timestamp",
8308 get_modify_timestamp
, &h_get_modify_timestamp
);
8309 cls_register_cxx_method(h_class
, "get_flags",
8311 get_flags
, &h_get_flags
);
8312 cls_register_cxx_method(h_class
, "set_flags",
8313 CLS_METHOD_RD
| CLS_METHOD_WR
,
8314 set_flags
, &h_set_flags
);
8315 cls_register_cxx_method(h_class
, "op_features_get", CLS_METHOD_RD
,
8316 op_features_get
, &h_op_features_get
);
8317 cls_register_cxx_method(h_class
, "op_features_set",
8318 CLS_METHOD_RD
| CLS_METHOD_WR
,
8319 op_features_set
, &h_op_features_set
);
8320 cls_register_cxx_method(h_class
, "metadata_list",
8322 metadata_list
, &h_metadata_list
);
8323 cls_register_cxx_method(h_class
, "metadata_set",
8324 CLS_METHOD_RD
| CLS_METHOD_WR
,
8325 metadata_set
, &h_metadata_set
);
8326 cls_register_cxx_method(h_class
, "metadata_remove",
8327 CLS_METHOD_RD
| CLS_METHOD_WR
,
8328 metadata_remove
, &h_metadata_remove
);
8329 cls_register_cxx_method(h_class
, "metadata_get",
8331 metadata_get
, &h_metadata_get
);
8332 cls_register_cxx_method(h_class
, "snapshot_get_limit",
8334 snapshot_get_limit
, &h_snapshot_get_limit
);
8335 cls_register_cxx_method(h_class
, "snapshot_set_limit",
8336 CLS_METHOD_RD
| CLS_METHOD_WR
,
8337 snapshot_set_limit
, &h_snapshot_set_limit
);
8338 cls_register_cxx_method(h_class
, "child_attach",
8339 CLS_METHOD_RD
| CLS_METHOD_WR
,
8340 child_attach
, &h_child_attach
);
8341 cls_register_cxx_method(h_class
, "child_detach",
8342 CLS_METHOD_RD
| CLS_METHOD_WR
,
8343 child_detach
, &h_child_detach
);
8344 cls_register_cxx_method(h_class
, "children_list",
8346 children_list
, &h_children_list
);
8347 cls_register_cxx_method(h_class
, "migration_set",
8348 CLS_METHOD_RD
| CLS_METHOD_WR
,
8349 migration_set
, &h_migration_set
);
8350 cls_register_cxx_method(h_class
, "migration_set_state",
8351 CLS_METHOD_RD
| CLS_METHOD_WR
,
8352 migration_set_state
, &h_migration_set_state
);
8353 cls_register_cxx_method(h_class
, "migration_get",
8355 migration_get
, &h_migration_get
);
8356 cls_register_cxx_method(h_class
, "migration_remove",
8357 CLS_METHOD_RD
| CLS_METHOD_WR
,
8358 migration_remove
, &h_migration_remove
);
8360 cls_register_cxx_method(h_class
, "set_modify_timestamp",
8361 CLS_METHOD_RD
| CLS_METHOD_WR
,
8362 set_modify_timestamp
, &h_set_modify_timestamp
);
8364 cls_register_cxx_method(h_class
, "set_access_timestamp",
8365 CLS_METHOD_RD
| CLS_METHOD_WR
,
8366 set_access_timestamp
, &h_set_access_timestamp
);
8368 /* methods for the rbd_children object */
8369 cls_register_cxx_method(h_class
, "add_child",
8370 CLS_METHOD_RD
| CLS_METHOD_WR
,
8371 add_child
, &h_add_child
);
8372 cls_register_cxx_method(h_class
, "remove_child",
8373 CLS_METHOD_RD
| CLS_METHOD_WR
,
8374 remove_child
, &h_remove_child
);
8375 cls_register_cxx_method(h_class
, "get_children",
8377 get_children
, &h_get_children
);
8379 /* methods for the rbd_id.$image_name objects */
8380 cls_register_cxx_method(h_class
, "get_id",
8383 cls_register_cxx_method(h_class
, "set_id",
8384 CLS_METHOD_RD
| CLS_METHOD_WR
,
8387 /* methods for the rbd_directory object */
8388 cls_register_cxx_method(h_class
, "dir_get_id",
8390 dir_get_id
, &h_dir_get_id
);
8391 cls_register_cxx_method(h_class
, "dir_get_name",
8393 dir_get_name
, &h_dir_get_name
);
8394 cls_register_cxx_method(h_class
, "dir_list",
8396 dir_list
, &h_dir_list
);
8397 cls_register_cxx_method(h_class
, "dir_add_image",
8398 CLS_METHOD_RD
| CLS_METHOD_WR
,
8399 dir_add_image
, &h_dir_add_image
);
8400 cls_register_cxx_method(h_class
, "dir_remove_image",
8401 CLS_METHOD_RD
| CLS_METHOD_WR
,
8402 dir_remove_image
, &h_dir_remove_image
);
8403 cls_register_cxx_method(h_class
, "dir_rename_image",
8404 CLS_METHOD_RD
| CLS_METHOD_WR
,
8405 dir_rename_image
, &h_dir_rename_image
);
8406 cls_register_cxx_method(h_class
, "dir_state_assert", CLS_METHOD_RD
,
8407 dir_state_assert
, &h_dir_state_assert
);
8408 cls_register_cxx_method(h_class
, "dir_state_set",
8409 CLS_METHOD_RD
| CLS_METHOD_WR
,
8410 dir_state_set
, &h_dir_state_set
);
8412 /* methods for the rbd_object_map.$image_id object */
8413 cls_register_cxx_method(h_class
, "object_map_load",
8415 object_map_load
, &h_object_map_load
);
8416 cls_register_cxx_method(h_class
, "object_map_save",
8417 CLS_METHOD_RD
| CLS_METHOD_WR
,
8418 object_map_save
, &h_object_map_save
);
8419 cls_register_cxx_method(h_class
, "object_map_resize",
8420 CLS_METHOD_RD
| CLS_METHOD_WR
,
8421 object_map_resize
, &h_object_map_resize
);
8422 cls_register_cxx_method(h_class
, "object_map_update",
8423 CLS_METHOD_RD
| CLS_METHOD_WR
,
8424 object_map_update
, &h_object_map_update
);
8425 cls_register_cxx_method(h_class
, "object_map_snap_add",
8426 CLS_METHOD_RD
| CLS_METHOD_WR
,
8427 object_map_snap_add
, &h_object_map_snap_add
);
8428 cls_register_cxx_method(h_class
, "object_map_snap_remove",
8429 CLS_METHOD_RD
| CLS_METHOD_WR
,
8430 object_map_snap_remove
, &h_object_map_snap_remove
);
8432 /* methods for the old format */
8433 cls_register_cxx_method(h_class
, "snap_list",
8435 old_snapshots_list
, &h_old_snapshots_list
);
8436 cls_register_cxx_method(h_class
, "snap_add",
8437 CLS_METHOD_RD
| CLS_METHOD_WR
,
8438 old_snapshot_add
, &h_old_snapshot_add
);
8439 cls_register_cxx_method(h_class
, "snap_remove",
8440 CLS_METHOD_RD
| CLS_METHOD_WR
,
8441 old_snapshot_remove
, &h_old_snapshot_remove
);
8442 cls_register_cxx_method(h_class
, "snap_rename",
8443 CLS_METHOD_RD
| CLS_METHOD_WR
,
8444 old_snapshot_rename
, &h_old_snapshot_rename
);
8446 /* methods for the rbd_mirroring object */
8447 cls_register_cxx_method(h_class
, "mirror_uuid_get", CLS_METHOD_RD
,
8448 mirror_uuid_get
, &h_mirror_uuid_get
);
8449 cls_register_cxx_method(h_class
, "mirror_uuid_set",
8450 CLS_METHOD_RD
| CLS_METHOD_WR
,
8451 mirror_uuid_set
, &h_mirror_uuid_set
);
8452 cls_register_cxx_method(h_class
, "mirror_mode_get", CLS_METHOD_RD
,
8453 mirror_mode_get
, &h_mirror_mode_get
);
8454 cls_register_cxx_method(h_class
, "mirror_mode_set",
8455 CLS_METHOD_RD
| CLS_METHOD_WR
,
8456 mirror_mode_set
, &h_mirror_mode_set
);
8457 cls_register_cxx_method(h_class
, "mirror_peer_ping",
8458 CLS_METHOD_RD
| CLS_METHOD_WR
,
8459 mirror_peer_ping
, &h_mirror_peer_ping
);
8460 cls_register_cxx_method(h_class
, "mirror_peer_list", CLS_METHOD_RD
,
8461 mirror_peer_list
, &h_mirror_peer_list
);
8462 cls_register_cxx_method(h_class
, "mirror_peer_add",
8463 CLS_METHOD_RD
| CLS_METHOD_WR
,
8464 mirror_peer_add
, &h_mirror_peer_add
);
8465 cls_register_cxx_method(h_class
, "mirror_peer_remove",
8466 CLS_METHOD_RD
| CLS_METHOD_WR
,
8467 mirror_peer_remove
, &h_mirror_peer_remove
);
8468 cls_register_cxx_method(h_class
, "mirror_peer_set_client",
8469 CLS_METHOD_RD
| CLS_METHOD_WR
,
8470 mirror_peer_set_client
, &h_mirror_peer_set_client
);
8471 cls_register_cxx_method(h_class
, "mirror_peer_set_cluster",
8472 CLS_METHOD_RD
| CLS_METHOD_WR
,
8473 mirror_peer_set_cluster
, &h_mirror_peer_set_cluster
);
8474 cls_register_cxx_method(h_class
, "mirror_peer_set_direction",
8475 CLS_METHOD_RD
| CLS_METHOD_WR
,
8476 mirror_peer_set_direction
,
8477 &h_mirror_peer_set_direction
);
8478 cls_register_cxx_method(h_class
, "mirror_image_list", CLS_METHOD_RD
,
8479 mirror_image_list
, &h_mirror_image_list
);
8480 cls_register_cxx_method(h_class
, "mirror_image_get_image_id", CLS_METHOD_RD
,
8481 mirror_image_get_image_id
,
8482 &h_mirror_image_get_image_id
);
8483 cls_register_cxx_method(h_class
, "mirror_image_get", CLS_METHOD_RD
,
8484 mirror_image_get
, &h_mirror_image_get
);
8485 cls_register_cxx_method(h_class
, "mirror_image_set",
8486 CLS_METHOD_RD
| CLS_METHOD_WR
,
8487 mirror_image_set
, &h_mirror_image_set
);
8488 cls_register_cxx_method(h_class
, "mirror_image_remove",
8489 CLS_METHOD_RD
| CLS_METHOD_WR
,
8490 mirror_image_remove
, &h_mirror_image_remove
);
8491 cls_register_cxx_method(h_class
, "mirror_image_status_set",
8492 CLS_METHOD_RD
| CLS_METHOD_WR
| CLS_METHOD_PROMOTE
,
8493 mirror_image_status_set
, &h_mirror_image_status_set
);
8494 cls_register_cxx_method(h_class
, "mirror_image_status_remove",
8495 CLS_METHOD_RD
| CLS_METHOD_WR
,
8496 mirror_image_status_remove
,
8497 &h_mirror_image_status_remove
);
8498 cls_register_cxx_method(h_class
, "mirror_image_status_get", CLS_METHOD_RD
,
8499 mirror_image_status_get
, &h_mirror_image_status_get
);
8500 cls_register_cxx_method(h_class
, "mirror_image_status_list", CLS_METHOD_RD
,
8501 mirror_image_status_list
,
8502 &h_mirror_image_status_list
);
8503 cls_register_cxx_method(h_class
, "mirror_image_status_get_summary",
8504 CLS_METHOD_RD
, mirror_image_status_get_summary
,
8505 &h_mirror_image_status_get_summary
);
8506 cls_register_cxx_method(h_class
, "mirror_image_status_remove_down",
8507 CLS_METHOD_RD
| CLS_METHOD_WR
,
8508 mirror_image_status_remove_down
,
8509 &h_mirror_image_status_remove_down
);
8510 cls_register_cxx_method(h_class
, "mirror_image_instance_get", CLS_METHOD_RD
,
8511 mirror_image_instance_get
,
8512 &h_mirror_image_instance_get
);
8513 cls_register_cxx_method(h_class
, "mirror_image_instance_list", CLS_METHOD_RD
,
8514 mirror_image_instance_list
,
8515 &h_mirror_image_instance_list
);
8516 cls_register_cxx_method(h_class
, "mirror_instances_list", CLS_METHOD_RD
,
8517 mirror_instances_list
, &h_mirror_instances_list
);
8518 cls_register_cxx_method(h_class
, "mirror_instances_add",
8519 CLS_METHOD_RD
| CLS_METHOD_WR
| CLS_METHOD_PROMOTE
,
8520 mirror_instances_add
, &h_mirror_instances_add
);
8521 cls_register_cxx_method(h_class
, "mirror_instances_remove",
8522 CLS_METHOD_RD
| CLS_METHOD_WR
,
8523 mirror_instances_remove
,
8524 &h_mirror_instances_remove
);
8525 cls_register_cxx_method(h_class
, "mirror_image_map_list",
8526 CLS_METHOD_RD
, mirror_image_map_list
,
8527 &h_mirror_image_map_list
);
8528 cls_register_cxx_method(h_class
, "mirror_image_map_update",
8529 CLS_METHOD_WR
, mirror_image_map_update
,
8530 &h_mirror_image_map_update
);
8531 cls_register_cxx_method(h_class
, "mirror_image_map_remove",
8532 CLS_METHOD_WR
, mirror_image_map_remove
,
8533 &h_mirror_image_map_remove
);
8534 cls_register_cxx_method(h_class
, "mirror_image_snapshot_unlink_peer",
8535 CLS_METHOD_RD
| CLS_METHOD_WR
,
8536 mirror_image_snapshot_unlink_peer
,
8537 &h_mirror_image_snapshot_unlink_peer
);
8538 cls_register_cxx_method(h_class
, "mirror_image_snapshot_set_copy_progress",
8539 CLS_METHOD_RD
| CLS_METHOD_WR
,
8540 mirror_image_snapshot_set_copy_progress
,
8541 &h_mirror_image_snapshot_set_copy_progress
);
8543 /* methods for the groups feature */
8544 cls_register_cxx_method(h_class
, "group_dir_list",
8546 group_dir_list
, &h_group_dir_list
);
8547 cls_register_cxx_method(h_class
, "group_dir_add",
8548 CLS_METHOD_RD
| CLS_METHOD_WR
,
8549 group_dir_add
, &h_group_dir_add
);
8550 cls_register_cxx_method(h_class
, "group_dir_remove",
8551 CLS_METHOD_RD
| CLS_METHOD_WR
,
8552 group_dir_remove
, &h_group_dir_remove
);
8553 cls_register_cxx_method(h_class
, "group_dir_rename",
8554 CLS_METHOD_RD
| CLS_METHOD_WR
,
8555 group_dir_rename
, &h_group_dir_rename
);
8556 cls_register_cxx_method(h_class
, "group_image_remove",
8557 CLS_METHOD_RD
| CLS_METHOD_WR
,
8558 group_image_remove
, &h_group_image_remove
);
8559 cls_register_cxx_method(h_class
, "group_image_list",
8561 group_image_list
, &h_group_image_list
);
8562 cls_register_cxx_method(h_class
, "group_image_set",
8563 CLS_METHOD_RD
| CLS_METHOD_WR
,
8564 group_image_set
, &h_group_image_set
);
8565 cls_register_cxx_method(h_class
, "image_group_add",
8566 CLS_METHOD_RD
| CLS_METHOD_WR
,
8567 image_group_add
, &h_image_group_add
);
8568 cls_register_cxx_method(h_class
, "image_group_remove",
8569 CLS_METHOD_RD
| CLS_METHOD_WR
,
8570 image_group_remove
, &h_image_group_remove
);
8571 cls_register_cxx_method(h_class
, "image_group_get",
8573 image_group_get
, &h_image_group_get
);
8574 cls_register_cxx_method(h_class
, "group_snap_set",
8575 CLS_METHOD_RD
| CLS_METHOD_WR
,
8576 group_snap_set
, &h_group_snap_set
);
8577 cls_register_cxx_method(h_class
, "group_snap_remove",
8578 CLS_METHOD_RD
| CLS_METHOD_WR
,
8579 group_snap_remove
, &h_group_snap_remove
);
8580 cls_register_cxx_method(h_class
, "group_snap_get_by_id",
8582 group_snap_get_by_id
, &h_group_snap_get_by_id
);
8583 cls_register_cxx_method(h_class
, "group_snap_list",
8585 group_snap_list
, &h_group_snap_list
);
8587 /* rbd_trash object methods */
8588 cls_register_cxx_method(h_class
, "trash_add",
8589 CLS_METHOD_RD
| CLS_METHOD_WR
,
8590 trash_add
, &h_trash_add
);
8591 cls_register_cxx_method(h_class
, "trash_remove",
8592 CLS_METHOD_RD
| CLS_METHOD_WR
,
8593 trash_remove
, &h_trash_remove
);
8594 cls_register_cxx_method(h_class
, "trash_list",
8596 trash_list
, &h_trash_list
);
8597 cls_register_cxx_method(h_class
, "trash_get",
8599 trash_get
, &h_trash_get
);
8600 cls_register_cxx_method(h_class
, "trash_state_set",
8601 CLS_METHOD_RD
| CLS_METHOD_WR
,
8602 trash_state_set
, &h_trash_state_set
);
8604 /* rbd_namespace object methods */
8605 cls_register_cxx_method(h_class
, "namespace_add",
8606 CLS_METHOD_RD
| CLS_METHOD_WR
,
8607 namespace_add
, &h_namespace_add
);
8608 cls_register_cxx_method(h_class
, "namespace_remove",
8609 CLS_METHOD_RD
| CLS_METHOD_WR
,
8610 namespace_remove
, &h_namespace_remove
);
8611 cls_register_cxx_method(h_class
, "namespace_list", CLS_METHOD_RD
,
8612 namespace_list
, &h_namespace_list
);
8614 /* data object methods */
8615 cls_register_cxx_method(h_class
, "copyup",
8616 CLS_METHOD_RD
| CLS_METHOD_WR
,
8618 cls_register_cxx_method(h_class
, "sparse_copyup",
8619 CLS_METHOD_RD
| CLS_METHOD_WR
,
8620 sparse_copyup
, &h_sparse_copyup
);
8621 cls_register_cxx_method(h_class
, "assert_snapc_seq",
8622 CLS_METHOD_RD
| CLS_METHOD_WR
,
8624 &h_assert_snapc_seq
);
8625 cls_register_cxx_method(h_class
, "sparsify",
8626 CLS_METHOD_RD
| CLS_METHOD_WR
,
8627 sparsify
, &h_sparsify
);