]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
cls/rbd: methods to set/get/remove image migration header
authorMykola Golub <to.my.trociny@gmail.com>
Wed, 22 Nov 2017 13:47:45 +0000 (15:47 +0200)
committerJason Dillaman <dillaman@redhat.com>
Tue, 14 Aug 2018 22:29:44 +0000 (18:29 -0400)
Signed-off-by: Mykola Golub <mgolub@suse.com>
src/cls/rbd/cls_rbd.cc
src/cls/rbd/cls_rbd_client.cc
src/cls/rbd/cls_rbd_client.h
src/cls/rbd/cls_rbd_types.cc
src/cls/rbd/cls_rbd_types.h
src/include/rbd/features.h
src/librbd/Features.cc
src/test/cls_rbd/test_cls_rbd.cc
src/tools/ceph-dencoder/types.h

index 836782f5f738438dc928d1766e6fc5f4ba1517e0..49855bc35520d9a32f94a88e4e8dff9fca0c95f0 100644 (file)
@@ -278,6 +278,184 @@ int snapshot_iterate(cls_method_context_t hctx, L& lambda) {
   return 0;
 }
 
+int set_migration(cls_method_context_t hctx,
+                  const cls::rbd::MigrationSpec &migration_spec, bool init) {
+  if (init) {
+    bufferlist bl;
+    int r = cls_cxx_map_get_val(hctx, "migration", &bl);
+    if (r != -ENOENT) {
+      if (r == 0) {
+        CLS_LOG(10, "migration already set");
+        return -EEXIST;
+      }
+      CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r).c_str());
+      return r;
+    }
+
+    uint64_t features = 0;
+    r = read_key(hctx, "features", &features);
+    if (r == -ENOENT) {
+      CLS_LOG(20, "no features, assuming v1 format");
+      bufferlist header;
+      r = cls_cxx_read(hctx, 0, sizeof(RBD_HEADER_TEXT), &header);
+      if (r < 0) {
+        CLS_ERR("failed to read v1 header: %s", cpp_strerror(r).c_str());
+        return r;
+      }
+      if (header.length() != sizeof(RBD_HEADER_TEXT)) {
+        CLS_ERR("unrecognized v1 header format");
+        return -ENXIO;
+      }
+      if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) != 0) {
+        if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(),
+                   header.length()) == 0) {
+          CLS_LOG(10, "migration already set");
+          return -EEXIST;
+        } else {
+          CLS_ERR("unrecognized v1 header format");
+          return -ENXIO;
+        }
+      }
+      if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC) {
+        CLS_LOG(10, "v1 format image can only be migration source");
+        return -EINVAL;
+      }
+
+      header.clear();
+      header.append(RBD_MIGRATE_HEADER_TEXT);
+      r = cls_cxx_write(hctx, 0, header.length(), &header);
+      if (r < 0) {
+        CLS_ERR("error updating v1 header: %s", cpp_strerror(r).c_str());
+        return r;
+      }
+    } else if (r < 0) {
+      CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
+      return r;
+    } else if ((features & RBD_FEATURE_MIGRATING) != 0ULL) {
+      if (migration_spec.header_type != cls::rbd::MIGRATION_HEADER_TYPE_DST) {
+        CLS_LOG(10, "migrating feature already set");
+        return -EEXIST;
+      }
+    } else {
+      features |= RBD_FEATURE_MIGRATING;
+      bl.clear();
+      encode(features, bl);
+      r = cls_cxx_map_set_val(hctx, "features", &bl);
+      if (r < 0) {
+        CLS_ERR("error updating features: %s", cpp_strerror(r).c_str());
+        return r;
+      }
+    }
+  }
+
+  bufferlist bl;
+  encode(migration_spec, bl);
+  int r = cls_cxx_map_set_val(hctx, "migration", &bl);
+  if (r < 0) {
+    CLS_ERR("error setting migration: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+
+  return 0;
+}
+
+int read_migration(cls_method_context_t hctx,
+                   cls::rbd::MigrationSpec *migration_spec) {
+  uint64_t features = 0;
+  int r = read_key(hctx, "features", &features);
+  if (r == -ENOENT) {
+    CLS_LOG(20, "no features, assuming v1 format");
+    bufferlist header;
+    r = cls_cxx_read(hctx, 0, sizeof(RBD_HEADER_TEXT), &header);
+    if (r < 0) {
+      CLS_ERR("failed to read v1 header: %s", cpp_strerror(r).c_str());
+      return r;
+    }
+    if (header.length() != sizeof(RBD_HEADER_TEXT)) {
+      CLS_ERR("unrecognized v1 header format");
+      return -ENXIO;
+    }
+    if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), header.length()) != 0) {
+      if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) == 0) {
+        CLS_LOG(10, "migration feature not set");
+        return -EINVAL;
+      } else {
+        CLS_ERR("unrecognized v1 header format");
+        return -ENXIO;
+      }
+    }
+    if (migration_spec->header_type != cls::rbd::MIGRATION_HEADER_TYPE_SRC) {
+      CLS_LOG(10, "v1 format image can only be migration source");
+      return -EINVAL;
+    }
+  } else if (r < 0) {
+    CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
+    return r;
+  } else if ((features & RBD_FEATURE_MIGRATING) == 0ULL) {
+    CLS_LOG(10, "migration feature not set");
+    return -EINVAL;
+  }
+
+  r = read_key(hctx, "migration", migration_spec);
+  if (r < 0) {
+    CLS_ERR("failed to read migration off disk: %s", cpp_strerror(r).c_str());
+    return r;
+  }
+
+  return 0;
+}
+
+int remove_migration(cls_method_context_t hctx) {
+  int r = remove_key(hctx, "migration");
+  if (r < 0) {
+    return r;
+  }
+
+  uint64_t features = 0;
+  r = read_key(hctx, "features", &features);
+  if (r == -ENOENT) {
+    CLS_LOG(20, "no features, assuming v1 format");
+    bufferlist header;
+    r = cls_cxx_read(hctx, 0, sizeof(RBD_MIGRATE_HEADER_TEXT), &header);
+    if (header.length() != sizeof(RBD_MIGRATE_HEADER_TEXT)) {
+      CLS_ERR("unrecognized v1 header format");
+      return -ENXIO;
+    }
+    if (memcmp(RBD_MIGRATE_HEADER_TEXT, header.c_str(), header.length()) != 0) {
+      if (memcmp(RBD_HEADER_TEXT, header.c_str(), header.length()) == 0) {
+        CLS_LOG(10, "migration feature not set");
+        return -EINVAL;
+      } else {
+        CLS_ERR("unrecognized v1 header format");
+        return -ENXIO;
+      }
+    }
+    header.clear();
+    header.append(RBD_HEADER_TEXT);
+    r = cls_cxx_write(hctx, 0, header.length(), &header);
+    if (r < 0) {
+      CLS_ERR("error updating v1 header: %s", cpp_strerror(r).c_str());
+      return r;
+    }
+  } else if (r < 0) {
+    CLS_ERR("failed to read features off disk: %s", cpp_strerror(r).c_str());
+    return r;
+  } else if ((features & RBD_FEATURE_MIGRATING) == 0ULL) {
+    CLS_LOG(10, "migrating feature not set");
+  } else {
+    features &= ~RBD_FEATURE_MIGRATING;
+    bufferlist bl;
+    encode(features, bl);
+    r = cls_cxx_map_set_val(hctx, "features", &bl);
+    if (r < 0) {
+      CLS_ERR("error updating features: %s", cpp_strerror(r).c_str());
+      return r;
+    }
+  }
+
+  return 0;
+}
+
 } // namespace image
 
 /**
@@ -3449,6 +3627,114 @@ int children_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
   return 0;
 }
 
+/**
+ * Set image migration.
+ *
+ * Input:
+ * @param migration_spec (cls::rbd::MigrationSpec) image migration spec
+ *
+ * Output:
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+int migration_set(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
+  cls::rbd::MigrationSpec migration_spec;
+  try {
+    auto it = in->cbegin();
+    decode(migration_spec, it);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+
+  int r = image::set_migration(hctx, migration_spec, true);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+/**
+ * Set image migration state.
+ *
+ * Input:
+ * @param state (cls::rbd::MigrationState) migration state
+ * @param description (std::string) migration state description
+ *
+ * Output:
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+int migration_set_state(cls_method_context_t hctx, bufferlist *in,
+                        bufferlist *out) {
+  cls::rbd::MigrationState state;
+  std::string description;
+  try {
+    auto it = in->cbegin();
+    decode(state, it);
+    decode(description, it);
+  } catch (const buffer::error &err) {
+    return -EINVAL;
+  }
+
+  cls::rbd::MigrationSpec migration_spec;
+  int r = image::read_migration(hctx, &migration_spec);
+  if (r < 0) {
+    return r;
+  }
+
+  migration_spec.state = state;
+  migration_spec.state_description = description;
+
+  r = image::set_migration(hctx, migration_spec, false);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
+/**
+ * Get image migration spec.
+ *
+ * Input:
+ *
+ * Output:
+ * @param migration_spec (cls::rbd::MigrationSpec) image migration spec
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+int migration_get(cls_method_context_t hctx, bufferlist *in, bufferlist *out) {
+  cls::rbd::MigrationSpec migration_spec;
+  int r = image::read_migration(hctx, &migration_spec);
+  if (r < 0) {
+    return r;
+  }
+
+  encode(migration_spec, *out);
+
+  return 0;
+}
+
+/**
+ * Remove image migration spec.
+ *
+ * Input:
+ *
+ * Output:
+ *
+ * @returns 0 on success, negative error code on failure
+ */
+int migration_remove(cls_method_context_t hctx, bufferlist *in,
+                     bufferlist *out) {
+  int r = image::remove_migration(hctx);
+  if (r < 0) {
+    return r;
+  }
+
+  return 0;
+}
+
 /****************************** Old format *******************************/
 
 int old_snapshots_list(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
@@ -6371,6 +6657,10 @@ CLS_INIT(rbd)
   cls_method_handle_t h_child_attach;
   cls_method_handle_t h_child_detach;
   cls_method_handle_t h_children_list;
+  cls_method_handle_t h_migration_set;
+  cls_method_handle_t h_migration_set_state;
+  cls_method_handle_t h_migration_get;
+  cls_method_handle_t h_migration_remove;
   cls_method_handle_t h_old_snapshots_list;
   cls_method_handle_t h_old_snapshot_add;
   cls_method_handle_t h_old_snapshot_remove;
@@ -6536,6 +6826,18 @@ CLS_INIT(rbd)
   cls_register_cxx_method(h_class, "children_list",
                           CLS_METHOD_RD,
                           children_list, &h_children_list);
+  cls_register_cxx_method(h_class, "migration_set",
+                          CLS_METHOD_RD | CLS_METHOD_WR,
+                          migration_set, &h_migration_set);
+  cls_register_cxx_method(h_class, "migration_set_state",
+                          CLS_METHOD_RD | CLS_METHOD_WR,
+                          migration_set_state, &h_migration_set_state);
+  cls_register_cxx_method(h_class, "migration_get",
+                          CLS_METHOD_RD,
+                          migration_get, &h_migration_get);
+  cls_register_cxx_method(h_class, "migration_remove",
+                          CLS_METHOD_RD | CLS_METHOD_WR,
+                          migration_remove, &h_migration_remove);
 
   /* methods for the rbd_children object */
   cls_register_cxx_method(h_class, "add_child",
index fabf0e2e853739754140a7fff12202b4e7266936..98e9cea9967e73eadee5c0ba63881ee6a441c882 100644 (file)
@@ -1591,6 +1591,82 @@ namespace librbd {
       return 0;
     }
 
+    int migration_set(librados::IoCtx *ioctx, const std::string &oid,
+                      const cls::rbd::MigrationSpec &migration_spec) {
+      librados::ObjectWriteOperation op;
+      migration_set(&op, migration_spec);
+      return ioctx->operate(oid, &op);
+    }
+
+    void migration_set(librados::ObjectWriteOperation *op,
+                       const cls::rbd::MigrationSpec &migration_spec) {
+      bufferlist bl;
+      encode(migration_spec, bl);
+      op->exec("rbd", "migration_set", bl);
+    }
+
+    int migration_set_state(librados::IoCtx *ioctx, const std::string &oid,
+                            cls::rbd::MigrationState state,
+                            const std::string &description) {
+      librados::ObjectWriteOperation op;
+      migration_set_state(&op, state, description);
+      return ioctx->operate(oid, &op);
+    }
+
+    void migration_set_state(librados::ObjectWriteOperation *op,
+                             cls::rbd::MigrationState state,
+                             const std::string &description) {
+      bufferlist bl;
+      encode(state, bl);
+      encode(description, bl);
+      op->exec("rbd", "migration_set_state", bl);
+    }
+
+    void migration_get_start(librados::ObjectReadOperation *op) {
+      bufferlist bl;
+      op->exec("rbd", "migration_get", bl);
+    }
+
+    int migration_get_finish(bufferlist::const_iterator *it,
+                             cls::rbd::MigrationSpec *migration_spec) {
+      try {
+       decode(*migration_spec, *it);
+      } catch (const buffer::error &err) {
+       return -EBADMSG;
+      }
+      return 0;
+    }
+
+    int migration_get(librados::IoCtx *ioctx, const std::string &oid,
+                      cls::rbd::MigrationSpec *migration_spec) {
+      librados::ObjectReadOperation op;
+      migration_get_start(&op);
+
+      bufferlist out_bl;
+      int r = ioctx->operate(oid, &op, &out_bl);
+      if (r < 0) {
+       return r;
+      }
+
+      auto iter = out_bl.cbegin();
+      r = migration_get_finish(&iter, migration_spec);
+      if (r < 0) {
+       return r;
+      }
+      return 0;
+    }
+
+    int migration_remove(librados::IoCtx *ioctx, const std::string &oid) {
+      librados::ObjectWriteOperation op;
+      migration_remove(&op);
+      return ioctx->operate(oid, &op);
+    }
+
+    void migration_remove(librados::ObjectWriteOperation *op) {
+      bufferlist bl;
+      op->exec("rbd", "migration_remove", bl);
+    }
+
     void mirror_uuid_get_start(librados::ObjectReadOperation *op) {
       bufferlist bl;
       op->exec("rbd", "mirror_uuid_get", bl);
index e89c6e90643fdf51d2d54afe05005693b0ea1d07..c96900e9864b8512fa136ce475c25cab034a837b 100644 (file)
@@ -258,6 +258,24 @@ namespace librbd {
                       snapid_t snap_id,
                       cls::rbd::ChildImageSpecs *child_images);
 
+    int migration_set(librados::IoCtx *ioctx, const std::string &oid,
+                    const cls::rbd::MigrationSpec &migration_spec);
+    void migration_set(librados::ObjectWriteOperation *op,
+                     const cls::rbd::MigrationSpec &migration_spec);
+    int migration_set_state(librados::IoCtx *ioctx, const std::string &oid,
+                            cls::rbd::MigrationState state,
+                            const std::string &description);
+    void migration_set_state(librados::ObjectWriteOperation *op,
+                             cls::rbd::MigrationState state,
+                             const std::string &description);
+    void migration_get_start(librados::ObjectReadOperation *op);
+    int migration_get_finish(bufferlist::const_iterator *it,
+                           cls::rbd::MigrationSpec *migration_spec);
+    int migration_get(librados::IoCtx *ioctx, const std::string &oid,
+                      cls::rbd::MigrationSpec *migration_spec);
+    int migration_remove(librados::IoCtx *ioctx, const std::string &oid);
+    void migration_remove(librados::ObjectWriteOperation *op);
+
     // operations on rbd_id objects
     void get_id_start(librados::ObjectReadOperation *op);
     int get_id_finish(bufferlist::const_iterator *it, std::string *id);
index 2897fe5b0bf57f10dde952ffa321bd99b02884d1..00538666ee169b2a9e0b0d6bf0da62a73a5a4f2c 100644 (file)
@@ -732,5 +732,121 @@ std::ostream& operator<<(std::ostream& os,
             << image_map.mapped_time << "]";
 }
 
+std::ostream& operator<<(std::ostream& os,
+                         const MigrationHeaderType& type) {
+  switch (type) {
+  case MIGRATION_HEADER_TYPE_SRC:
+    os << "source";
+    break;
+  case MIGRATION_HEADER_TYPE_DST:
+    os << "destination";
+    break;
+  default:
+    os << "unknown (" << static_cast<uint32_t>(type) << ")";
+    break;
+  }
+  return os;
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const MigrationState& migration_state) {
+  switch (migration_state) {
+  case MIGRATION_STATE_ERROR:
+    os << "error";
+    break;
+  case MIGRATION_STATE_PREPARING:
+    os << "preparing";
+    break;
+  case MIGRATION_STATE_PREPARED:
+    os << "prepared";
+    break;
+  case MIGRATION_STATE_EXECUTING:
+    os << "executing";
+    break;
+  case MIGRATION_STATE_EXECUTED:
+    os << "executed";
+    break;
+  default:
+    os << "unknown (" << static_cast<uint32_t>(migration_state) << ")";
+    break;
+  }
+  return os;
+}
+
+void MigrationSpec::encode(bufferlist& bl) const {
+  ENCODE_START(1, 1, bl);
+  encode(header_type, bl);
+  encode(pool_id, bl);
+  encode(image_name, bl);
+  encode(image_id, bl);
+  encode(snap_seqs, bl);
+  encode(overlap, bl);
+  encode(flatten, bl);
+  encode(mirroring, bl);
+  encode(state, bl);
+  encode(state_description, bl);
+  ENCODE_FINISH(bl);
+}
+
+void MigrationSpec::decode(bufferlist::const_iterator& bl) {
+  DECODE_START(1, bl);
+  decode(header_type, bl);
+  decode(pool_id, bl);
+  decode(image_name, bl);
+  decode(image_id, bl);
+  decode(snap_seqs, bl);
+  decode(overlap, bl);
+  decode(flatten, bl);
+  decode(mirroring, bl);
+  decode(state, bl);
+  decode(state_description, bl);
+  DECODE_FINISH(bl);
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const std::map<uint64_t, uint64_t>& snap_seqs) {
+  os << "{";
+  size_t count = 0;
+  for (auto &it : snap_seqs) {
+    os << (count++ > 0 ? ", " : "") << "(" << it.first << ", " << it.second
+       << ")";
+  }
+  os << "}";
+  return os;
+}
+
+void MigrationSpec::dump(Formatter *f) const {
+  f->dump_stream("header_type") << header_type;
+  f->dump_int("pool_id", pool_id);
+  f->dump_string("image_name", image_name);
+  f->dump_string("image_id", image_id);
+  f->dump_stream("snap_seqs") << snap_seqs;
+  f->dump_unsigned("overlap", overlap);
+  f->dump_bool("mirroring", mirroring);
+}
+
+void MigrationSpec::generate_test_instances(std::list<MigrationSpec*> &o) {
+  o.push_back(new MigrationSpec());
+  o.push_back(new MigrationSpec(MIGRATION_HEADER_TYPE_SRC, 1, "image_name",
+                                "image_id", {{1, 2}}, 123, true, true,
+                                MIGRATION_STATE_PREPARED, "description"));
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const MigrationSpec& migration_spec) {
+  os << "["
+     << "header_type=" << migration_spec.header_type << ", "
+     << "pool_id=" << migration_spec.pool_id << ", "
+     << "image_name=" << migration_spec.image_name << ", "
+     << "image_id=" << migration_spec.image_id << ", "
+     << "snap_seqs=" << migration_spec.snap_seqs << ", "
+     << "overlap=" << migration_spec.overlap << ", "
+     << "flatten=" << migration_spec.flatten << ", "
+     << "mirroring=" << migration_spec.mirroring << ", "
+     << "state=" << migration_spec.state << ", "
+     << "state_description=" << migration_spec.state_description << "]";
+  return os;
+}
+
 } // namespace rbd
 } // namespace cls
index 4007f59c59948b418d865816392917fabcd01959..7be7d5171a859e491099fd976f8567a69a26fe04 100644 (file)
@@ -587,6 +587,90 @@ std::ostream& operator<<(std::ostream& os, const MirrorImageMap &image_map);
 
 WRITE_CLASS_ENCODER(MirrorImageMap);
 
+enum MigrationHeaderType {
+  MIGRATION_HEADER_TYPE_SRC = 1,
+  MIGRATION_HEADER_TYPE_DST = 2,
+};
+
+inline void encode(const MigrationHeaderType &type, bufferlist& bl) {
+  using ceph::encode;
+  encode(static_cast<uint8_t>(type), bl);
+}
+
+inline void decode(MigrationHeaderType &type, bufferlist::const_iterator& it) {
+  uint8_t int_type;
+  using ceph::decode;
+  decode(int_type, it);
+  type = static_cast<MigrationHeaderType>(int_type);
+}
+
+enum MigrationState {
+  MIGRATION_STATE_ERROR = 0,
+  MIGRATION_STATE_PREPARING = 1,
+  MIGRATION_STATE_PREPARED = 2,
+  MIGRATION_STATE_EXECUTING = 3,
+  MIGRATION_STATE_EXECUTED = 4,
+};
+
+inline void encode(const MigrationState &state, bufferlist& bl) {
+  using ceph::encode;
+  encode(static_cast<uint8_t>(state), bl);
+}
+
+inline void decode(MigrationState &state, bufferlist::const_iterator& it) {
+  uint8_t int_state;
+  using ceph::decode;
+  decode(int_state, it);
+  state = static_cast<MigrationState>(int_state);
+}
+
+std::ostream& operator<<(std::ostream& os,
+                         const MigrationState& migration_state);
+
+struct MigrationSpec {
+  MigrationHeaderType header_type = MIGRATION_HEADER_TYPE_SRC;
+  int64_t pool_id = -1;
+  std::string image_name;
+  std::string image_id;
+  std::map<uint64_t, uint64_t> snap_seqs;
+  uint64_t overlap = 0;
+  bool flatten = false;
+  bool mirroring = false;
+  MigrationState state = MIGRATION_STATE_ERROR;
+  std::string state_description;
+
+  MigrationSpec() {
+  }
+  MigrationSpec(MigrationHeaderType header_type, int64_t pool_id,
+                const std::string &image_name, const std::string &image_id,
+                const std::map<uint64_t, uint64_t> &snap_seqs, uint64_t overlap,
+                bool mirroring, bool flatten, MigrationState state,
+                const std::string &state_description)
+    : header_type(header_type), pool_id(pool_id), image_name(image_name),
+      image_id(image_id), snap_seqs(snap_seqs), overlap(overlap),
+      flatten(flatten), mirroring(mirroring), state(state),
+      state_description(state_description) {
+  }
+
+  void encode(bufferlist &bl) const;
+  void decode(bufferlist::const_iterator& it);
+  void dump(Formatter *f) const;
+
+  static void generate_test_instances(std::list<MigrationSpec*> &o);
+
+  inline bool operator==(const MigrationSpec& ms) const {
+    return header_type == ms.header_type && pool_id == ms.pool_id &&
+      image_name == ms.image_name && image_id == ms.image_id &&
+      snap_seqs == ms.snap_seqs && overlap == ms.overlap &&
+      flatten == ms.flatten && mirroring == ms.mirroring && state == ms.state &&
+      state_description == ms.state_description;
+  }
+};
+
+std::ostream& operator<<(std::ostream& os, const MigrationSpec& migration_spec);
+
+WRITE_CLASS_ENCODER(MigrationSpec);
+
 } // namespace rbd
 } // namespace cls
 
