rbd: add group snap info command

... 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>
This commit is contained in:
Ramana Raja 2024-06-18 17:32:24 -04:00
parent 103cd8e78b
commit e5ccce14c4
15 changed files with 960 additions and 121 deletions

View File

@ -6,6 +6,12 @@
including `timezone.utc` makes it explicit and avoids the potential of the including `timezone.utc` makes it explicit and avoids the potential of the
returned timestamp getting misinterpreted -- in Python 3, many `datetime` returned timestamp getting misinterpreted -- in Python 3, many `datetime`
methods treat "naive" `datetime` objects as local times. 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 >=19.0.0

View File

@ -367,6 +367,9 @@ Commands
:command:`group snap list` *group-spec* :command:`group snap list` *group-spec*
List snapshots of a group. 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* :command:`group snap rm` *group-snap-spec*
Remove a snapshot from a group. Remove a snapshot from a group.

View File

@ -25,7 +25,7 @@ list_groups()
check_group_exists() check_group_exists()
{ {
local group_name=$1 local group_name=$1
list_groups | grep $group_name list_groups | grep -w $group_name
} }
remove_group() remove_group()
@ -165,7 +165,7 @@ check_snapshot_in_group()
{ {
local group_name=$1 local group_name=$1
local snap_name=$2 local snap_name=$2
list_snapshots $group_name | grep $snap_name list_snapshots $group_name | grep -w $snap_name
} }
check_snapshots_count_in_group() check_snapshots_count_in_group()
@ -182,12 +182,43 @@ check_snapshot_not_in_group()
{ {
local group_name=$1 local group_name=$1
local snap_name=$2 local snap_name=$2
for v in $(list_snapshots $group_name | awk '{print $1}'); do
if [ "$v" = "$snap_name" ]; then check_group_exists $group_name || return 1
return 1 ! check_snapshot_in_group $group_name $snap_name
fi }
done
return 0 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 consistency group"
@ -217,23 +248,24 @@ echo "PASSED"
echo "TEST: create remove snapshots of consistency group" echo "TEST: create remove snapshots of consistency group"
image="test_image" image="test_image"
group="test_consistency_group" group="test_consistency_group"
snap="group_snap" snaps=("group_snap1" "group_snap2" "group_snap3" "group_snap4")
new_snap="new_group_snap"
sec_snap="group_snap2"
create_image $image create_image $image
create_group $group create_group $group
create_snapshot $group ${snaps[0]}
check_snapshot_info $group ${snaps[0]} 0
add_image_to_group $image $group add_image_to_group $image $group
create_snapshot $group $snap create_snapshot $group ${snaps[1]}
check_snapshot_in_group $group $snap check_snapshot_info $group ${snaps[1]} 1
rename_snapshot $group $snap $new_snap rename_snapshot $group ${snaps[1]} ${snaps[2]}
check_snapshot_not_in_group $group $snap check_snapshot_info $group ${snaps[2]} 1
create_snapshot $group $sec_snap check_snapshot_not_in_group $group ${snaps[1]}
check_snapshot_in_group $group $sec_snap create_snapshot $group ${snaps[3]}
rollback_snapshot $group $new_snap check_snapshot_in_group $group ${snaps[3]}
remove_snapshot $group $new_snap rollback_snapshot $group ${snaps[2]}
check_snapshot_not_in_group $group $new_snap remove_snapshot $group ${snaps[2]}
remove_snapshot $group $sec_snap check_snapshot_not_in_group $group ${snaps[2]}
check_snapshot_not_in_group $group $sec_snap remove_snapshot $group ${snaps[3]}
check_snapshot_not_in_group $group ${snaps[3]}
remove_group $group remove_group $group
remove_image $image remove_image $image
echo "PASSED" echo "PASSED"
@ -247,6 +279,7 @@ create_group $group
add_image_to_group $image $group add_image_to_group $image $group
create_snapshots $group $snap 10 create_snapshots $group $snap 10
check_snapshots_count_in_group $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 remove_snapshots $group $snap 10
create_snapshots $group $snap 100 create_snapshots $group $snap 100
check_snapshots_count_in_group $group $snap 100 check_snapshots_count_in_group $group $snap 100

View File

@ -249,11 +249,27 @@ typedef enum {
RBD_GROUP_SNAP_STATE_COMPLETE RBD_GROUP_SNAP_STATE_COMPLETE
} rbd_group_snap_state_t; } 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 { typedef struct {
char *name; char *name;
rbd_group_snap_state_t state; rbd_group_snap_state_t state;
} rbd_group_snap_info_t; } 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 { typedef struct {
int64_t group_pool; int64_t group_pool;
char *group_name; char *group_name;
@ -1491,6 +1507,18 @@ CEPH_RBD_API int rbd_group_snap_list(rados_ioctx_t group_p,
CEPH_RBD_API int rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps, CEPH_RBD_API int rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps,
size_t group_snap_info_size, size_t group_snap_info_size,
size_t num_entries); 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, CEPH_RBD_API int rbd_group_snap_rollback(rados_ioctx_t group_p,
const char *group_name, const char *group_name,
const char *snap_name); const char *snap_name);

View File

@ -161,11 +161,26 @@ namespace librbd {
typedef rbd_group_snap_state_t group_snap_state_t; 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 { typedef struct {
std::string name; std::string name;
group_snap_state_t state; group_snap_state_t state;
} group_snap_info_t; } 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; typedef rbd_image_info_t image_info_t;
class CEPH_RBD_API ProgressContext class CEPH_RBD_API ProgressContext
@ -443,6 +458,11 @@ public:
int group_snap_list(IoCtx& group_ioctx, const char *group_name, int group_snap_list(IoCtx& group_ioctx, const char *group_name,
std::vector<group_snap_info_t> *snaps, std::vector<group_snap_info_t> *snaps,
size_t group_snap_info_size); 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, int group_snap_rollback(IoCtx& io_ctx, const char *group_name,
const char *snap_name); const char *snap_name);
int group_snap_rollback_with_progress(IoCtx& io_ctx, const char *group_name, int group_snap_rollback_with_progress(IoCtx& io_ctx, const char *group_name,

View File

@ -53,26 +53,16 @@ snap_t get_group_snap_id(I* ictx,
return CEPH_NOSNAP; 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) std::vector<cls::rbd::GroupSnapshot> *cls_snaps)
{ {
CephContext *cct = (CephContext *)group_ioctx.cct(); 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); string group_header_oid = util::group_header_name(group_id);
const int max_read = 1024; const int max_read = 1024;
cls::rbd::GroupSnapshot snap_last; cls::rbd::GroupSnapshot snap_last;
int r;
for (;;) { for (;;) {
vector<cls::rbd::GroupSnapshot> snaps_page; vector<cls::rbd::GroupSnapshot> snaps_page;
@ -476,6 +466,48 @@ int notify_quiesce(std::vector<I*> &ictxs, ProgressContext &prog_ctx,
return ret_code; 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 } // anonymous namespace
template <typename I> template <typename I>
@ -560,7 +592,7 @@ int Group<I>::remove(librados::IoCtx& io_ctx, const char *group_name)
string group_header_oid = util::group_header_name(group_id); string group_header_oid = util::group_header_name(group_id);
std::vector<cls::rbd::GroupSnapshot> snaps; 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) { if (r < 0 && r != -ENOENT) {
lderr(cct) << "error listing group snapshots" << dendl; lderr(cct) << "error listing group snapshots" << dendl;
return r; return r;
@ -1158,7 +1190,7 @@ int Group<I>::snap_remove(librados::IoCtx& group_ioctx, const char *group_name,
} }
std::vector<cls::rbd::GroupSnapshot> snaps; 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) { if (r < 0) {
return r; return r;
} }
@ -1199,7 +1231,7 @@ int Group<I>::snap_rename(librados::IoCtx& group_ioctx, const char *group_name,
} }
std::vector<cls::rbd::GroupSnapshot> group_snaps; 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) { if (r < 0) {
return r; return r;
} }
@ -1228,22 +1260,78 @@ int Group<I>::snap_rename(librados::IoCtx& group_ioctx, const char *group_name,
template <typename I> template <typename I>
int Group<I>::snap_list(librados::IoCtx& group_ioctx, const char *group_name, 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)
{ {
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;
}
std::vector<cls::rbd::GroupSnapshot> cls_group_snaps;
r = group_snap_list(group_ioctx, group_id, &cls_group_snaps);
if (r < 0) { if (r < 0) {
return r; return r;
} }
for (auto snap : cls_snaps) { std::vector<group_snap_info2_t> group_snaps_tmp(cls_group_snaps.size());
snaps->push_back( for (size_t i = 0; i < cls_group_snaps.size(); i++) {
group_snap_info_t { r = GroupSnapshot_to_group_snap_info2(group_ioctx, group_id,
snap.name, cls_group_snaps[i],
static_cast<group_snap_state_t>(snap.state)}); &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)
{
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;
}
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; return 0;
} }
@ -1264,7 +1352,7 @@ int Group<I>::snap_rollback(librados::IoCtx& group_ioctx,
} }
std::vector<cls::rbd::GroupSnapshot> snaps; 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) { if (r < 0) {
return r; return r;
} }

View File

@ -47,7 +47,10 @@ struct Group {
static int snap_rename(librados::IoCtx& group_ioctx, const char *group_name, static int snap_rename(librados::IoCtx& group_ioctx, const char *group_name,
const char *old_snap_name, const char *new_snap_name); const char *old_snap_name, const char *new_snap_name);
static int snap_list(librados::IoCtx& group_ioctx, const char *group_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, static int snap_rollback(librados::IoCtx& group_ioctx,
const char *group_name, const char *snap_name, const char *group_name, const char *snap_name,
ProgressContext& pctx); ProgressContext& pctx);

View File

@ -263,12 +263,31 @@ void group_info_cpp_to_c(const librbd::group_info_t &cpp_info,
c_info->pool = cpp_info.pool; 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) { rbd_group_snap_info_t *c_info) {
c_info->name = strdup(cpp_info.name.c_str()); c_info->name = strdup(cpp_info.name.c_str());
c_info->state = cpp_info.state; 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, void mirror_image_info_cpp_to_c(const librbd::mirror_image_info_t &cpp_info,
rbd_mirror_image_info_t *c_info) { rbd_mirror_image_info_t *c_info) {
c_info->global_id = strdup(cpp_info.global_id.c_str()); c_info->global_id = strdup(cpp_info.global_id.c_str());
@ -1436,11 +1455,34 @@ namespace librbd {
return -ERANGE; 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); tracepoint(librbd, group_snap_list_exit, r);
return 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, int RBD::group_snap_rename(IoCtx& group_ioctx, const char *group_name,
const char *old_snap_name, const char *old_snap_name,
const char *new_snap_name) const char *new_snap_name)
@ -7230,6 +7272,34 @@ extern "C" int rbd_group_snap_rename(rados_ioctx_t group_p,
return r; 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, extern "C" int rbd_group_snap_list(rados_ioctx_t group_p,
const char *group_name, const char *group_name,
rbd_group_snap_info_t *snaps, rbd_group_snap_info_t *snaps,
@ -7251,7 +7321,7 @@ extern "C" int rbd_group_snap_list(rados_ioctx_t group_p,
return -ERANGE; 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); int r = librbd::api::Group<>::snap_list(group_ioctx, group_name, &cpp_snaps);
if (r == -ENOENT) { if (r == -ENOENT) {
@ -7293,6 +7363,40 @@ extern "C" int rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps,
return 0; 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, extern "C" int rbd_group_snap_rollback(rados_ioctx_t group_p,
const char *group_name, const char *group_name,
const char *snap_name) const char *snap_name)

View File

@ -224,9 +224,18 @@ cdef extern from "rbd/librbd.h" nogil:
_RBD_GROUP_SNAP_STATE_INCOMPLETE "RBD_GROUP_SNAP_STATE_INCOMPLETE" _RBD_GROUP_SNAP_STATE_INCOMPLETE "RBD_GROUP_SNAP_STATE_INCOMPLETE"
_RBD_GROUP_SNAP_STATE_COMPLETE "RBD_GROUP_SNAP_STATE_COMPLETE" _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 *name
char *image_snap_name
rbd_group_snap_state_t state 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: ctypedef enum rbd_image_migration_state_t:
_RBD_IMAGE_MIGRATION_STATE_UNKNOWN "RBD_IMAGE_MIGRATION_STATE_UNKNOWN" _RBD_IMAGE_MIGRATION_STATE_UNKNOWN "RBD_IMAGE_MIGRATION_STATE_UNKNOWN"
@ -700,14 +709,18 @@ cdef extern from "rbd/librbd.h" nogil:
const char *old_snap_name, const char *old_snap_name,
const char *new_snap_name) const char *new_snap_name)
int rbd_group_snap_list(rados_ioctx_t group_p, int rbd_group_snap_get_info(rados_ioctx_t group_p, const char *group_name,
const char *group_name, const char *snap_name,
rbd_group_snap_info_t *snaps, rbd_group_snap_info2_t *group_snap)
size_t group_snap_info_size, void rbd_group_snap_get_info_cleanup(rbd_group_snap_info2_t *group_snap)
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)
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, int rbd_group_snap_rollback(rados_ioctx_t group_p, const char *group_name,
const char *snap_name) const char *snap_name)

View File

@ -228,9 +228,18 @@ cdef nogil:
_RBD_GROUP_SNAP_STATE_INCOMPLETE "RBD_GROUP_SNAP_STATE_INCOMPLETE" _RBD_GROUP_SNAP_STATE_INCOMPLETE "RBD_GROUP_SNAP_STATE_INCOMPLETE"
_RBD_GROUP_SNAP_STATE_COMPLETE "RBD_GROUP_SNAP_STATE_COMPLETE" _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 *name
char *image_snap_name
rbd_group_snap_state_t state 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: ctypedef enum rbd_image_migration_state_t:
_RBD_IMAGE_MIGRATION_STATE_UNKNOWN "RBD_IMAGE_MIGRATION_STATE_UNKNOWN" _RBD_IMAGE_MIGRATION_STATE_UNKNOWN "RBD_IMAGE_MIGRATION_STATE_UNKNOWN"
@ -886,14 +895,19 @@ cdef nogil:
const char *old_snap_name, const char *old_snap_name,
const char *new_snap_name): const char *new_snap_name):
pass pass
int rbd_group_snap_list(rados_ioctx_t group_p, int rbd_group_snap_list2(rados_ioctx_t group_p,
const char *group_name, const char *group_name,
rbd_group_snap_info_t *snaps, rbd_group_snap_info2_t *snaps,
size_t group_snap_info_size, size_t *snaps_size):
size_t *snaps_size):
pass pass
void rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps, void rbd_group_snap_list2_cleanup(rbd_group_snap_info2_t *snaps,
size_t group_snap_info_size, size_t len): 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_get_info_cleanup(rbd_group_snap_info2_t *group_snap):
pass pass
int rbd_group_snap_rollback(rados_ioctx_t group_p, const char *group_name, int rbd_group_snap_rollback(rados_ioctx_t group_p, const char *group_name,
const char *snap_name): const char *snap_name):

View File

@ -2778,6 +2778,71 @@ cdef class Group(object):
if ret != 0: if ret != 0:
raise make_ex(ret, 'error removing group snapshot', group_errno_to_exception) 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): def rename_snap(self, old_snap_name, new_snap_name):
""" """
Rename group's snapshot. Rename group's snapshot.
@ -2802,7 +2867,7 @@ cdef class Group(object):
def list_snaps(self): def list_snaps(self):
""" """
Iterate over the images of a group. Iterate over the snapshots of a group.
:returns: :class:`GroupSnapIterator` :returns: :class:`GroupSnapIterator`
""" """
@ -5934,47 +5999,72 @@ cdef class GroupSnapIterator(object):
""" """
Iterator over snaps specs for a group. 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: Keys are:
* ``name`` (str) - name of the snapshot * ``id`` (str) - ID of the group snapshot
* ``state`` (int) - state of the 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
""" """
cdef rbd_group_snap_info_t *snaps cdef rbd_group_snap_info2_t *group_snaps
cdef size_t num_snaps cdef size_t num_group_snaps
cdef object group cdef object group
def __init__(self, Group group): def __init__(self, Group group):
self.group = group self.group = group
self.snaps = NULL self.group_snaps = NULL
self.num_snaps = 10 self.num_group_snaps = 10
while True: while True:
self.snaps = <rbd_group_snap_info_t*>realloc_chk(self.snaps, self.group_snaps = <rbd_group_snap_info2_t*>realloc_chk(
self.num_snaps * self.group_snaps, self.num_group_snaps * sizeof(rbd_group_snap_info2_t))
sizeof(rbd_group_snap_info_t))
with nogil: with nogil:
ret = rbd_group_snap_list(group._ioctx, group._name, self.snaps, ret = rbd_group_snap_list2(group._ioctx, group._name, self.group_snaps,
sizeof(rbd_group_snap_info_t), &self.num_group_snaps)
&self.num_snaps)
if ret >= 0: if ret >= 0:
break break
elif ret != -errno.ERANGE: elif ret != -errno.ERANGE:
raise make_ex(ret, 'error listing snapshots for group %s' % group.name, group_errno_to_exception) raise make_ex(ret, 'error listing snapshots for group %s' % group.name, group_errno_to_exception)
def __iter__(self): 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 { yield {
'name' : decode_cstr(self.snaps[i].name), 'id': decode_cstr(group_snap.id),
'state' : self.snaps[i].state, '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): def __dealloc__(self):
if self.snaps: if self.group_snaps:
rbd_group_snap_list_cleanup(self.snaps, rbd_group_snap_list2_cleanup(self.group_snaps,
sizeof(rbd_group_snap_info_t), self.num_group_snaps)
self.num_snaps) free(self.group_snaps)
free(self.snaps)

View File

@ -53,6 +53,7 @@
group remove (group rm) Delete a group. group remove (group rm) Delete a group.
group rename Rename a group within pool. group rename Rename a group within pool.
group snap create Make a snapshot of a group. 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 list (... ls) List snapshots of a group.
group snap remove (... rm) Remove a snapshot from a group. group snap remove (... rm) Remove a snapshot from a group.
group snap rename Rename group's snapshot. group snap rename Rename group's snapshot.
@ -1070,6 +1071,27 @@
--skip-quiesce do not run quiesce hooks --skip-quiesce do not run quiesce hooks
--ignore-quiesce-error ignore quiesce hook error --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 rbd help group snap list
usage: rbd group snap list [--format <format>] [--pretty-format] usage: rbd group snap list [--format <format>] [--pretty-format]
[--pool <pool>] [--namespace <namespace>] [--pool <pool>] [--namespace <namespace>]

View File

@ -489,3 +489,263 @@ TEST_F(TestGroup, add_snapshotPP)
ASSERT_EQ(0, rbd.group_snap_remove(ioctx, group_name, snap_name)); ASSERT_EQ(0, rbd.group_snap_remove(ioctx, group_name, snap_name));
ASSERT_EQ(0, rbd.group_remove(ioctx, group_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()));
}

View File

@ -20,7 +20,7 @@ from rados import (Rados,
LIBRADOS_OP_FLAG_FADVISE_NOCACHE, LIBRADOS_OP_FLAG_FADVISE_NOCACHE,
LIBRADOS_OP_FLAG_FADVISE_RANDOM) LIBRADOS_OP_FLAG_FADVISE_RANDOM)
from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists, from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
ImageBusy, ImageHasSnapshots, ReadOnlyImage, ImageBusy, ImageHasSnapshots, ReadOnlyImage, ObjectNotFound,
FunctionNotSupported, ArgumentOutOfRange, FunctionNotSupported, ArgumentOutOfRange,
ECANCELED, OperationCanceled, ECANCELED, OperationCanceled,
DiskQuotaExceeded, ConnectionShutdown, PermissionError, DiskQuotaExceeded, ConnectionShutdown, PermissionError,
@ -49,7 +49,7 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR, RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR,
RBD_WRITE_ZEROES_FLAG_THICK_PROVISION, RBD_WRITE_ZEROES_FLAG_THICK_PROVISION,
RBD_ENCRYPTION_FORMAT_LUKS1, RBD_ENCRYPTION_FORMAT_LUKS2, RBD_ENCRYPTION_FORMAT_LUKS1, RBD_ENCRYPTION_FORMAT_LUKS2,
RBD_ENCRYPTION_FORMAT_LUKS) RBD_ENCRYPTION_FORMAT_LUKS, RBD_GROUP_SNAP_STATE_COMPLETE)
rados = None rados = None
ioctx = None ioctx = None
@ -2809,6 +2809,8 @@ def test_list_groups_after_removed():
eq([], RBD().group_list(ioctx)) eq([], RBD().group_list(ioctx))
class TestGroups(object): 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): def setup_method(self, method):
global snap_name global snap_name
@ -2882,6 +2884,35 @@ class TestGroups(object):
with Image(ioctx, image_name) as image: with Image(ioctx, image_name) as image:
eq(0, image.op_features() & RBD_OPERATION_FEATURE_GROUP) 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): def test_group_snap(self):
global snap_name global snap_name
eq([], list(self.group.list_snaps())) eq([], list(self.group.list_snaps()))
@ -2919,18 +2950,30 @@ class TestGroups(object):
eq([], list(self.group.list_snaps())) eq([], list(self.group.list_snaps()))
def test_group_snap_list_many(self): 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 global snap_name
eq([], list(self.group.list_snaps())) assert list(self.group.list_snaps()) == []
snap_names = [] snap_names = []
for x in range(0, 20): for x in range(0, 20):
snap_names.append(snap_name) snap_names.append(snap_name)
self.group.create_snap(snap_name) self.group.create_snap(snap_name)
snap_name = get_temp_snap_name() snap_name = get_temp_snap_name()
snap_names.sort() gp_snaps_list = self.group.list_snaps()
answer = [snap['name'] for snap in self.group.list_snaps()] gp_snap_names = []
answer.sort() for gp_snap in gp_snaps_list:
eq(snap_names, answer) 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): def test_group_snap_namespace(self):
global snap_name global snap_name

