mirror of
https://github.com/ceph/ceph
synced 2025-03-24 19:27:56 +00:00
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:
parent
103cd8e78b
commit
e5ccce14c4
@ -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
|
||||
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
@ -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>]
|
||||
|
@ -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()));
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.",
|
||||
|
Loading…
Reference in New Issue
Block a user