index 24ae5fcfc10fcad5951f130202a6dd09304762ac..89c54a36b213520c17c51566d8ea08fe0c1170c1 100644 (file)
@@ -10,6 +10,7 @@
 #define RBD_FEATURE_JOURNALING          (1ULL<<6)
 #define RBD_FEATURE_DATA_POOL           (1ULL<<7)
 #define RBD_FEATURE_OPERATIONS          (1ULL<<8)
+#define RBD_FEATURE_MIGRATING           (1ULL<<9)
 
 #define RBD_FEATURES_DEFAULT             (RBD_FEATURE_LAYERING | \
                                          RBD_FEATURE_EXCLUSIVE_LOCK | \
@@ -26,6 +27,7 @@
 #define RBD_FEATURE_NAME_JOURNALING      "journaling"
 #define RBD_FEATURE_NAME_DATA_POOL       "data-pool"
 #define RBD_FEATURE_NAME_OPERATIONS      "operations"
+#define RBD_FEATURE_NAME_MIGRATING       "migrating"
 
 /// features that make an image inaccessible for read or write by
 /// clients that don't understand them
@@ -40,7 +42,8 @@
                                          RBD_FEATURE_FAST_DIFF      | \
                                          RBD_FEATURE_DEEP_FLATTEN   | \
                                          RBD_FEATURE_JOURNALING     | \
