... to show information about a group snapshot.
And also include group snap ID in `group snap ls` output.
Fixes: https://tracker.ceph.com/issues/66011
Signed-off-by: Ramana Raja <rraja@redhat.com>
including `timezone.utc` makes it explicit and avoids the potential of the
returned timestamp getting misinterpreted -- in Python 3, many `datetime`
methods treat "naive" `datetime` objects as local times.
+* RBD: `rbd group info` and `rbd group snap info` commands are introduced to
+ show information about a group and a group snapshot respectively.
+* RBD: `rbd group snap ls` output now includes the group snap IDs. The header
+ of the column showing the state of a group snapshot in the unformatted CLI
+ output is changed from 'STATUS' to 'STATE'. The state of a group snapshot
+ that was shown as 'ok' is now shown as 'complete', which is more descriptive.
>=19.0.0
:command:`group snap list` *group-spec*
List snapshots of a group.
+:command:`group snap info` *group-snap-spec*
+ Get information about a snapshot of a group.
+
:command:`group snap rm` *group-snap-spec*
Remove a snapshot from a group.
check_group_exists()
{
local group_name=$1
- list_groups | grep $group_name
+ list_groups | grep -w $group_name
}
remove_group()
{
local group_name=$1
local snap_name=$2
- list_snapshots $group_name | grep $snap_name
+ list_snapshots $group_name | grep -w $snap_name
}
check_snapshots_count_in_group()
{
local group_name=$1
local snap_name=$2
- for v in $(list_snapshots $group_name | awk '{print $1}'); do
- if [ "$v" = "$snap_name" ]; then
- return 1
- fi
- done
- return 0
+
+ check_group_exists $group_name || return 1
+ ! check_snapshot_in_group $group_name $snap_name
+}
+
+check_snap_id_in_list_snapshots()
+{
+ local group_name=$1
+ local snap_name=$2
+
+ local snap_id_in_info=$(
+ rbd group snap info $group_name@$snap_name --format=json |
+ jq -r '.id')
+ [[ -n "$snap_id_in_info" ]] || return 1
+
+ local snap_id_in_list=$(
+ rbd group snap ls $group_name --format=json |
+ jq --arg snap_name $snap_name -r '
+ .[] | select(.snapshot == $snap_name) | .id')
+ test "$snap_id_in_list" = "$snap_id_in_info"
+}
+
+check_snapshot_info()
+{
+ local group_name=$1
+ local snap_name=$2
+ local image_count=$3
+
+ local snap_info=$(rbd group snap info $group_name@$snap_name --format=json)
+ local actual_snap_name=$(jq -r ".name" <<< "$snap_info")
+ test "$actual_snap_name" = "$snap_name" || return 1
+
+ local snap_state=$(jq -r ".state" <<< "$snap_info")
+ test "$snap_state" = "complete" || return 1
+
+ local actual_image_count=$(jq '.images | length' <<< "$snap_info")
+ test "$actual_image_count" = "$image_count"
}
echo "TEST: create remove consistency group"
echo "TEST: create remove snapshots of consistency group"
image="test_image"
group="test_consistency_group"
-snap="group_snap"
-new_snap="new_group_snap"
-sec_snap="group_snap2"
+snaps=("group_snap1" "group_snap2" "group_snap3" "group_snap4")
create_image $image
create_group $group
+create_snapshot $group ${snaps[0]}
+check_snapshot_info $group ${snaps[0]} 0
add_image_to_group $image $group
-create_snapshot $group $snap
-check_snapshot_in_group $group $snap
-rename_snapshot $group $snap $new_snap
-check_snapshot_not_in_group $group $snap
-create_snapshot $group $sec_snap
-check_snapshot_in_group $group $sec_snap
-rollback_snapshot $group $new_snap
-remove_snapshot $group $new_snap
-check_snapshot_not_in_group $group $new_snap
-remove_snapshot $group $sec_snap
-check_snapshot_not_in_group $group $sec_snap
+create_snapshot $group ${snaps[1]}
+check_snapshot_info $group ${snaps[1]} 1
+rename_snapshot $group ${snaps[1]} ${snaps[2]}
+check_snapshot_info $group ${snaps[2]} 1
+check_snapshot_not_in_group $group ${snaps[1]}
+create_snapshot $group ${snaps[3]}
+check_snapshot_in_group $group ${snaps[3]}
+rollback_snapshot $group ${snaps[2]}
+remove_snapshot $group ${snaps[2]}
+check_snapshot_not_in_group $group ${snaps[2]}
+remove_snapshot $group ${snaps[3]}
+check_snapshot_not_in_group $group ${snaps[3]}
remove_group $group
remove_image $image
echo "PASSED"
add_image_to_group $image $group
create_snapshots $group $snap 10
check_snapshots_count_in_group $group $snap 10
+check_snap_id_in_list_snapshots $group ${snap}1
remove_snapshots $group $snap 10
create_snapshots $group $snap 100
check_snapshots_count_in_group $group $snap 100
RBD_GROUP_SNAP_STATE_COMPLETE
} rbd_group_snap_state_t;
+typedef struct {
+ char *image_name;
+ int64_t pool_id;
+ uint64_t snap_id;
+} rbd_group_image_snap_info_t;
+
typedef struct {
char *name;
rbd_group_snap_state_t state;
} rbd_group_snap_info_t;
+typedef struct {
+ char *id;
+ char *name;
+ char *image_snap_name;
+ rbd_group_snap_state_t state;
+ //rbd_group_snap_namespace_type_t namespace_type;
+ size_t image_snaps_count;
+ rbd_group_image_snap_info_t *image_snaps;
+} rbd_group_snap_info2_t;
+
typedef struct {
int64_t group_pool;
char *group_name;
CEPH_RBD_API int rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps,
size_t group_snap_info_size,
size_t num_entries);
+CEPH_RBD_API int rbd_group_snap_list2(rados_ioctx_t group_p,
+ const char *group_name,
+ rbd_group_snap_info2_t *snaps,
+ size_t *num_entries);
+CEPH_RBD_API void rbd_group_snap_list2_cleanup(rbd_group_snap_info2_t *snaps,
+ size_t num_entries);
+CEPH_RBD_API int rbd_group_snap_get_info(rados_ioctx_t group_p,
+ const char *group_name,
+ const char *snap_name,
+ rbd_group_snap_info2_t *group_snap);
+CEPH_RBD_API void rbd_group_snap_get_info_cleanup(
+ rbd_group_snap_info2_t *group_snap);
CEPH_RBD_API int rbd_group_snap_rollback(rados_ioctx_t group_p,
const char *group_name,
const char *snap_name);
typedef rbd_group_snap_state_t group_snap_state_t;
+ typedef struct {
+ std::string image_name;
+ int64_t pool_id;
+ uint64_t snap_id;
+ } group_image_snap_info_t;
+
typedef struct {
std::string name;
group_snap_state_t state;
} group_snap_info_t;
+ typedef struct {
+ std::string id;
+ std::string name;
+ std::string image_snap_name;
+ group_snap_state_t state;
+ //group_snap_namespace_type_t namespace_type;
+ std::vector<group_image_snap_info_t> image_snaps;
+ } group_snap_info2_t;
+
typedef rbd_image_info_t image_info_t;
class CEPH_RBD_API ProgressContext
int group_snap_list(IoCtx& group_ioctx, const char *group_name,
std::vector<group_snap_info_t> *snaps,
size_t group_snap_info_size);
+ int group_snap_list2(IoCtx& group_ioctx, const char *group_name,
+ std::vector<group_snap_info2_t> *snaps);
+ int group_snap_get_info(IoCtx& group_ioctx, const char *group_name,
+ const char *snap_name,
+ group_snap_info2_t *group_snap);
int group_snap_rollback(IoCtx& io_ctx, const char *group_name,
const char *snap_name);
int group_snap_rollback_with_progress(IoCtx& io_ctx, const char *group_name,
return CEPH_NOSNAP;
}
-int group_snap_list(librados::IoCtx& group_ioctx, const char *group_name,
+int group_snap_list(librados::IoCtx& group_ioctx, const std::string& group_id,
std::vector<cls::rbd::GroupSnapshot> *cls_snaps)
{
CephContext *cct = (CephContext *)group_ioctx.cct();
- string group_id;
- vector<string> ind_snap_names;
-
- int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
- group_name, &group_id);
- if (r < 0) {
- lderr(cct) << "error reading group id object: "
- << cpp_strerror(r)
- << dendl;
- return r;
- }
string group_header_oid = util::group_header_name(group_id);
const int max_read = 1024;
cls::rbd::GroupSnapshot snap_last;
+ int r;
for (;;) {
vector<cls::rbd::GroupSnapshot> snaps_page;
return ret_code;
}
+int GroupSnapshot_to_group_snap_info2(
+ librados::IoCtx& group_ioctx, const std::string& group_id,
+ const cls::rbd::GroupSnapshot& cls_group_snap,
+ group_snap_info2_t* group_snap)
+{
+ std::vector<group_image_snap_info_t> image_snaps;
+ image_snaps.reserve(cls_group_snap.snaps.size());
+
+ for (const auto& snap : cls_group_snap.snaps) {
+ librbd::IoCtx image_ioctx;
+ int r = util::create_ioctx(group_ioctx, "image", snap.pool, {},
+ &image_ioctx);
+ if (r < 0) {
+ return r;
+ }
+
+ std::string image_name;
+ r = cls_client::dir_get_name(&image_ioctx, RBD_DIRECTORY, snap.image_id,
+ &image_name);
+ if (r < 0) {
+ return r;
+ }
+
+ image_snaps.push_back(
+ group_image_snap_info_t {
+ std::move(image_name),
+ snap.pool,
+ snap.snap_id
+ });
+ }
+
+ group_snap->id = cls_group_snap.id;
+ group_snap->name = cls_group_snap.name;
+ group_snap->state = static_cast<group_snap_state_t>(cls_group_snap.state);
+ group_snap->image_snap_name = calc_ind_image_snap_name(group_ioctx.get_id(),
+ group_id,
+ cls_group_snap.id);
+ group_snap->image_snaps = std::move(image_snaps);
+
+ return 0;
+}
+
} // anonymous namespace
template <typename I>
string group_header_oid = util::group_header_name(group_id);
std::vector<cls::rbd::GroupSnapshot> snaps;
- r = group_snap_list(io_ctx, group_name, &snaps);
+ r = group_snap_list(io_ctx, group_id, &snaps);
if (r < 0 && r != -ENOENT) {
lderr(cct) << "error listing group snapshots" << dendl;
return r;
}
std::vector<cls::rbd::GroupSnapshot> snaps;
- r = group_snap_list(group_ioctx, group_name, &snaps);
+ r = group_snap_list(group_ioctx, group_id, &snaps);
if (r < 0) {
return r;
}
}
std::vector<cls::rbd::GroupSnapshot> group_snaps;
- r = group_snap_list(group_ioctx, group_name, &group_snaps);
+ r = group_snap_list(group_ioctx, group_id, &group_snaps);
if (r < 0) {
return r;
}
template <typename I>
int Group<I>::snap_list(librados::IoCtx& group_ioctx, const char *group_name,
- std::vector<group_snap_info_t> *snaps)
+ std::vector<group_snap_info2_t> *group_snaps)
+{
+ CephContext *cct = (CephContext *)group_ioctx.cct();
+
+ std::string group_id;
+ int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
+ group_name, &group_id);
+ if (r < 0) {
+ lderr(cct) << "error reading group id object: " << cpp_strerror(r)
+ << dendl;
+ return r;
+ }
+
+ std::vector<cls::rbd::GroupSnapshot> cls_group_snaps;
+ r = group_snap_list(group_ioctx, group_id, &cls_group_snaps);
+ if (r < 0) {
+ return r;
+ }
+
+ std::vector<group_snap_info2_t> group_snaps_tmp(cls_group_snaps.size());
+ for (size_t i = 0; i < cls_group_snaps.size(); i++) {
+ r = GroupSnapshot_to_group_snap_info2(group_ioctx, group_id,
+ cls_group_snaps[i],
+ &group_snaps_tmp[i]);
+ if (r < 0) {
+ return r;
+ }
+ }
+
+ *group_snaps = std::move(group_snaps_tmp);
+ return 0;
+}
+
+template <typename I>
+int Group<I>::snap_get_info(librados::IoCtx& group_ioctx,
+ const char *group_name, const char *snap_name,
+ group_snap_info2_t* group_snap)
{
- std::vector<cls::rbd::GroupSnapshot> cls_snaps;
+ CephContext *cct = (CephContext *)group_ioctx.cct();
- int r = group_snap_list(group_ioctx, group_name, &cls_snaps);
+ std::string group_id;
+ int r = cls_client::dir_get_id(&group_ioctx, RBD_GROUP_DIRECTORY,
+ group_name, &group_id);
if (r < 0) {
+ lderr(cct) << "error reading group id object: " << cpp_strerror(r)
+ << dendl;
return r;
}
- for (auto snap : cls_snaps) {
- snaps->push_back(
- group_snap_info_t {
- snap.name,
- static_cast<group_snap_state_t>(snap.state)});
+ std::vector<cls::rbd::GroupSnapshot> cls_group_snaps;
+ r = group_snap_list(group_ioctx, group_id, &cls_group_snaps);
+ if (r < 0) {
+ return r;
+ }
+ const cls::rbd::GroupSnapshot *cls_group_snap_ptr = nullptr;
+ for (const auto& cls_group_snap : cls_group_snaps) {
+ if (cls_group_snap.name == snap_name) {
+ cls_group_snap_ptr = &cls_group_snap;
+ break;
+ }
+ }
+ if (cls_group_snap_ptr == nullptr) {
+ return -ENOENT;
+ }
+
+ r = GroupSnapshot_to_group_snap_info2(group_ioctx, group_id,
+ *cls_group_snap_ptr, group_snap);
+ if (r < 0) {
+ return r;
}
+
return 0;
}
}
std::vector<cls::rbd::GroupSnapshot> snaps;
- r = group_snap_list(group_ioctx, group_name, &snaps);
+ r = group_snap_list(group_ioctx, group_id, &snaps);
if (r < 0) {
return r;
}
static int snap_rename(librados::IoCtx& group_ioctx, const char *group_name,
const char *old_snap_name, const char *new_snap_name);
static int snap_list(librados::IoCtx& group_ioctx, const char *group_name,
- std::vector<group_snap_info_t> *snaps);
+ std::vector<group_snap_info2_t> *snaps);
+ static int snap_get_info(librados::IoCtx& group_ioctx,
+ const char *group_name, const char *snap_name,
+ group_snap_info2_t* group_snap);
static int snap_rollback(librados::IoCtx& group_ioctx,
const char *group_name, const char *snap_name,
ProgressContext& pctx);
c_info->pool = cpp_info.pool;
}
-void group_snap_info_cpp_to_c(const librbd::group_snap_info_t &cpp_info,
+void group_snap_info_cpp_to_c(const librbd::group_snap_info2_t &cpp_info,
rbd_group_snap_info_t *c_info) {
c_info->name = strdup(cpp_info.name.c_str());
c_info->state = cpp_info.state;
}
+void group_snap_info2_cpp_to_c(const librbd::group_snap_info2_t &cpp_info,
+ rbd_group_snap_info2_t *c_info) {
+ c_info->id = strdup(cpp_info.id.c_str());
+ c_info->name = strdup(cpp_info.name.c_str());
+ c_info->image_snap_name = strdup(cpp_info.image_snap_name.c_str());
+ c_info->state = cpp_info.state;
+ c_info->image_snaps_count = cpp_info.image_snaps.size();
+ c_info->image_snaps = static_cast<rbd_group_image_snap_info_t*>(calloc(
+ cpp_info.image_snaps.size(), sizeof(rbd_group_image_snap_info_t)));
+ size_t i = 0;
+ for (const auto& cpp_image_snap : cpp_info.image_snaps) {
+ c_info->image_snaps[i].image_name = strdup(
+ cpp_image_snap.image_name.c_str());
+ c_info->image_snaps[i].pool_id = cpp_image_snap.pool_id;
+ c_info->image_snaps[i].snap_id = cpp_image_snap.snap_id;
+ i++;
+ }
+}
+
void mirror_image_info_cpp_to_c(const librbd::mirror_image_info_t &cpp_info,
rbd_mirror_image_info_t *c_info) {
c_info->global_id = strdup(cpp_info.global_id.c_str());
return -ERANGE;
}
- int r = librbd::api::Group<>::snap_list(group_ioctx, group_name, snaps);
+ std::vector<group_snap_info2_t> snaps2;
+ int r = librbd::api::Group<>::snap_list(group_ioctx, group_name, &snaps2);
+
+ for (const auto& snap : snaps2) {
+ snaps->push_back(
+ group_snap_info_t {
+ snap.name,
+ snap.state
+ });
+ }
+
tracepoint(librbd, group_snap_list_exit, r);
return r;
}
+ int RBD::group_snap_list2(IoCtx& group_ioctx, const char *group_name,
+ std::vector<group_snap_info2_t> *snaps)
+ {
+ return librbd::api::Group<>::snap_list(group_ioctx, group_name, snaps);
+ }
+
+ int RBD::group_snap_get_info(IoCtx& group_ioctx, const char *group_name,
+ const char *snap_name,
+ group_snap_info2_t *group_snap) {
+ return librbd::api::Group<>::snap_get_info(group_ioctx, group_name,
+ snap_name, group_snap);
+ }
+
int RBD::group_snap_rename(IoCtx& group_ioctx, const char *group_name,
const char *old_snap_name,
const char *new_snap_name)
return r;
}
+extern "C" int rbd_group_snap_get_info(
+ rados_ioctx_t group_p, const char *group_name, const char *snap_name,
+ rbd_group_snap_info2_t *group_snap)
+{
+ librados::IoCtx group_ioctx;
+ librados::IoCtx::from_rados_ioctx_t(group_p, group_ioctx);
+
+ librbd::group_snap_info2_t cpp_group_snap;
+ int r = librbd::api::Group<>::snap_get_info(group_ioctx, group_name,
+ snap_name, &cpp_group_snap);
+ if (r < 0) {
+ return r;
+ }
+ group_snap_info2_cpp_to_c(cpp_group_snap, group_snap);
+ return 0;
+}
+
+extern "C" void rbd_group_snap_get_info_cleanup(
+ rbd_group_snap_info2_t *group_snap) {
+ free(group_snap->id);
+ free(group_snap->name);
+ free(group_snap->image_snap_name);
+ for (size_t i = 0; i < group_snap->image_snaps_count; ++i) {
+ free(group_snap->image_snaps[i].image_name);
+ }
+ free(group_snap->image_snaps);
+}
+
extern "C" int rbd_group_snap_list(rados_ioctx_t group_p,
const char *group_name,
rbd_group_snap_info_t *snaps,
return -ERANGE;
}
- std::vector<librbd::group_snap_info_t> cpp_snaps;
+ std::vector<librbd::group_snap_info2_t> cpp_snaps;
int r = librbd::api::Group<>::snap_list(group_ioctx, group_name, &cpp_snaps);
if (r == -ENOENT) {
return 0;
}
+extern "C" int rbd_group_snap_list2(rados_ioctx_t group_p,
+ const char *group_name,
+ rbd_group_snap_info2_t *snaps,
+ size_t *snaps_size)
+{
+ librados::IoCtx group_ioctx;
+ librados::IoCtx::from_rados_ioctx_t(group_p, group_ioctx);
+
+ std::vector<librbd::group_snap_info2_t> cpp_snaps;
+ int r = librbd::api::Group<>::snap_list(group_ioctx, group_name, &cpp_snaps);
+ if (r < 0) {
+ return r;
+ }
+
+ if (*snaps_size < cpp_snaps.size()) {
+ *snaps_size = cpp_snaps.size();
+ return -ERANGE;
+ }
+
+ for (size_t i = 0; i < cpp_snaps.size(); ++i) {
+ group_snap_info2_cpp_to_c(cpp_snaps[i], &snaps[i]);
+ }
+
+ *snaps_size = cpp_snaps.size();
+ return 0;
+}
+
+extern "C" void rbd_group_snap_list2_cleanup(rbd_group_snap_info2_t *snaps,
+ size_t len) {
+ for (size_t i = 0; i < len; ++i) {
+ rbd_group_snap_get_info_cleanup(&snaps[i]);
+ }
+}
+
extern "C" int rbd_group_snap_rollback(rados_ioctx_t group_p,
const char *group_name,
const char *snap_name)
_RBD_GROUP_SNAP_STATE_INCOMPLETE "RBD_GROUP_SNAP_STATE_INCOMPLETE"
_RBD_GROUP_SNAP_STATE_COMPLETE "RBD_GROUP_SNAP_STATE_COMPLETE"
- ctypedef struct rbd_group_snap_info_t:
+ ctypedef struct rbd_group_image_snap_info_t:
+ char *image_name
+ int64_t pool_id
+ uint64_t snap_id
+
+ ctypedef struct rbd_group_snap_info2_t:
+ char *id
char *name
+ char *image_snap_name
rbd_group_snap_state_t state
+ size_t image_snaps_count
+ rbd_group_image_snap_info_t *image_snaps
ctypedef enum rbd_image_migration_state_t:
_RBD_IMAGE_MIGRATION_STATE_UNKNOWN "RBD_IMAGE_MIGRATION_STATE_UNKNOWN"
const char *old_snap_name,
const char *new_snap_name)
- int rbd_group_snap_list(rados_ioctx_t group_p,
- const char *group_name,
- rbd_group_snap_info_t *snaps,
- size_t group_snap_info_size,
- size_t *snaps_size)
+ int rbd_group_snap_get_info(rados_ioctx_t group_p, const char *group_name,
+ const char *snap_name,
+ rbd_group_snap_info2_t *group_snap)
+ void rbd_group_snap_get_info_cleanup(rbd_group_snap_info2_t *group_snap)
+
+ int rbd_group_snap_list2(rados_ioctx_t group_p,
+ const char *group_name,
+ rbd_group_snap_info2_t *snaps,
+ size_t *snaps_size)
+ void rbd_group_snap_list2_cleanup(rbd_group_snap_info2_t *snaps,
+ size_t len)
- void rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps,
- size_t group_snap_info_size, size_t len)
int rbd_group_snap_rollback(rados_ioctx_t group_p, const char *group_name,
const char *snap_name)
_RBD_GROUP_SNAP_STATE_INCOMPLETE "RBD_GROUP_SNAP_STATE_INCOMPLETE"
_RBD_GROUP_SNAP_STATE_COMPLETE "RBD_GROUP_SNAP_STATE_COMPLETE"
- ctypedef struct rbd_group_snap_info_t:
+ ctypedef struct rbd_group_image_snap_info_t:
+ char *image_name
+ int64_t pool_id
+ uint64_t snap_id
+
+ ctypedef struct rbd_group_snap_info2_t:
+ char *id
char *name
+ char *image_snap_name
rbd_group_snap_state_t state
+ size_t image_snaps_count
+ rbd_group_image_snap_info_t *image_snaps
ctypedef enum rbd_image_migration_state_t:
_RBD_IMAGE_MIGRATION_STATE_UNKNOWN "RBD_IMAGE_MIGRATION_STATE_UNKNOWN"
const char *old_snap_name,
const char *new_snap_name):
pass
- int rbd_group_snap_list(rados_ioctx_t group_p,
- const char *group_name,
- rbd_group_snap_info_t *snaps,
- size_t group_snap_info_size,
- size_t *snaps_size):
+ int rbd_group_snap_list2(rados_ioctx_t group_p,
+ const char *group_name,
+ rbd_group_snap_info2_t *snaps,
+ size_t *snaps_size):
+ pass
+ void rbd_group_snap_list2_cleanup(rbd_group_snap_info2_t *snaps,
+ size_t len):
+ pass
+ int rbd_group_snap_get_info(rados_ioctx_t group_p, const char *group_name,
+ const char *snap_name,
+ rbd_group_snap_info2_t *group_snap):
pass
- void rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps,
- size_t group_snap_info_size, size_t len):
+ void rbd_group_snap_get_info_cleanup(rbd_group_snap_info2_t *group_snap):
pass
int rbd_group_snap_rollback(rados_ioctx_t group_p, const char *group_name,
const char *snap_name):
if ret != 0:
raise make_ex(ret, 'error removing group snapshot', group_errno_to_exception)
+ def get_snap_info(self, snap_name):
+ """
+ Get information about a group snapshot.
+
+ :param snap_name: the name of the snapshot to get
+ :type name: str
+
+ :raises: :class:`ObjectNotFound`
+ :raises: :class:`InvalidArgument`
+ :raises: :class:`FunctionNotSupported`
+
+ :returns: dict - contains the following keys:
+
+ * ``id`` (str) - ID of the group snapshot
+
+ * ``name`` (str) - name of the group snapshot
+
+ * ``state`` (int) - state of the group snapshot
+
+ * ``image_snap_name`` (str) - name of the image snapshots
+
+ * ``image_snaps`` (list) - image snapshots that constitute the group snapshot.
+
+ Each image snapshot is itself a dictionary with keys:
+
+ * ``pool_id`` (int) - ID of the image's pool
+
+ * ``snap_id`` (int) - ID of the image snapshot
+
+ * ``image_name`` (str) - name of the image
+
+ """
+ snap_name = cstr(snap_name, 'snap_name')
+ cdef:
+ char *_snap_name = snap_name
+ rbd_group_snap_info2_t group_snap
+
+ with nogil:
+ ret = rbd_group_snap_get_info(self._ioctx, self._name,
+ _snap_name, &group_snap)
+ if ret != 0:
+ raise make_ex(ret, 'error showing a group snapshot',
+ group_errno_to_exception)
+ image_snaps = []
+ for i in range(group_snap.image_snaps_count):
+ image_snap = &group_snap.image_snaps[i]
+ image_snaps.append(
+ {
+ 'pool_id': image_snap.pool_id,
+ 'snap_id': image_snap.snap_id,
+ 'image_name': decode_cstr(image_snap.image_name),
+ }
+ )
+ snap_info = {
+ 'id': decode_cstr(group_snap.id),
+ 'name': decode_cstr(group_snap.name),
+ 'state': group_snap.state,
+ 'image_snap_name': decode_cstr(group_snap.image_snap_name),
+ 'image_snaps': image_snaps
+ }
+
+ rbd_group_snap_get_info_cleanup(&group_snap)
+
+ return snap_info
+
def rename_snap(self, old_snap_name, new_snap_name):
"""
Rename group's snapshot.
def list_snaps(self):
"""
- Iterate over the images of a group.
+ Iterate over the snapshots of a group.
:returns: :class:`GroupSnapIterator`
"""
"""
Iterator over snaps specs for a group.
- Yields a dictionary containing information about a snapshot.
+ Yields a dictionary containing information about a group snapshot.
Keys are:
- * ``name`` (str) - name of the snapshot
+ * ``id`` (str) - ID of the group snapshot
+
+ * ``name`` (str) - name of the group snapshot
+
+ * ``state`` (int) - state of the group snapshot
+
+ * ``image_snap_name`` (str) - name of the image snapshots
+
+ * ``image_snaps`` (list) - image snapshots that constitute the group snapshot.
+
+ Each image snapshot is itself a dictionary with keys:
- * ``state`` (int) - state of the snapshot
+ * ``pool_id`` (int) - ID of the image's pool
+
+ * ``snap_id`` (int) - ID of the image snapshot
+
+ * ``image_name`` (str) - name of the image
"""
- cdef rbd_group_snap_info_t *snaps
- cdef size_t num_snaps
+ cdef rbd_group_snap_info2_t *group_snaps
+ cdef size_t num_group_snaps
cdef object group
def __init__(self, Group group):
self.group = group
- self.snaps = NULL
- self.num_snaps = 10
+ self.group_snaps = NULL
+ self.num_group_snaps = 10
while True:
- self.snaps = <rbd_group_snap_info_t*>realloc_chk(self.snaps,
- self.num_snaps *
- sizeof(rbd_group_snap_info_t))
+ self.group_snaps = <rbd_group_snap_info2_t*>realloc_chk(
+ self.group_snaps, self.num_group_snaps * sizeof(rbd_group_snap_info2_t))
with nogil:
- ret = rbd_group_snap_list(group._ioctx, group._name, self.snaps,
- sizeof(rbd_group_snap_info_t),
- &self.num_snaps)
-
+ ret = rbd_group_snap_list2(group._ioctx, group._name, self.group_snaps,
+ &self.num_group_snaps)
if ret >= 0:
break
elif ret != -errno.ERANGE:
raise make_ex(ret, 'error listing snapshots for group %s' % group.name, group_errno_to_exception)
def __iter__(self):
- for i in range(self.num_snaps):
+ for i in range(self.num_group_snaps):
+ group_snap = &self.group_snaps[i]
+ image_snaps = []
+ for j in range(group_snap.image_snaps_count):
+ image_snap = &group_snap.image_snaps[j]
+ image_snaps.append(
+ {
+ 'pool_id': image_snap.pool_id,
+ 'snap_id': image_snap.snap_id,
+ 'image_name': decode_cstr(image_snap.image_name),
+ }
+ )
+
yield {
- 'name' : decode_cstr(self.snaps[i].name),
- 'state' : self.snaps[i].state,
- }
+ 'id': decode_cstr(group_snap.id),
+ 'name': decode_cstr(group_snap.name),
+ 'state': group_snap.state,
+ 'image_snap_name': decode_cstr(group_snap.image_snap_name),
+ 'image_snaps': image_snaps,
+ }
def __dealloc__(self):
- if self.snaps:
- rbd_group_snap_list_cleanup(self.snaps,
- sizeof(rbd_group_snap_info_t),
- self.num_snaps)
- free(self.snaps)
+ if self.group_snaps:
+ rbd_group_snap_list2_cleanup(self.group_snaps,
+ self.num_group_snaps)
+ free(self.group_snaps)
group remove (group rm) Delete a group.
group rename Rename a group within pool.
group snap create Make a snapshot of a group.
+ group snap info Show information about a group snapshot.
group snap list (... ls) List snapshots of a group.
group snap remove (... rm) Remove a snapshot from a group.
group snap rename Rename group's snapshot.
--skip-quiesce do not run quiesce hooks
--ignore-quiesce-error ignore quiesce hook error
+ rbd help group snap info
+ usage: rbd group snap info [--pool <pool>] [--namespace <namespace>]
+ [--group <group>] [--snap <snap>]
+ [--format <format>] [--pretty-format]
+ <group-snap-spec>
+
+ Show information about a group snapshot.
+
+ Positional arguments
+ <group-snap-spec> group specification
+ (example:
+ [<pool-name>/[<namespace>/]]<group-name>@<snap-name>)
+
+ Optional arguments
+ -p [ --pool ] arg pool name
+ --namespace arg namespace name
+ --group arg group name
+ --snap arg snapshot name
+ --format arg output format (plain, json, or xml) [default: plain]
+ --pretty-format pretty formatting (json and xml)
+
rbd help group snap list
usage: rbd group snap list [--format <format>] [--pretty-format]
[--pool <pool>] [--namespace <namespace>]
ASSERT_EQ(0, rbd.group_snap_remove(ioctx, group_name, snap_name));
ASSERT_EQ(0, rbd.group_remove(ioctx, group_name));
}
+
+TEST_F(TestGroup, snap_get_info)
+{
+ REQUIRE_FORMAT_V2();
+
+ std::string pool_name2 = get_temp_pool_name("test-librbd-");
+ ASSERT_EQ(0, rados_pool_create(_cluster, pool_name2.c_str()));
+
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, _pool_name.c_str(), &ioctx));
+
+ rados_ioctx_t ioctx2;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, pool_name2.c_str(), &ioctx2));
+
+ const char *gp_name = "gp_snapgetinfo";
+ ASSERT_EQ(0, rbd_group_create(ioctx2, gp_name));
+ ASSERT_EQ(0, rbd_group_image_add(ioctx2, gp_name, ioctx,
+ m_image_name.c_str()));
+
+ const char *gp_snap_name = "snap_snapshot";
+ ASSERT_EQ(0, rbd_group_snap_create(ioctx2, gp_name, gp_snap_name));
+
+ rbd_group_snap_info2_t gp_snap_info;
+ ASSERT_EQ(-ENOENT, rbd_group_snap_get_info(ioctx2, "absent", gp_snap_name,
+ &gp_snap_info));
+ ASSERT_EQ(-ENOENT, rbd_group_snap_get_info(ioctx2, gp_name, "absent",
+ &gp_snap_info));
+
+ ASSERT_EQ(0, rbd_group_snap_get_info(ioctx2, gp_name, gp_snap_name,
+ &gp_snap_info));
+ ASSERT_STREQ(gp_snap_name, gp_snap_info.name);
+ ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snap_info.state);
+ ASSERT_EQ(1U, gp_snap_info.image_snaps_count);
+ ASSERT_EQ(m_image_name, gp_snap_info.image_snaps[0].image_name);
+ ASSERT_EQ(rados_ioctx_get_id(ioctx), gp_snap_info.image_snaps[0].pool_id);
+
+ rbd_group_snap_get_info_cleanup(&gp_snap_info);
+ ASSERT_EQ(0, rbd_group_snap_remove(ioctx2, gp_name, gp_snap_name));
+ ASSERT_EQ(0, rbd_group_remove(ioctx2, gp_name));
+ rados_ioctx_destroy(ioctx2);
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, rados_pool_delete(_cluster, pool_name2.c_str()));
+}
+
+TEST_F(TestGroup, snap_get_infoPP)
+{
+ REQUIRE_FORMAT_V2();
+
+ std::string pool_name2 = get_temp_pool_name("test-librbd-");
+ ASSERT_EQ(0, _rados.pool_create(pool_name2.c_str()));
+
+ librados::IoCtx ioctx2;
+ ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx2));
+
+ const char *gp_name = "gp_snapgetinfoPP";
+ ASSERT_EQ(0, m_rbd.group_create(ioctx2, gp_name));
+ ASSERT_EQ(0, m_rbd.group_image_add(ioctx2, gp_name, m_ioctx,
+ m_image_name.c_str()));
+
+ const char *gp_snap_name = "snap_snapshot";
+ ASSERT_EQ(0, m_rbd.group_snap_create(ioctx2, gp_name, gp_snap_name));
+
+ librbd::group_snap_info2_t gp_snap_info;
+ ASSERT_EQ(-ENOENT, m_rbd.group_snap_get_info(ioctx2, "absent", gp_snap_name,
+ &gp_snap_info));
+ ASSERT_EQ(-ENOENT, m_rbd.group_snap_get_info(ioctx2, gp_name, "absent",
+ &gp_snap_info));
+
+ ASSERT_EQ(0, m_rbd.group_snap_get_info(ioctx2, gp_name, gp_snap_name,
+ &gp_snap_info));
+ ASSERT_EQ(gp_snap_name, gp_snap_info.name);
+ ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snap_info.state);
+ ASSERT_EQ(1U, gp_snap_info.image_snaps.size());
+ ASSERT_EQ(m_image_name, gp_snap_info.image_snaps[0].image_name);
+ ASSERT_EQ(m_ioctx.get_id(), gp_snap_info.image_snaps[0].pool_id);
+
+ ASSERT_EQ(0, m_rbd.group_snap_remove(ioctx2, gp_name, gp_snap_name));
+ ASSERT_EQ(0, m_rbd.group_remove(ioctx2, gp_name));
+ ASSERT_EQ(0, _rados.pool_delete(pool_name2.c_str()));
+}
+
+TEST_F(TestGroup, snap_list2)
+{
+ REQUIRE_FORMAT_V2();
+
+ std::string pool_name2 = get_temp_pool_name("test-librbd-");
+ ASSERT_EQ(0, rados_pool_create(_cluster, pool_name2.c_str()));
+
+ rados_ioctx_t ioctx;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, _pool_name.c_str(), &ioctx));
+
+ rados_ioctx_t ioctx2;
+ ASSERT_EQ(0, rados_ioctx_create(_cluster, pool_name2.c_str(), &ioctx2));
+
+ std::string image_name2 = get_temp_image_name();
+ uint64_t features;
+ int order = 0;
+ ASSERT_TRUE(get_features(&features));
+ ASSERT_EQ(0, rbd_create2(ioctx2, image_name2.c_str(), m_image_size, features,
+ &order));
+
+ const char *gp_name = "gp_snaplist2";
+ ASSERT_EQ(0, rbd_group_create(ioctx, gp_name));
+
+ size_t num_snaps = 10U;
+ auto gp_snaps = static_cast<rbd_group_snap_info2_t*>(calloc(
+ num_snaps, sizeof(rbd_group_snap_info2_t)));
+ ASSERT_EQ(-ENOENT, rbd_group_snap_list2(ioctx, "absent", gp_snaps,
+ &num_snaps));
+ ASSERT_EQ(0, rbd_group_snap_list2(ioctx, gp_name, gp_snaps, &num_snaps));
+ ASSERT_EQ(0U, num_snaps);
+
+ const char* const gp_snap_names[] = {
+ "snap_snapshot0", "snap_snapshot1", "snap_snapshot2", "snap_snapshot3"};
+ ASSERT_EQ(0, rbd_group_snap_create(ioctx, gp_name, gp_snap_names[0]));
+
+ ASSERT_EQ(0, rbd_group_image_add(ioctx, gp_name, ioctx,
+ m_image_name.c_str()));
+ ASSERT_EQ(0, rbd_group_snap_create(ioctx, gp_name, gp_snap_names[1]));
+
+ ASSERT_EQ(0, rbd_group_image_add(ioctx, gp_name, ioctx2,
+ image_name2.c_str()));
+ ASSERT_EQ(0, rbd_group_snap_create(ioctx, gp_name, gp_snap_names[2]));
+
+ ASSERT_EQ(0, rbd_group_image_remove(ioctx, gp_name, ioctx,
+ m_image_name.c_str()));
+ ASSERT_EQ(0, rbd_group_snap_create(ioctx, gp_name, gp_snap_names[3]));
+
+ num_snaps = 3U;
+ ASSERT_EQ(-ERANGE, rbd_group_snap_list2(ioctx, gp_name, gp_snaps,
+ &num_snaps));
+ ASSERT_EQ(4U, num_snaps);
+ ASSERT_EQ(0, rbd_group_snap_list2(ioctx, gp_name, gp_snaps, &num_snaps));
+ ASSERT_EQ(4U, num_snaps);
+
+ for (int i = 0; i < 4; i++) {
+ ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snaps[i].state);
+ if (!strcmp(gp_snaps[i].name, gp_snap_names[0])) {
+ ASSERT_EQ(0U, gp_snaps[i].image_snaps_count);
+ } else if (!strcmp(gp_snaps[i].name, gp_snap_names[1])) {
+ ASSERT_EQ(1U, gp_snaps[i].image_snaps_count);
+ ASSERT_EQ(m_image_name, gp_snaps[i].image_snaps[0].image_name);
+ ASSERT_EQ(rados_ioctx_get_id(ioctx), gp_snaps[i].image_snaps[0].pool_id);
+ } else if (!strcmp(gp_snaps[i].name, gp_snap_names[2])) {
+ ASSERT_EQ(2U, gp_snaps[i].image_snaps_count);
+ for (int j = 0; j < 2; j++) {
+ if (m_image_name == gp_snaps[i].image_snaps[j].image_name) {
+ ASSERT_EQ(rados_ioctx_get_id(ioctx),
+ gp_snaps[i].image_snaps[j].pool_id);
+ } else if (image_name2 == gp_snaps[i].image_snaps[j].image_name) {
+ ASSERT_EQ(rados_ioctx_get_id(ioctx2),
+ gp_snaps[i].image_snaps[j].pool_id);
+ } else {
+ FAIL() << "Unexpected image in group snap: "
+ << gp_snaps[i].image_snaps[j].image_name;
+ }
+ }
+ } else if (!strcmp(gp_snaps[i].name, gp_snap_names[3])) {
+ ASSERT_EQ(1U, gp_snaps[i].image_snaps_count);
+ ASSERT_EQ(image_name2, gp_snaps[i].image_snaps[0].image_name);
+ ASSERT_EQ(rados_ioctx_get_id(ioctx2),
+ gp_snaps[i].image_snaps[0].pool_id);
+ } else {
+ FAIL() << "Unexpected group snap: " << gp_snaps[i].name;
+ }
+ }
+
+ for (const auto& gp_snap_name : gp_snap_names) {
+ ASSERT_EQ(0, rbd_group_snap_remove(ioctx, gp_name, gp_snap_name));
+ }
+ rbd_group_snap_list2_cleanup(gp_snaps, num_snaps);
+ free(gp_snaps);
+ ASSERT_EQ(0, rbd_group_snap_list2(ioctx, gp_name, NULL, &num_snaps));
+ ASSERT_EQ(0U, num_snaps);
+ ASSERT_EQ(0, rbd_group_remove(ioctx, gp_name));
+ rados_ioctx_destroy(ioctx2);
+ rados_ioctx_destroy(ioctx);
+ ASSERT_EQ(0, rados_pool_delete(_cluster, pool_name2.c_str()));
+}
+
+TEST_F(TestGroup, snap_list2PP)
+{
+ REQUIRE_FORMAT_V2();
+
+ std::string pool_name2 = get_temp_pool_name("test-librbd-");
+ ASSERT_EQ(0, _rados.pool_create(pool_name2.c_str()));
+
+ librados::IoCtx ioctx2;
+ ASSERT_EQ(0, _rados.ioctx_create(pool_name2.c_str(), ioctx2));
+
+ std::string image_name2 = get_temp_image_name();
+ ASSERT_EQ(0, create_image_pp(m_rbd, ioctx2, image_name2.c_str(),
+ m_image_size));
+
+ const char *gp_name = "gp_snaplist2PP";
+ ASSERT_EQ(0, m_rbd.group_create(m_ioctx, gp_name));
+
+ std::vector<librbd::group_snap_info2_t> gp_snaps;
+ ASSERT_EQ(-ENOENT, m_rbd.group_snap_list2(m_ioctx, "absent", &gp_snaps));
+ ASSERT_EQ(0, m_rbd.group_snap_list2(m_ioctx, gp_name, &gp_snaps));
+ ASSERT_EQ(0U, gp_snaps.size());
+
+ const char* const gp_snap_names[] = {
+ "snap_snapshot0", "snap_snapshot1", "snap_snapshot2", "snap_snapshot3"};
+
+ ASSERT_EQ(0, m_rbd.group_snap_create(m_ioctx, gp_name, gp_snap_names[0]));
+
+ ASSERT_EQ(0, m_rbd.group_image_add(m_ioctx, gp_name, m_ioctx,
+ m_image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.group_snap_create(m_ioctx, gp_name, gp_snap_names[1]));
+
+ ASSERT_EQ(0, m_rbd.group_image_add(m_ioctx, gp_name, ioctx2,
+ image_name2.c_str()));
+ ASSERT_EQ(0, m_rbd.group_snap_create(m_ioctx, gp_name, gp_snap_names[2]));
+
+ ASSERT_EQ(0, m_rbd.group_image_remove(m_ioctx, gp_name,
+ m_ioctx, m_image_name.c_str()));
+ ASSERT_EQ(0, m_rbd.group_snap_create(m_ioctx, gp_name, gp_snap_names[3]));
+
+ ASSERT_EQ(0, m_rbd.group_snap_list2(m_ioctx, gp_name, &gp_snaps));
+ ASSERT_EQ(4U, gp_snaps.size());
+
+ for (const auto& gp_snap : gp_snaps) {
+ ASSERT_EQ(RBD_GROUP_SNAP_STATE_COMPLETE, gp_snap.state);
+ if (gp_snap.name == gp_snap_names[0]) {
+ ASSERT_EQ(0U, gp_snap.image_snaps.size());
+ } else if (gp_snap.name == gp_snap_names[1]) {
+ ASSERT_EQ(1U, gp_snap.image_snaps.size());
+ ASSERT_EQ(m_image_name, gp_snap.image_snaps[0].image_name);
+ ASSERT_EQ(m_ioctx.get_id(), gp_snap.image_snaps[0].pool_id);
+ } else if (gp_snap.name == gp_snap_names[2]) {
+ ASSERT_EQ(2U, gp_snap.image_snaps.size());
+ for (const auto& image_snap : gp_snap.image_snaps) {
+ if (image_snap.image_name == m_image_name) {
+ ASSERT_EQ(m_ioctx.get_id(), image_snap.pool_id);
+ } else if (image_snap.image_name == image_name2) {
+ ASSERT_EQ(ioctx2.get_id(), image_snap.pool_id);
+ } else {
+ FAIL() << "Unexpected image in group snap: "
+ << image_snap.image_name;
+ }
+ }
+ } else if (gp_snap.name == gp_snap_names[3]) {
+ ASSERT_EQ(1U, gp_snap.image_snaps.size());
+ ASSERT_EQ(image_name2, gp_snap.image_snaps[0].image_name);
+ ASSERT_EQ(ioctx2.get_id(), gp_snap.image_snaps[0].pool_id);
+ } else {
+ FAIL() << "Unexpected group snap: " << gp_snap.name;
+ }
+ }
+
+ for (const auto& gp_snap_name : gp_snap_names) {
+ ASSERT_EQ(0, m_rbd.group_snap_remove(m_ioctx, gp_name, gp_snap_name));
+ }
+ std::vector<librbd::group_snap_info2_t> gp_snaps2;
+ ASSERT_EQ(0, m_rbd.group_snap_list2(m_ioctx, gp_name, &gp_snaps2));
+ ASSERT_EQ(0U, gp_snaps2.size());
+ ASSERT_EQ(0, m_rbd.group_remove(m_ioctx, gp_name));
+ ASSERT_EQ(0, _rados.pool_delete(pool_name2.c_str()));
+}
LIBRADOS_OP_FLAG_FADVISE_NOCACHE,
LIBRADOS_OP_FLAG_FADVISE_RANDOM)
from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
- ImageBusy, ImageHasSnapshots, ReadOnlyImage,
+ ImageBusy, ImageHasSnapshots, ReadOnlyImage, ObjectNotFound,
FunctionNotSupported, ArgumentOutOfRange,
ECANCELED, OperationCanceled,
DiskQuotaExceeded, ConnectionShutdown, PermissionError,
RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR,
RBD_WRITE_ZEROES_FLAG_THICK_PROVISION,
RBD_ENCRYPTION_FORMAT_LUKS1, RBD_ENCRYPTION_FORMAT_LUKS2,
- RBD_ENCRYPTION_FORMAT_LUKS)
+ RBD_ENCRYPTION_FORMAT_LUKS, RBD_GROUP_SNAP_STATE_COMPLETE)
rados = None
ioctx = None
eq([], RBD().group_list(ioctx))
class TestGroups(object):
+ img_snap_keys = ['image_name', 'pool_id', 'snap_id']
+ gp_snap_keys = ['id', 'image_snap_name', 'image_snaps', 'name', 'state']
def setup_method(self, method):
global snap_name
with Image(ioctx, image_name) as image:
eq(0, image.op_features() & RBD_OPERATION_FEATURE_GROUP)
+ def test_group_snap_get_info(self):
+ self.image_names.append(create_image())
+ self.image_names.sort()
+ self.group.add_image(ioctx, self.image_names[0])
+ self.group.add_image(ioctx, self.image_names[1])
+ pool_id = ioctx.get_pool_id()
+
+ assert_raises(ObjectNotFound, self.group.get_snap_info, "")
+
+ self.group.create_snap(snap_name)
+ snap_info_dict = self.group.get_snap_info(snap_name)
+ image_names = []
+ assert sorted(snap_info_dict.keys()) == self.gp_snap_keys
+ assert snap_info_dict['name'] == snap_name
+ assert snap_info_dict['state'] == RBD_GROUP_SNAP_STATE_COMPLETE
+ for image_snap in snap_info_dict['image_snaps']:
+ assert sorted(image_snap.keys()) == self.img_snap_keys
+ assert image_snap['pool_id'] == pool_id
+ image_names.append(image_snap['image_name'])
+ with Image(ioctx, image_snap['image_name']) as member_image:
+ snaps = [snap for snap in member_image.list_snaps()]
+ assert len(snaps) == 1
+ assert snaps[0]['name'] == snap_info_dict['image_snap_name']
+ assert snaps[0]['id'] == image_snap['snap_id']
+ assert sorted(image_names) == self.image_names
+
+ self.group.remove_snap(snap_name)
+ assert_raises(ObjectNotFound, self.group.get_snap_info, snap_name)
+
def test_group_snap(self):
global snap_name
eq([], list(self.group.list_snaps()))
eq([], list(self.group.list_snaps()))
def test_group_snap_list_many(self):
+ self.image_names.append(create_image())
+ self.image_names.sort()
+ self.group.add_image(ioctx, self.image_names[0])
+ self.group.add_image(ioctx, self.image_names[1])
+
global snap_name
- eq([], list(self.group.list_snaps()))
+ assert list(self.group.list_snaps()) == []
snap_names = []
for x in range(0, 20):
snap_names.append(snap_name)
self.group.create_snap(snap_name)
snap_name = get_temp_snap_name()
- snap_names.sort()
- answer = [snap['name'] for snap in self.group.list_snaps()]
- answer.sort()
- eq(snap_names, answer)
+ gp_snaps_list = self.group.list_snaps()
+ gp_snap_names = []
+ for gp_snap in gp_snaps_list:
+ assert sorted(gp_snap.keys()) == self.gp_snap_keys
+ gp_snap_names.append(gp_snap['name'])
+ image_names = []
+ for img_snap in gp_snap['image_snaps']:
+ assert sorted(img_snap.keys()) == self.img_snap_keys
+ image_names.append(img_snap['image_name'])
+ assert sorted(image_names) == self.image_names
+ assert sorted(gp_snap_names) == sorted(snap_names)
def test_group_snap_namespace(self):
global snap_name
}
}
+std::string get_group_snap_state_name(rbd_group_snap_state_t state)
+{
+ switch (state) {
+ case RBD_GROUP_SNAP_STATE_INCOMPLETE:
+ return "incomplete";
+ case RBD_GROUP_SNAP_STATE_COMPLETE:
+ return "complete";
+ default:
+ return "unknown (" + stringify(state) + ")";
+ }
+}
+
int execute_create(const po::variables_map &vm,
const std::vector<std::string> &ceph_global_init_args) {
size_t arg_index = 0;
}
librbd::RBD rbd;
- std::vector<librbd::group_snap_info_t> snaps;
-
- r = rbd.group_snap_list(io_ctx, group_name.c_str(), &snaps,
- sizeof(librbd::group_snap_info_t));
-
- if (r == -ENOENT) {
- r = 0;
- }
+ std::vector<librbd::group_snap_info2_t> snaps;
+ r = rbd.group_snap_list2(io_ctx, group_name.c_str(), &snaps);
if (r < 0) {
return r;
}
if (f) {
f->open_array_section("group_snaps");
} else {
+ t.define_column("ID", TextTable::LEFT, TextTable::LEFT);
t.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
- t.define_column("STATUS", TextTable::LEFT, TextTable::RIGHT);
+ t.define_column("STATE", TextTable::LEFT, TextTable::RIGHT);
}
- for (auto i : snaps) {
- std::string snap_name = i.name;
- int state = i.state;
- std::string state_string;
- if (RBD_GROUP_SNAP_STATE_INCOMPLETE == state) {
- state_string = "incomplete";
- } else {
- state_string = "ok";
- }
- if (r < 0) {
- return r;
- }
+ for (const auto& snap : snaps) {
+ auto state_string = get_group_snap_state_name(snap.state);
if (f) {
f->open_object_section("group_snap");
- f->dump_string("snapshot", snap_name);
+ f->dump_string("id", snap.id);
+ f->dump_string("snapshot", snap.name);
f->dump_string("state", state_string);
f->close_section();
} else {
- t << snap_name << state_string << TextTable::endrow;
+ t << snap.id << snap.name << state_string << TextTable::endrow;
}
}
return 0;
}
+int execute_group_snap_info(const po::variables_map &vm,
+ const std::vector<std::string> &ceph_global_args) {
+ size_t arg_index = 0;
+ std::string pool_name;
+ std::string namespace_name;
+ std::string group_name;
+ std::string group_snap_name;
+
+ int r = utils::get_pool_generic_snapshot_names(
+ vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, at::POOL_NAME, &pool_name,
+ &namespace_name, GROUP_NAME, "group", &group_name, &group_snap_name, true,
+ utils::SNAPSHOT_PRESENCE_REQUIRED, utils::SPEC_VALIDATION_FULL);
+ if (r < 0) {
+ return r;
+ }
+
+ at::Format::Formatter formatter;
+ r = utils::get_formatter(vm, &formatter);
+ if (r < 0) {
+ return r;
+ }
+ Formatter *f = formatter.get();
+
+ librados::Rados rados;
+ librados::IoCtx io_ctx;
+ r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
+ if (r < 0) {
+ return r;
+ }
+
+ librbd::RBD rbd;
+ librbd::group_snap_info2_t group_snap;
+ r = rbd.group_snap_get_info(io_ctx, group_name.c_str(),
+ group_snap_name.c_str(), &group_snap);
+ if (r < 0) {
+ std::cerr << "rbd: failed to show group snapshot: "
+ << cpp_strerror(r) << std::endl;
+ return r;
+ }
+
+ auto state_string = get_group_snap_state_name(group_snap.state);
+ if (f) {
+ f->open_object_section("group_snapshot");
+ f->dump_string("id", group_snap.id);
+ f->dump_string("name", group_snap.name);
+ f->dump_string("state", state_string);
+ f->dump_string("image_snap_name", group_snap.image_snap_name);
+ f->open_array_section("images");
+ } else {
+ std::cout << "rbd group snapshot '" << group_snap.name << "':\n"
+ << "\tid: " << group_snap.id << std::endl
+ << "\tstate: " << state_string << std::endl
+ << "\timage snap: " << group_snap.image_snap_name << std::endl
+ << "\timages:" << std::endl;
+ }
+
+ std::sort(group_snap.image_snaps.begin(), group_snap.image_snaps.end(),
+ [](const librbd::group_image_snap_info_t& lhs,
+ const librbd::group_image_snap_info_t& rhs) {
+ if (lhs.pool_id != rhs.pool_id) {
+ return lhs.pool_id < rhs.pool_id;
+ }
+ return lhs.image_name < rhs.image_name;
+ }
+ );
+
+ for (const auto& image_snap : group_snap.image_snaps) {
+ std::string pool_name;
+ r = rados.pool_reverse_lookup(image_snap.pool_id, &pool_name);
+ if (r == -ENOENT) {
+ pool_name = "<missing image pool " + stringify(image_snap.pool_id) + ">";
+ } else if (r < 0) {
+ std::cerr << "rbd: error looking up pool name for pool_id="
+ << image_snap.pool_id << ": " << cpp_strerror(r) << std::endl;
+ return r;
+ }
+
+ if (f) {
+ f->open_object_section("image");
+ f->dump_string("pool_name", pool_name);
+ f->dump_string("namespace", io_ctx.get_namespace());
+ f->dump_string("image_name", image_snap.image_name);
+ f->dump_int("snap_id", image_snap.snap_id);
+ f->close_section();
+ } else {
+ std::cout << "\t\t" << pool_name << "/";
+ if (!io_ctx.get_namespace().empty()) {
+ std::cout << io_ctx.get_namespace() << "/";
+ }
+ std::cout << image_snap.image_name << " (snap id: " << image_snap.snap_id
+ << ")" << std::endl;
+ }
+ }
+
+ if (f) {
+ f->close_section();
+ f->close_section();
+ f->flush(std::cout);
+ }
+
+ return 0;
+}
+
int execute_group_snap_rollback(const po::variables_map &vm,
const std::vector<std::string> &global_args) {
size_t arg_index = 0;
false);
}
+void get_group_snap_info_arguments(po::options_description *positional,
+ po::options_description *options) {
+ add_group_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE,
+ true);
+ at::add_format_options(options);
+}
+
void get_group_snap_rollback_arguments(po::options_description *positional,
po::options_description *options) {
at::add_no_progress_option(options);
{"group", "snap", "list"}, {"group", "snap", "ls"},
"List snapshots of a group.",
"", &get_group_snap_list_arguments, &execute_group_snap_list);
+Shell::Action action_group_snap_info(
+ {"group", "snap", "info"}, {},
+ "Show information about a group snapshot.",
+ "", &get_group_snap_info_arguments, &execute_group_snap_info);
Shell::Action action_group_snap_rollback(
{"group", "snap", "rollback"}, {},
"Rollback group to snapshot.",