cls_handle_t h_class;
cls_method_handle_t h_snapshots_list;
+cls_method_handle_t h_snapshot_add;
-int snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+static int snap_read_header(cls_method_context_t hctx, bufferlist& bl)
{
int snap_count = 0;
uint64_t snap_names_len = 0;
int rc;
struct rbd_obj_header_ondisk *header;
- bufferlist bl;
cls_log("snapshots_list");
break;
}
- bufferptr p(snap_names_len);
+ return 0;
+}
+
+int snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ bufferlist bl;
+ struct rbd_obj_header_ondisk *header;
+ int rc = snap_read_header(hctx, bl);
+ if (rc < 0)
+ return rc;
+
+ header = (struct rbd_obj_header_ondisk *)bl.c_str();
+ bufferptr p(header->snap_names_len);
+ char *buf = (char *)header;
+ char *name = buf + sizeof(*header) + header->snap_count * sizeof(struct rbd_obj_snap_ondisk);
+ char *end = name + header->snap_names_len;
memcpy(p.c_str(),
- bl.c_str() + sizeof(*header) + snap_count * sizeof(struct rbd_obj_snap_ondisk),
- snap_names_len);
+ buf + sizeof(*header) + header->snap_count * sizeof(struct rbd_obj_snap_ondisk),
+ header->snap_names_len);
+
+ ::encode(header->snap_count, *out);
+
+ for (int i = 0; i < header->snap_count; i++) {
+ string s = name;
+ ::encode(header->snaps[i].id, *out);
+ ::encode(header->snaps[i].image_size, *out);
+ ::encode(s, *out);
+
+ name += strlen(name) + 1;
+ if (name > end)
+ return -EIO;
+ }
+
+ return 0;
+}
+
+int snapshot_add(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ bufferlist bl;
+ struct rbd_obj_header_ondisk *header;
+ bufferlist newbl;
+ bufferptr header_bp(sizeof(*header));
+ struct rbd_obj_snap_ondisk *new_snaps;
+
+ int rc = snap_read_header(hctx, bl);
+ if (rc < 0)
+ return rc;
+
+ header = (struct rbd_obj_header_ondisk *)bl.c_str();
+
+ int snaps_id_ofs = sizeof(*header);
+ int len = snaps_id_ofs;
+ int names_ofs = snaps_id_ofs + sizeof(*new_snaps) * header->snap_count;
+ const char *snap_name;
+ const char *snap_names = ((char *)header) + names_ofs;
+ const char *end = snap_names + header->snap_names_len;
+ bufferlist::iterator iter = in->begin();
+ string s;
+ uint64_t snap_id;
+
+ try {
+ ::decode(s, iter);
+ ::decode(snap_id, iter);
+ } catch (buffer::error *err) {
+ return -EINVAL;
+ }
+ snap_name = s.c_str();
+
+
+ const char *cur_snap_name;
+ for (cur_snap_name = snap_names; cur_snap_name < end; cur_snap_name += strlen(cur_snap_name) + 1) {
+ if (strncmp(cur_snap_name, snap_name, end - cur_snap_name) == 0)
+ return -EEXIST;
+ }
+ if (cur_snap_name > end)
+ return -EIO;
+
+ int snap_name_len = strlen(snap_name);
+
+ bufferptr new_names_bp(header->snap_names_len + snap_name_len + 1);
+ bufferptr new_snaps_bp(sizeof(*new_snaps) * (header->snap_count + 1));
+
+ /* copy snap names and append to new snap name */
+ char *new_snap_names = new_names_bp.c_str();
+ strcpy(new_snap_names, snap_name);
+ memcpy(new_snap_names + snap_name_len + 1, snap_names, header->snap_names_len);
+
+ /* append new snap id */
+ new_snaps = (struct rbd_obj_snap_ondisk *)new_snaps_bp.c_str();
+ memcpy(new_snaps + 1, header->snaps, sizeof(*new_snaps) * header->snap_count);
+
+ header->snap_count = header->snap_count + 1;
+ header->snap_names_len = header->snap_names_len + snap_name_len + 1;
+ header->snap_seq = header->snap_seq + 1;
+
+ new_snaps[0].id = snap_id;
+ new_snaps[0].image_size = header->image_size;
+
+ len += sizeof(*new_snaps) * header->snap_count + header->snap_names_len;
+
+ memcpy(header_bp.c_str(), header, sizeof(*header));
+
+ newbl.push_back(header_bp);
+ newbl.push_back(new_snaps_bp);
+ newbl.push_back(new_names_bp);
- out->push_back(p);
+ rc = cls_cxx_write(hctx, 0, len, &newbl);
+ if (rc < 0)
+ return rc;
- return snap_count;
+ return 0;
}
void class_init()
{
- cls_log("Loaded rbd class!");
+ CLS_LOG("Loaded rbd class!");
- cls_register("rbd", &h_class);
- cls_register_cxx_method(h_class, "snap_list", CLS_METHOD_RD, snapshots_list, &h_snapshots_list);
+ cls_register("rbd", &h_class);
+ cls_register_cxx_method(h_class, "snap_list", CLS_METHOD_RD, snapshots_list, &h_snapshots_list);
+ cls_register_cxx_method(h_class, "snap_add", CLS_METHOD_RD | CLS_METHOD_WR, snapshot_add, &h_snapshot_add);
- return;
+ return;
}
void usage()
{
- cout << "usage: rbdtool [-n <auth user>] [-p|--pool <name>] <cmd>\n"
+ cout << "usage: rbdtool [-n <auth user>] [-p|--pool <name>] [-n|--object <imagename>] <cmd>\n"
<< "where 'pool' is a rados pool name (default is 'rbd') and 'cmd' is one of:\n"
<< "\t--list list rbd images\n"
<< "\t--info show information about image size, striping, etc.\n"
<< "\t--resize <image name> --size <new size in MB>\n"
<< "\t resize (expand or contract) image\n"
<< "\t--delete <image name>\n"
- << "\t delete an image" << std::endl;
- exit(1);
+ << "\t delete an image\n"
+ << "\t--list-snaps <image name>\n"
+ << "\t dump list of specific image snapshots"
+ << "\t--add-snap <snap name>\n"
+ << "\t create a snapshot for the spacified image" << std::endl;
}
common_init(args, "rbdtool", false, true);
bool opt_create = false, opt_delete = false, opt_list = false, opt_info = false, opt_resize = false,
- opt_list_snaps = false;
+ opt_list_snaps = false, opt_add_snap = false;
char *poolname = (char *)"rbd";
uint64_t size = 0;
int order = 0;
- char *imgname;
+ char *imgname = NULL, *snapname = NULL;
FOR_EACH_ARG(args) {
if (CONF_ARG_EQ("list", '\0')) {
} else if (CONF_ARG_EQ("list-snaps", '\0')) {
CONF_SAFE_SET_ARG_VAL(&imgname, OPT_STR);
opt_list_snaps = true;
+ } else if (CONF_ARG_EQ("add-snap", '\0')) {
+ CONF_SAFE_SET_ARG_VAL(&snapname, OPT_STR);
+ opt_add_snap = true;
} else if (CONF_ARG_EQ("pool", 'p')) {
CONF_SAFE_SET_ARG_VAL(&poolname, OPT_STR);
} else if (CONF_ARG_EQ("object", 'n')) {
}
if (!opt_create && !opt_delete && !opt_list && !opt_info && !opt_resize &&
- !opt_list_snaps)
+ !opt_list_snaps && !opt_add_snap) {
usage();
+ exit(1);
+ }
+ if (!imgname) {
+ usage();
+ exit(1);
+ }
if (rados.initialize(argc, argv) < 0) {
cerr << "couldn't initialize rados!" << std::endl;
cout << "done." << std::endl;
} else if (opt_list_snaps) {
bufferlist bl, bl2;
- char *s;
- r = rados.exec(pool, md_oid, "rbd", "snap_list", bl, bl2);
+ if (!imgname) {
+ usage();
+ err_exit(pool);
+ }
+ r = rados.exec(pool, md_oid, "rbd", "snap_list", bl, bl2);
if (r < 0) {
cerr << "list_snaps failed: " << strerror(-r) << std::endl;
err_exit(pool);
}
- s = bl2.c_str();
- for (int i=0; i<r; i++, s += strlen(s) + 1)
- cout << s << std::endl;
+ uint32_t num_snaps;
+ bufferlist::iterator iter = bl2.begin();
+ ::decode(num_snaps, iter);
+ for (uint32_t i=0; i < num_snaps; i++) {
+ uint64_t id, image_size;
+ string s;
+ ::decode(id, iter);
+ ::decode(image_size, iter);
+ ::decode(s, iter);
+ cout << id << "\t" << s << "\t" << image_size << std::endl;
+ }
+ } else if (opt_add_snap) {
+ bufferlist bl, bl2;
+ uint64_t snap_id;
+ if (!imgname || !snapname) {
+ usage();
+ err_exit(pool);
+ }
+ r = rados.selfmanaged_snap_create(pool, &snap_id);
+ if (r < 0) {
+ cerr << "failed to create snap id: " << strerror(-r) << std::endl;
+ err_exit(pool);
+ }
+
+ ::encode(snapname, bl);
+ ::encode(snap_id, bl);
+
+ r = rados.exec(pool, md_oid, "rbd", "snap_add", bl, bl2);
+ if (r < 0) {
+ cerr << "list_snaps failed: " << strerror(-r) << std::endl;
+ err_exit(pool);
+ }
}
rados.close_pool(pool);