-                                         RBD_FEATURE_OPERATIONS)
+                                         RBD_FEATURE_OPERATIONS     | \
+                                         RBD_FEATURE_MIGRATING)
 
 #define RBD_FEATURES_ALL               (RBD_FEATURE_LAYERING       | \
                                         RBD_FEATURE_STRIPINGV2     | \
@@ -50,7 +53,8 @@
                                          RBD_FEATURE_DEEP_FLATTEN   | \
                                          RBD_FEATURE_JOURNALING     | \
                                          RBD_FEATURE_DATA_POOL      | \
-                                         RBD_FEATURE_OPERATIONS)
+                                         RBD_FEATURE_OPERATIONS     | \
+                                         RBD_FEATURE_MIGRATING)
 
 /// features that may be dynamically enabled or disabled
 #define RBD_FEATURES_MUTABLE            (RBD_FEATURE_EXCLUSIVE_LOCK | \
 #define RBD_FEATURES_IMPLICIT_ENABLE  (RBD_FEATURE_STRIPINGV2 | \
                                        RBD_FEATURE_DATA_POOL  | \
                                        RBD_FEATURE_FAST_DIFF  | \
-                                       RBD_FEATURE_OPERATIONS)
+                                       RBD_FEATURE_OPERATIONS | \
+                                       RBD_FEATURE_MIGRATING)
 
 /// features that cannot be controlled by the user
