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
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

View File

@ -367,6 +367,9 @@ Commands
: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.

View File

@ -25,7 +25,7 @@ list_groups()
check_group_exists()
{
local group_name=$1
list_groups | grep $group_name
list_groups | grep -w $group_name
}
remove_group()
@ -165,7 +165,7 @@ check_snapshot_in_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()
@ -182,12 +182,43 @@ check_snapshot_not_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"
@ -217,23 +248,24 @@ echo "PASSED"
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"
@ -247,6 +279,7 @@ create_group $group
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

View File

@ -249,11 +249,27 @@ typedef enum {
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;
@ -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,
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);

View File

@ -161,11 +161,26 @@ namespace librbd {
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
@ -443,6 +458,11 @@ public:
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,

View File

@ -53,26 +53,16 @@ snap_t get_group_snap_id(I* ictx,
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;
@ -476,6 +466,48 @@ int notify_quiesce(std::vector<I*> &ictxs, ProgressContext &prog_ctx,
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>
@ -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);
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;
@ -1158,7 +1190,7 @@ int Group<I>::snap_remove(librados::IoCtx& group_ioctx, const char *group_name,
}
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;
}
@ -1199,7 +1231,7 @@ int Group<I>::snap_rename(librados::IoCtx& group_ioctx, const char *group_name,
}
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;
}
@ -1228,22 +1260,78 @@ int Group<I>::snap_rename(librados::IoCtx& group_ioctx, const char *group_name,
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)
{
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) {
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<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)
{
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;
}
@ -1264,7 +1352,7 @@ int Group<I>::snap_rollback(librados::IoCtx& group_ioctx,
}
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;
}

View File

@ -47,7 +47,10 @@ struct Group {
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);

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;
}
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());
@ -1436,11 +1455,34 @@ namespace librbd {
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)
@ -7230,6 +7272,34 @@ extern "C" int rbd_group_snap_rename(rados_ioctx_t group_p,
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,
@ -7251,7 +7321,7 @@ extern "C" int rbd_group_snap_list(rados_ioctx_t group_p,
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) {
@ -7293,6 +7363,40 @@ extern "C" int rbd_group_snap_list_cleanup(rbd_group_snap_info_t *snaps,
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)

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_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"
@ -700,14 +709,18 @@ cdef extern from "rbd/librbd.h" nogil:
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)

View File

@ -228,9 +228,18 @@ cdef nogil:
_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"
@ -886,14 +895,19 @@ cdef nogil:
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_list_cleanup(rbd_group_snap_info_t *snaps,
size_t group_snap_info_size, size_t len):
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_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):

View File

@ -2778,6 +2778,71 @@ cdef class Group(object):
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.
@ -2802,7 +2867,7 @@ cdef class Group(object):
def list_snaps(self):
"""
Iterate over the images of a group.
Iterate over the snapshots of a group.
:returns: :class:`GroupSnapIterator`
"""
@ -5934,47 +5999,72 @@ cdef class GroupSnapIterator(object):
"""
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
* ``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 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)

View File

@ -53,6 +53,7 @@
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.
@ -1070,6 +1071,27 @@
--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>]

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_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_RANDOM)
from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
ImageBusy, ImageHasSnapshots, ReadOnlyImage,
ImageBusy, ImageHasSnapshots, ReadOnlyImage, ObjectNotFound,
FunctionNotSupported, ArgumentOutOfRange,
ECANCELED, OperationCanceled,
DiskQuotaExceeded, ConnectionShutdown, PermissionError,
@ -49,7 +49,7 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
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
@ -2809,6 +2809,8 @@ def test_list_groups_after_removed():
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
@ -2882,6 +2884,35 @@ class TestGroups(object):
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()))
@ -2919,18 +2950,30 @@ class TestGroups(object):
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

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,
const std::vector<std::string> &ceph_global_init_args) {
size_t arg_index = 0;
@ -705,14 +717,8 @@ int execute_group_snap_list(const po::variables_map &vm,
}
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;
}
@ -721,29 +727,21 @@ int execute_group_snap_list(const po::variables_map &vm,
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;
}
}
@ -756,6 +754,109 @@ int execute_group_snap_list(const po::variables_map &vm,
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;
@ -917,6 +1018,13 @@ void get_group_snap_list_arguments(po::options_description *positional,
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);
@ -964,6 +1072,10 @@ Shell::Action action_group_snap_list(
{"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.",