View File

@ -85,6 +85,18 @@ void add_group_spec_options(po::options_description *pos,
} }
} }
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, int execute_create(const po::variables_map &vm,
const std::vector<std::string> &ceph_global_init_args) { const std::vector<std::string> &ceph_global_init_args) {
size_t arg_index = 0; size_t arg_index = 0;
@ -705,14 +717,8 @@ int execute_group_snap_list(const po::variables_map &vm,
} }
librbd::RBD rbd; librbd::RBD rbd;
std::vector<librbd::group_snap_info_t> snaps; std::vector<librbd::group_snap_info2_t> snaps;
r = rbd.group_snap_list2(io_ctx, group_name.c_str(), &snaps);
r = rbd.group_snap_list(io_ctx, group_name.c_str(), &snaps,
sizeof(librbd::group_snap_info_t));
if (r == -ENOENT) {
r = 0;
}
if (r < 0) { if (r < 0) {
return r; return r;
} }
@ -721,29 +727,21 @@ int execute_group_snap_list(const po::variables_map &vm,
if (f) { if (f) {
f->open_array_section("group_snaps"); f->open_array_section("group_snaps");
} else { } else {
t.define_column("ID", TextTable::LEFT, TextTable::LEFT);
t.define_column("NAME", 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) { for (const auto& snap : snaps) {
std::string snap_name = i.name; auto state_string = get_group_snap_state_name(snap.state);
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;
}
if (f) { if (f) {
f->open_object_section("group_snap"); 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->dump_string("state", state_string);
f->close_section(); f->close_section();
} else { } else {
t << snap_name << state_string << TextTable::endrow; t << snap.id << snap.name << state_string << TextTable::endrow;
} }
} }
@ -756,6 +754,109 @@ int execute_group_snap_list(const po::variables_map &vm,
return 0; 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, int execute_group_snap_rollback(const po::variables_map &vm,
const std::vector<std::string> &global_args) { const std::vector<std::string> &global_args) {
size_t arg_index = 0; size_t arg_index = 0;
@ -917,6 +1018,13 @@ void get_group_snap_list_arguments(po::options_description *positional,
false); 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, void get_group_snap_rollback_arguments(po::options_description *positional,
po::options_description *options) { po::options_description *options) {
at::add_no_progress_option(options); at::add_no_progress_option(options);
@ -964,6 +1072,10 @@ Shell::Action action_group_snap_list(
{"group", "snap", "list"}, {"group", "snap", "ls"}, {"group", "snap", "list"}, {"group", "snap", "ls"},
"List snapshots of a group.", "List snapshots of a group.",
"", &get_group_snap_list_arguments, &execute_group_snap_list); "", &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( Shell::Action action_group_snap_rollback(
{"group", "snap", "rollback"}, {}, {"group", "snap", "rollback"}, {},
"Rollback group to snapshot.", "Rollback group to snapshot.",