-#define RBD_FEATURES_INTERNAL         (RBD_FEATURE_OPERATIONS)
+#define RBD_FEATURES_INTERNAL         (RBD_FEATURE_OPERATIONS | \
+                                       RBD_FEATURE_MIGRATING)
 
 #define RBD_OPERATION_FEATURE_CLONE_PARENT      (1ULL<<0)
 #define RBD_OPERATION_FEATURE_CLONE_CHILD       (1ULL<<1)
index 5c8c085839cc58f88027fb9798628e2539fbe3a1..15b1493135968fc5c76406f9dcb663e14104af2d 100644 (file)
@@ -20,7 +20,7 @@ static const std::map<std::string, uint64_t> RBD_FEATURE_MAP = {
   {RBD_FEATURE_NAME_JOURNALING, RBD_FEATURE_JOURNALING},
   {RBD_FEATURE_NAME_DATA_POOL, RBD_FEATURE_DATA_POOL},
 };
-static_assert((RBD_FEATURE_OPERATIONS << 1) > RBD_FEATURES_ALL,
+static_assert((RBD_FEATURE_MIGRATING << 1) > RBD_FEATURES_ALL,
              "new RBD feature added");
 
 
index 9c888c9e0e76247a01dcb1e8955dbb905cabada2..f8a386d5162ca2468405c53458db1dd88d74e0fb 100644 (file)
@@ -2764,3 +2764,118 @@ TEST_F(TestClsRbd, namespace_methods)
   ASSERT_EQ(0, namespace_list(&ioctx, name1, 1, &entries));
   ASSERT_TRUE(entries.empty());
 }
