return 0;
}
+int get_snapshot_timestamp(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ uint64_t snap_id;
+
+ bufferlist::iterator iter = in->begin();
+ try {
+ ::decode(snap_id, iter);
+ } catch (const buffer::error &err) {
+ return -EINVAL;
+ }
+
+ CLS_LOG(20, "get_snapshot_timestamp snap_id=%llu", (unsigned long long)snap_id);
+
+ if (snap_id == CEPH_NOSNAP) {
+ return -EINVAL;
+ }
+
+ cls_rbd_snap snap;
+ string snapshot_key;
+ key_from_snap_id(snap_id, &snapshot_key);
+ int r = read_key(hctx, snapshot_key, &snap);
+ if (r < 0) {
+ return r;
+ }
+
+ ::encode(snap.timestamp, *out);
+ return 0;
+}
+
/**
* Retrieve namespace of a snapshot.
*
cls_method_handle_t h_get_data_pool;
cls_method_handle_t h_get_snapshot_name;
cls_method_handle_t h_get_snapshot_namespace;
+ cls_method_handle_t h_get_snapshot_timestamp;
cls_method_handle_t h_snapshot_add;
cls_method_handle_t h_snapshot_remove;
cls_method_handle_t h_snapshot_rename;
cls_register_cxx_method(h_class, "get_snapshot_namespace",
CLS_METHOD_RD,
get_snapshot_namespace, &h_get_snapshot_namespace);
+ cls_register_cxx_method(h_class, "get_snapshot_timestamp",
+ CLS_METHOD_RD,
+ get_snapshot_timestamp, &h_get_snapshot_timestamp);
cls_register_cxx_method(h_class, "snapshot_add",
CLS_METHOD_RD | CLS_METHOD_WR,
snapshot_add, &h_snapshot_add);
void snapshot_list_start(librados::ObjectReadOperation *op,
const std::vector<snapid_t> &ids) {
- for (vector<snapid_t>::const_iterator it = ids.begin();
- it != ids.end(); ++it) {
- snapid_t snap_id = it->val;
+ for (auto snap_id : ids) {
bufferlist bl1, bl2, bl3, bl4;
::encode(snap_id, bl1);
op->exec("rbd", "get_snapshot_name", bl1);
protection_statuses);
}
- void snap_namespace_list_start(librados::ObjectReadOperation *op,
- const std::vector<snapid_t> &ids)
+ void snapshot_timestamp_list_start(librados::ObjectReadOperation *op,
+ const std::vector<snapid_t> &ids)
{
- for (vector<snapid_t>::const_iterator it = ids.begin();
- it != ids.end(); ++it) {
- snapid_t snap_id = it->val;
+ for (auto snap_id : ids) {
bufferlist bl;
::encode(snap_id, bl);
- op->exec("rbd", "get_snapshot_namespace", bl);
+ op->exec("rbd", "get_snapshot_timestamp", bl);
}
}
- int snap_namespace_list_finish(bufferlist::iterator *it,
- const std::vector<snapid_t> &ids,
- std::vector<cls::rbd::SnapshotNamespace> *namespaces)
+ int snapshot_timestamp_list_finish(bufferlist::iterator *it,
+ const std::vector<snapid_t> &ids,
+ std::vector<utime_t> *timestamps)
+ {
+ timestamps->resize(ids.size());
+ try {
+ for (size_t i = 0; i < timestamps->size(); ++i) {
+ utime_t t;
+ ::decode(t, *it);
+ (*timestamps)[i] = t;
+ }
+ } catch (const buffer::error &err) {
+ return -EBADMSG;
+ }
+ return 0;
+ }
+
+ int snapshot_timestamp_list(librados::IoCtx *ioctx, const std::string &oid,
+ const std::vector<snapid_t> &ids,
+ std::vector<utime_t> *timestamps)
+ {
+ librados::ObjectReadOperation op;
+ snapshot_timestamp_list_start(&op, ids);
+
+ bufferlist out_bl;
+ int r = ioctx->operate(oid, &op, &out_bl);
+ if (r < 0) {
+ return r;
+ }
+
+ bufferlist::iterator it = out_bl.begin();
+ return snapshot_timestamp_list_finish(&it, ids, timestamps);
+ }
+
+ void snapshot_namespace_list_start(librados::ObjectReadOperation *op,
+ const std::vector<snapid_t> &ids)
+ {
+ for (auto snap_id : ids) {
+ bufferlist bl;
+ ::encode(snap_id, bl);
+ op->exec("rbd", "get_snapshot_namespace", bl);
+ }
+ }
+
+ int snapshot_namespace_list_finish(bufferlist::iterator *it,
+ const std::vector<snapid_t> &ids,
+ std::vector<cls::rbd::SnapshotNamespace> *namespaces)
{
namespaces->resize(ids.size());
try {
return 0;
}
- int snap_namespace_list(librados::IoCtx *ioctx, const std::string &oid,
- const std::vector<snapid_t> &ids,
- std::vector<cls::rbd::SnapshotNamespace> *namespaces)
+ int snapshot_namespace_list(librados::IoCtx *ioctx, const std::string &oid,
+ const std::vector<snapid_t> &ids,
+ std::vector<cls::rbd::SnapshotNamespace> *namespaces)
{
librados::ObjectReadOperation op;
- snap_namespace_list_start(&op, ids);
+ snapshot_namespace_list_start(&op, ids);
bufferlist out_bl;
int r = ioctx->operate(oid, &op, &out_bl);
}
bufferlist::iterator it = out_bl.begin();
- return snap_namespace_list_finish(&it, ids, namespaces);
+ return snapshot_namespace_list_finish(&it, ids, namespaces);
}
void old_snapshot_add(librados::ObjectWriteOperation *op,
std::vector<uint64_t> *sizes,
std::vector<parent_info> *parents,
std::vector<uint8_t> *protection_statuses);
+ void snapshot_timestamp_list_start(librados::ObjectReadOperation *op,
+ const std::vector<snapid_t> &ids);
+
+ int snapshot_timestamp_list_finish(bufferlist::iterator *it,
+ const std::vector<snapid_t> &ids,
+ std::vector<utime_t> *timestamps);
+
+ int snapshot_timestamp_list(librados::IoCtx *ioctx, const std::string &oid,
+ const std::vector<snapid_t> &ids,
+ std::vector<utime_t> *timestamps);
+
int snapshot_list(librados::IoCtx *ioctx, const std::string &oid,
const std::vector<snapid_t> &ids,
std::vector<string> *names,
std::vector<parent_info> *parents,
std::vector<uint8_t> *protection_statuses);
- void snap_namespace_list_start(librados::ObjectReadOperation *op,
- const std::vector<snapid_t> &ids);
- int snap_namespace_list_finish(bufferlist::iterator *it,
- const std::vector<snapid_t> &ids,
- std::vector<cls::rbd::SnapshotNamespace> *namespaces);
- int snap_namespace_list(librados::IoCtx *ioctx, const std::string &oid,
- const std::vector<snapid_t> &ids,
- std::vector<cls::rbd::SnapshotNamespace> *namespaces);
+ void snapshot_namespace_list_start(librados::ObjectReadOperation *op,
+ const std::vector<snapid_t> &ids);
+ int snapshot_namespace_list_finish(bufferlist::iterator *it,
+ const std::vector<snapid_t> &ids,
+ std::vector<cls::rbd::SnapshotNamespace> *namespaces);
+ int snapshot_namespace_list(librados::IoCtx *ioctx, const std::string &oid,
+ const std::vector<snapid_t> &ids,
+ std::vector<cls::rbd::SnapshotNamespace> *namespaces);
void get_all_features_start(librados::ObjectReadOperation *op);
int get_all_features_finish(bufferlist::iterator *it,
*/
CEPH_RBD_API int rbd_snap_set_limit(rbd_image_t image, uint64_t limit);
+/**
+ * Get the timestamp of a snapshot for an image.
+ *
+ * @param snap_id the snap id of a snapshot of input image.
+ * @param timestamp the timestamp of input snapshot.
+ * @returns 0 on success, negative error code on failure
+ */
+CEPH_RBD_API int rbd_snap_get_timestamp(rbd_image_t image, uint64_t snap_id, struct timespec *timestamp);
+
CEPH_RBD_API int rbd_snap_set(rbd_image_t image, const char *snapname);
CEPH_RBD_API int rbd_flatten(rbd_image_t image);
int snap_rename(const char *srcname, const char *dstname);
int snap_get_limit(uint64_t *limit);
int snap_set_limit(uint64_t limit);
+ int snap_get_timestamp(uint64_t snap_id, struct timespec *timestamp);
/* I/O */
ssize_t read(uint64_t ofs, size_t len, ceph::bufferlist& bl);
parent_info parent;
uint8_t protection_status;
uint64_t flags;
+ utime_t timestamp;
SnapInfo(std::string _name, const cls::rbd::SnapshotNamespace &_snap_namespace,
uint64_t _size, parent_info _parent,
- uint8_t _protection_status, uint64_t _flags)
+ uint8_t _protection_status, uint64_t _flags, utime_t _timestamp)
: name(_name), snap_namespace(_snap_namespace), size(_size), parent(_parent),
- protection_status(_protection_status), flags(_flags) {}
+ protection_status(_protection_status), flags(_flags), timestamp(_timestamp) {}
};
}
}
//m_snap_namespaces = {m_snap_names.size(), cls::rbd::UserSnapshotNamespace()};
- m_snap_namespaces = std::vector
- <cls::rbd::SnapshotNamespace>(
+ m_snap_namespaces = std::vector<cls::rbd::SnapshotNamespace>(
m_snap_names.size(),
cls::rbd::UserSnapshotNamespace());
+ m_snap_timestamps = std::vector<utime_t>(m_snap_names.size(), utime_t());
+
send_v1_get_locks();
return nullptr;
}
m_snap_sizes.clear();
m_snap_parents.clear();
m_snap_protection.clear();
+ m_snap_timestamps.clear();
send_v2_refresh_parent();
return;
}
return m_on_finish;
}
+ send_v2_get_snap_timestamps();
+ return nullptr;
+}
+
+template <typename I>
+void RefreshRequest<I>::send_v2_get_snap_timestamps() {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << dendl;
+
+ librados::ObjectReadOperation op;
+ cls_client::snapshot_timestamp_list_start(&op, m_snapc.snaps);
+
+ using klass = RefreshRequest<I>;
+ librados::AioCompletion *comp = create_rados_ack_callback<
+ klass, &klass::handle_v2_get_snap_timestamps>(this);
+ m_out_bl.clear();
+ int r = m_image_ctx.md_ctx.aio_operate(m_image_ctx.header_oid, comp, &op,
+ &m_out_bl);
+ assert(r == 0);
+ comp->release();
+}
+
+template <typename I>
+Context *RefreshRequest<I>::handle_v2_get_snap_timestamps(int *result) {
+ CephContext *cct = m_image_ctx.cct;
+ ldout(cct, 10) << this << " " << __func__ << ": " << "r=" << *result << dendl;
+
+ if (*result == 0) {
+ bufferlist::iterator it = m_out_bl.begin();
+ *result = cls_client::snapshot_timestamp_list_finish(&it, m_snapc.snaps, &m_snap_timestamps);
+ }
+ if (*result == -ENOENT) {
+ ldout(cct, 10) << "out-of-sync snapshot state detected" << dendl;
+ send_v2_get_mutable_metadata();
+ return nullptr;
+ } else if (*result == -EOPNOTSUPP) {
+ // Ignore it means no snap timestamps are available
+ } else if (*result < 0) {
+ lderr(cct) << "failed to retrieve snapshots: " << cpp_strerror(*result)
+ << dendl;
+ return m_on_finish;
+ }
+
send_v2_get_snap_namespaces();
return nullptr;
}
ldout(cct, 10) << this << " " << __func__ << dendl;
librados::ObjectReadOperation op;
- cls_client::snap_namespace_list_start(&op, m_snapc.snaps);
+ cls_client::snapshot_namespace_list_start(&op, m_snapc.snaps);
using klass = RefreshRequest<I>;
librados::AioCompletion *comp = create_rados_ack_callback<
if (*result == 0) {
bufferlist::iterator it = m_out_bl.begin();
- *result = cls_client::snap_namespace_list_finish(&it, m_snapc.snaps,
- &m_snap_namespaces);
+ *result = cls_client::snapshot_namespace_list_finish(&it, m_snapc.snaps,
+ &m_snap_namespaces);
}
if (*result == -ENOENT) {
ldout(cct, 10) << "out-of-sync snapshot state detected" << dendl;
}
m_image_ctx.add_snap(m_snap_names[i], m_snap_namespaces[i], m_snapc.snaps[i].val,
- m_snap_sizes[i], parent, protection_status, flags);
+ m_snap_sizes[i], parent, protection_status, flags, m_snap_timestamps[i]);
}
m_image_ctx.snapc = m_snapc;
#include "include/int_types.h"
#include "include/buffer.h"
+#include "include/utime.h"
#include "common/snap_types.h"
#include "cls/lock/cls_lock_types.h"
#include "librbd/ImageCtx.h"
* V2_GET_SNAPSHOTS (skip if no snaps) |
* | |
* v |
+ * V2_GET_SNAP_TIMESTAMPS |
+ * | |
+ * v |
* V2_GET_SNAP_NAMESPACES |
* | |
* v |
std::vector<parent_info> m_snap_parents;
std::vector<uint8_t> m_snap_protection;
std::vector<uint64_t> m_snap_flags;
+ std::vector<utime_t> m_snap_timestamps;
std::map<rados::cls::lock::locker_id_t,
rados::cls::lock::locker_info_t> m_lockers;
void send_v2_get_snap_namespaces();
Context *handle_v2_get_snap_namespaces(int *result);
+ void send_v2_get_snap_timestamps();
+ Context *handle_v2_get_snap_timestamps(int *result);
+
void send_v2_refresh_parent();
Context *handle_v2_refresh_parent(int *result);
return r;
}
+ int snap_get_timestamp(ImageCtx *ictx, uint64_t snap_id, struct timespec *timestamp)
+ {
+ std::map<librados::snap_t, SnapInfo>::iterator snap_it = ictx->snap_info.find(snap_id);
+ assert(snap_it != ictx->snap_info.end());
+ utime_t time = snap_it->second.timestamp;
+ time.to_timespec(timestamp);
+ return 0;
+ }
+
int snap_get_limit(ImageCtx *ictx, uint64_t *limit)
{
int r = cls_client::snapshot_get_limit(&ictx->md_ctx, ictx->header_oid,
int snap_exists(ImageCtx *ictx, const char *snap_name, bool *exists);
int snap_get_limit(ImageCtx *ictx, uint64_t *limit);
int snap_set_limit(ImageCtx *ictx, uint64_t limit);
+ int snap_get_timestamp(ImageCtx *ictx, uint64_t snap_id, struct timespec *timestamp);
int snap_remove(ImageCtx *ictx, const char *snap_name, uint32_t flags, ProgressContext& pctx);
int get_snap_namespace(ImageCtx *ictx,
const char *snap_name,
return r;
}
+ int Image::snap_get_timestamp(uint64_t snap_id, struct timespec *timestamp)
+ {
+ ImageCtx *ictx = (ImageCtx *)ctx;
+ tracepoint(librbd, snap_get_timestamp_enter, ictx, ictx->name.c_str());
+ int r = librbd::snap_get_timestamp(ictx, snap_id, timestamp);
+ tracepoint(librbd, snap_get_timestamp_exit, r);
+ return r;
+ }
+
int Image::snap_get_limit(uint64_t *limit)
{
ImageCtx *ictx = (ImageCtx *)ctx;
return r;
}
+extern "C" int rbd_snap_get_timestamp(rbd_image_t image, uint64_t snap_id, struct timespec *timestamp)
+{
+ librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
+ tracepoint(librbd, snap_get_timestamp_enter, ictx, ictx->name.c_str());
+ int r = librbd::snap_get_timestamp(ictx, snap_id, timestamp);
+ tracepoint(librbd, snap_get_timestamp_exit, r);
+ return r;
+}
+
extern "C" int rbd_snap_set_limit(rbd_image_t image, uint64_t limit)
{
librbd::ImageCtx *ictx = (librbd::ImageCtx *)image;
cdef extern from "time.h":
ctypedef long int time_t
+ cdef struct timespec:
+ time_t tv_sec
+ long tv_nsec
cdef extern from "limits.h":
cdef uint64_t INT64_MAX
int *is_protected)
int rbd_snap_get_limit(rbd_image_t image, uint64_t *limit)
int rbd_snap_set_limit(rbd_image_t image, uint64_t limit)
+ int rbd_snap_get_timestamp(rbd_image_t image, uint64_t snap_id, timespec *timestamp)
int rbd_snap_set(rbd_image_t image, const char *snapname)
int rbd_flatten(rbd_image_t image)
int rbd_rebuild_object_map(rbd_image_t image, librbd_progress_fn_t cb,
raise make_ex(ret, 'error setting snapshot limit for %s' % self.name)
return ret
+ def get_snap_timestamp(self, snap_id):
+ """
+ Get the snapshot timestamp for an image.
+ :param snap_id: the snapshot id of a snap shot
+ """
+ cdef:
+ timespec timestamp
+ uint64_t _snap_id = snap_id
+ with nogil:
+ ret = rbd_snap_get_timestamp(self.image, _snap_id, ×tamp)
+ if ret != 0:
+ raise make_ex(ret, 'error getting snapshot timestamp for image: %s, snap_id: %d' % (self.name, snap_id))
+ return datetime.fromtimestamp(timestamp.tv_sec)
+
def remove_snap_limit(self):
"""
Remove the snapshot limit for an image, essentially setting
}
}
+ void expect_snap_timestamp_list(MockRefreshImageCtx &mock_image_ctx, int r) {
+ auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
+ exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("get_snapshot_timestamp"), _, _, _));
+ if (r < 0) {
+ expect.WillOnce(Return(r));
+ } else {
+ expect.WillOnce(DoDefault());
+ }
+ }
+
void expect_snap_namespace_list(MockRefreshImageCtx &mock_image_ctx, int r) {
auto &expect = EXPECT_CALL(get_mock_io_ctx(mock_image_ctx.md_ctx),
exec(mock_image_ctx.header_oid, _, StrEq("rbd"), StrEq("get_snapshot_namespace"), _, _, _));
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_get_snapshots(mock_image_ctx, 0);
+ expect_snap_timestamp_list(mock_image_ctx, 0);
expect_snap_namespace_list(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
if (ictx->test_features(RBD_FEATURE_EXCLUSIVE_LOCK)) {
expect_get_flags(mock_image_ctx, 0);
expect_get_group(mock_image_ctx, 0);
expect_get_snapshots(mock_image_ctx, 0);
+ expect_snap_timestamp_list(mock_image_ctx, 0);
expect_snap_namespace_list(mock_image_ctx, 0);
expect_refresh_parent_is_required(mock_refresh_parent_request, false);
if (ictx->test_features(RBD_FEATURE_OBJECT_MAP)) {
t.define_column("SNAPID", TextTable::RIGHT, TextTable::RIGHT);
t.define_column("NAME", TextTable::LEFT, TextTable::LEFT);
t.define_column("SIZE", TextTable::RIGHT, TextTable::RIGHT);
+ t.define_column("TIMESTAMP", TextTable::LEFT, TextTable::LEFT);
}
for (std::vector<librbd::snap_info_t>::iterator s = snaps.begin();
s != snaps.end(); ++s) {
+ struct timespec timestamp;
+ image.snap_get_timestamp(s->id, ×tamp);
+ string tt_str = "";
+ if(timestamp.tv_sec != 0) {
+ time_t tt = timestamp.tv_sec;
+ tt_str = ctime(&tt);
+ tt_str = tt_str.substr(0, tt_str.length() - 1);
+ }
+
if (f) {
f->open_object_section("snapshot");
f->dump_unsigned("id", s->id);
f->dump_string("name", s->name);
f->dump_unsigned("size", s->size);
+ f->dump_string("timestamp", tt_str);
f->close_section();
} else {
- t << s->id << s->name << stringify(prettybyte_t(s->size))
+ t << s->id << s->name << stringify(prettybyte_t(s->size)) << tt_str
<< TextTable::endrow;
}
}
)
)
+TRACEPOINT_EVENT(librbd, snap_get_timestamp_enter,
+ TP_ARGS(
+ void*, imagectx,
+ const char*, name),
+ TP_FIELDS(
+ ctf_integer_hex(void*, imagectx, imagectx)
+ ctf_string(name, name)
+ )
+)
+
+TRACEPOINT_EVENT(librbd, snap_get_timestamp_exit,
+ TP_ARGS(
+ int, retval),
+ TP_FIELDS(
+ ctf_integer(int, retval, retval)
+ )
+)
+
TRACEPOINT_EVENT(librbd, snap_get_limit_enter,
TP_ARGS(
void*, imagectx,