+
+TEST_F(TestClsRbd, migration)
+{
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  string oid = get_temp_image_name();
+  ASSERT_EQ(0, create_image(&ioctx, oid, 0, 22, 0, oid, -1));
+
+  cls::rbd::MigrationSpec migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_DST, 1,
+                                         "name", "id", {}, 0, false, false,
+                                         cls::rbd::MIGRATION_STATE_PREPARING,
+                                         "123");
+  cls::rbd::MigrationSpec read_migration_spec;
+
+  ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+
+  uint64_t features;
+  ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
+  ASSERT_EQ(0U, features);
+
+  ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
+  ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
+  ASSERT_EQ(migration_spec, read_migration_spec);
+
+  ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
+  ASSERT_EQ(RBD_FEATURE_MIGRATING, features);
+
+  ASSERT_EQ(-EEXIST, migration_set(&ioctx, oid, migration_spec));
+
+  migration_spec.state = cls::rbd::MIGRATION_STATE_PREPARED;
+  migration_spec.state_description = "456";
+  ASSERT_EQ(0, migration_set_state(&ioctx, oid, migration_spec.state,
+                                   migration_spec.state_description));
+  ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
+  ASSERT_EQ(migration_spec, read_migration_spec);
+
+  ASSERT_EQ(0, migration_remove(&ioctx, oid));
+
+  ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
+  ASSERT_EQ(0U, features);
+
+  ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+  ASSERT_EQ(-EINVAL, migration_set_state(&ioctx, oid, migration_spec.state,
+                                         migration_spec.state_description));
+
+  migration_spec.header_type = cls::rbd::MIGRATION_HEADER_TYPE_SRC;
+
+  ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
+
+  ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
+  ASSERT_EQ(RBD_FEATURE_MIGRATING, features);
+
+  ASSERT_EQ(0, migration_remove(&ioctx, oid));
+
+  ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+  ASSERT_EQ(0, get_features(&ioctx, oid, CEPH_NOSNAP, &features));
+  ASSERT_EQ(0U, features);
+
+  ioctx.close();
+}
+
+TEST_F(TestClsRbd, migration_v1)
+{
+  librados::IoCtx ioctx;
+  ASSERT_EQ(0, _rados.ioctx_create(_pool_name.c_str(), ioctx));
+
+  bufferlist header;
+  header.append(RBD_HEADER_TEXT, sizeof(RBD_HEADER_TEXT));
+  string oid = get_temp_image_name();
+  ASSERT_EQ(0, ioctx.write(oid, header, header.length(), 0));
+
+  cls::rbd::MigrationSpec migration_spec(cls::rbd::MIGRATION_HEADER_TYPE_DST, 1,
+                                         "name", "id", {}, 0, false, false,
+                                         cls::rbd::MIGRATION_STATE_PREPARING,
+                                         "123");
+  cls::rbd::MigrationSpec read_migration_spec;
+
+  ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+
+  // v1 format image can only be migration source
+  ASSERT_EQ(-EINVAL, migration_set(&ioctx, oid, migration_spec));
+
+  migration_spec.header_type = cls::rbd::MIGRATION_HEADER_TYPE_SRC;
+  ASSERT_EQ(0, migration_set(&ioctx, oid, migration_spec));
+
+  ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
+  ASSERT_EQ(migration_spec, read_migration_spec);
+
+  header.clear();
+  ASSERT_EQ(static_cast<int>(sizeof(RBD_MIGRATE_HEADER_TEXT)),
+            ioctx.read(oid, header, sizeof(RBD_MIGRATE_HEADER_TEXT), 0));
+  ASSERT_STREQ(RBD_MIGRATE_HEADER_TEXT, header.c_str());
+
+  ASSERT_EQ(-EEXIST, migration_set(&ioctx, oid, migration_spec));
+
+  migration_spec.state = cls::rbd::MIGRATION_STATE_PREPARED;
+  migration_spec.state_description = "456";
+  ASSERT_EQ(0, migration_set_state(&ioctx, oid, migration_spec.state,
+                                   migration_spec.state_description));
+  ASSERT_EQ(0, migration_get(&ioctx, oid, &read_migration_spec));
+  ASSERT_EQ(migration_spec, read_migration_spec);
+
+  ASSERT_EQ(0, migration_remove(&ioctx, oid));
+
+  ASSERT_EQ(-EINVAL, migration_get(&ioctx, oid, &read_migration_spec));
+  ASSERT_EQ(-EINVAL, migration_set_state(&ioctx, oid, migration_spec.state,
+                                         migration_spec.state_description));
+  header.clear();
+  ASSERT_EQ(static_cast<int>(sizeof(RBD_HEADER_TEXT)),
+            ioctx.read(oid, header, sizeof(RBD_HEADER_TEXT), 0));
+  ASSERT_STREQ(RBD_HEADER_TEXT, header.c_str());
+
+  ioctx.close();
+}
index 5bd6e70bcf0fff755810e5f8d04aaa2957618175..2542c6bb21b2a5aa42c32253556435e06d1c308d 100644 (file)
@@ -465,6 +465,7 @@ TYPE(cls_rbd_snap)
 
 #include "cls/rbd/cls_rbd_types.h"
 TYPE(cls::rbd::ChildImageSpec)
+TYPE(cls::rbd::MigrationSpec)
 TYPE(cls::rbd::MirrorPeer)
 TYPE(cls::rbd::MirrorImage)
 TYPE(cls::rbd::MirrorImageMap)