ceph osd crush class rm CLASS || return 1
expect_failure $dir ENOENT ceph osd crush class rm CLASS || return 1
+ # test rm-device-class
+ ceph osd crush set-device-class aaa osd.0 || return 1
+ ceph osd tree | grep -q 'aaa' || return 1
+ ceph osd crush dump | grep -q '~aaa' || return 1
+ ceph osd crush tree --show-shadow | grep -q '~aaa' || return 1
+ ceph osd crush set-device-class bbb osd.1 || return 1
+ ceph osd tree | grep -q 'bbb' || return 1
+ ceph osd crush dump | grep -q '~bbb' || return 1
+ ceph osd crush tree --show-shadow | grep -q '~bbb' || return 1
+ ceph osd crush set-device-class ccc osd.2 || return 1
+ ceph osd tree | grep -q 'ccc' || return 1
+ ceph osd crush dump | grep -q '~ccc' || return 1
+ ceph osd crush tree --show-shadow | grep -q '~ccc' || return 1
+ ceph osd crush rm-device-class 0 || return 1
+ ceph osd tree | grep -q 'aaa' && return 1
+ ceph osd crush dump | grep -q '~aaa' && return 1
+ ceph osd crush tree --show-shadow | grep -q '~aaa' && return 1
+ ceph osd crush class ls | grep -q 'aaa' && return 1
+ ceph osd crush rm-device-class 1 || return 1
+ ceph osd tree | grep -q 'bbb' && return 1
+ ceph osd crush dump | grep -q '~bbb' && return 1
+ ceph osd crush tree --show-shadow | grep -q '~bbb' && return 1
+ ceph osd crush class ls | grep -q 'bbb' && return 1
+ ceph osd crush rm-device-class 2 || return 1
+ ceph osd tree | grep -q 'ccc' && return 1
+ ceph osd crush dump | grep -q '~ccc' && return 1
+ ceph osd crush tree --show-shadow | grep -q '~ccc' && return 1
+ ceph osd crush class ls | grep -q 'ccc' && return 1
+ ceph osd crush set-device-class asdf all || return 1
+ ceph osd tree | grep -q 'asdf' || return 1
+ ceph osd crush dump | grep -q '~asdf' || return 1
+ ceph osd crush tree --show-shadow | grep -q '~asdf' || return 1
+ ceph osd crush rm-device-class all || return 1
+ ceph osd tree | grep -q 'asdf' && return 1
+ ceph osd crush dump | grep -q '~asdf' && return 1
+ ceph osd crush tree --show-shadow | grep -q '~asdf' && return 1
+
ceph osd crush set-device-class abc osd.2 || return 1
ceph osd crush move osd.2 root=foo rack=foo-rack host=foo-host || return 1
out=`ceph osd tree |awk '$1 == 2 && $2 == "abc" {print $0}'`
return 1
fi
+ # verify 'crush move' too
+ ceph osd crush dump | grep -q 'foo~abc' || return 1
+ ceph osd crush tree --show-shadow | grep -q 'foo~abc' || return 1
+ ceph osd crush dump | grep -q 'foo-rack~abc' || return 1
+ ceph osd crush tree --show-shadow | grep -q 'foo-rack~abc' || return 1
+ ceph osd crush dump | grep -q 'foo-host~abc' || return 1
+ ceph osd crush tree --show-shadow | grep -q 'foo-host~abc' || return 1
+ ceph osd crush rm-device-class osd.2 || return 1
+ ceph osd crush dump | grep -q 'foo~abc' && return 1
+ ceph osd crush tree --show-shadow | grep -q 'foo~abc' && return 1
+ ceph osd crush dump | grep -q 'foo-rack~abc' && return 1
+ ceph osd crush tree --show-shadow | grep -q 'foo-rack~abc' && return 1
+ ceph osd crush dump | grep -q 'foo-host~abc' && return 1
+ ceph osd crush tree --show-shadow | grep -q 'foo-host~abc' && return 1
+ # restore class, so we can continue to test create-replicated
+ ceph osd crush set-device-class abc osd.2 || return 1
+
ceph osd crush rule create-replicated foo-rule foo host abc || return 1
# test class_is_in_use
return pair<string, string>();
}
-int CrushWrapper::get_immediate_parent_id(int id, int *parent) const
+int CrushWrapper::get_immediate_parent_id(int id,
+ int *parent,
+ parent_type_t choice) const
{
for (int bidx = 0; bidx < crush->max_buckets; bidx++) {
crush_bucket *b = crush->buckets[bidx];
if (b == 0)
continue;
- if (is_shadow_item(b->id))
+ if (choice == PARENT_NONSHADOW && is_shadow_item(b->id)) {
continue;
+ } else if (choice == PARENT_SHADOW && !is_shadow_item(b->id)) {
+ continue;
+ }
for (unsigned i = 0; i < b->size; i++) {
if (b->items[i] == id) {
*parent = b->id;
return 1;
}
+int CrushWrapper::remove_device_class(CephContext *cct, int id, ostream *ss)
+{
+ assert(ss);
+ const char *name = get_item_name(id);
+ if (!name) {
+ *ss << "osd." << id << " does not have a name";
+ return -ENOENT;
+ }
+
+ const char *class_name = get_item_class(id);
+ if (!class_name) {
+ *ss << "osd." << id << " has not been bound to a specific class yet";
+ return 0;
+ }
+ class_remove_item(id);
+
+ // note that there is no need to remove ourselves from shadow parent
+ // and reweight because we are going to destroy all shadow trees
+ // rebuild them all (if necessary) later.
+
+ // see if there is any osds that still reference this class
+ set<int> devices;
+ get_devices_by_class(class_name, &devices);
+ if (devices.empty()) {
+ // class has no more devices
+ remove_class_name(class_name);
+ }
+
+ int r = rebuild_roots_with_classes();
+ if (r < 0) {
+ *ss << "unable to rebuild roots with class '" << class_name << "' "
+ << "of osd." << id << ": " << cpp_strerror(r);
+ return r;
+ }
+ return 0;
+}
+
int CrushWrapper::device_class_clone(int original_id, int device_class, int *clone)
{
const char *item_name = get_item_name(original_id);
* FIXME: ambiguous for items that occur multiple times in the map
*/
pair<string,string> get_immediate_parent(int id, int *ret = NULL);
- int get_immediate_parent_id(int id, int *parent) const;
+
+ typedef enum {
+ PARENT_NONSHADOW,
+ PARENT_SHADOW,
+ PARENT_ALL,
+ } parent_type_t;
+
+ int get_immediate_parent_id(int id,
+ int *parent,
+ parent_type_t choice = PARENT_NONSHADOW) const;
/**
* return ancestor of the given type, or 0 if none
}
int update_device_class(int id, const string& class_name, const string& name, ostream *ss);
+ int remove_device_class(CephContext *cct, int id, ostream *ss);
int device_class_clone(int original, int device_class, int *clone);
bool class_is_in_use(int class_id, ostream *ss = nullptr);
int populate_classes();
"set the <class> of the osd(s) <id> [<id>...]," \
"or use <all|any|*> to set all.", \
"osd", "rw", "cli,rest")
+COMMAND("osd crush rm-device-class " \
+ "name=ids,type=CephString,n=N", \
+ "remove class of the osd(s) <id> [<id>...]," \
+ "or use <all|any|*> to remove all.", \
+ "osd", "rw", "cli,rest")
COMMAND("osd crush create-or-move " \
"name=id,type=CephOsdName " \
"name=weight,type=CephFloat,range=0.0 " \
return true;
}
+ } else if (prefix == "osd crush rm-device-class") {
+ bool stop = false;
+ vector<string> idvec;
+ cmd_getval(g_ceph_context, cmdmap, "ids", idvec);
+ CrushWrapper newcrush;
+ _get_pending_crush(newcrush);
+ set<int> updated;
+
+ for (unsigned j = 0; j < idvec.size() && !stop; j++) {
+ set<int> osds;
+
+ // wildcard?
+ if (j == 0 &&
+ (idvec[0] == "any" || idvec[0] == "all" || idvec[0] == "*")) {
+ osdmap.get_all_osds(osds);
+ stop = true;
+ } else {
+ // try traditional single osd way
+ long osd = parse_osd_id(idvec[j].c_str(), &ss);
+ if (osd < 0) {
+ // ss has reason for failure
+ ss << ", unable to parse osd id:\"" << idvec[j] << "\". ";
+ err = -EINVAL;
+ goto reply;
+ }
+ osds.insert(osd);
+ }
+
+ for (auto &osd : osds) {
+ if (!osdmap.exists(osd)) {
+ ss << "osd." << osd << " does not exist. ";
+ continue;
+ }
+
+ auto class_name = newcrush.get_item_class(osd);
+ stringstream ts;
+ if (!class_name) {
+ ss << "osd." << osd << " belongs to no class, ";
+ continue;
+ }
+ // note that we do not verify if class_is_in_use here
+ // in case the device is misclassified and user wants
+ // to overridely reset...
+
+ err = newcrush.remove_device_class(g_ceph_context, osd, &ss);
+ if (err < 0) {
+ // ss has reason for failure
+ goto reply;
+ }
+ updated.insert(osd);
+ }
+ }
+
+ if (!updated.empty()) {
+ pending_inc.crush.clear();
+ newcrush.encode(pending_inc.crush, mon->get_quorum_con_features());
+ ss << "done removing class of osd(s): " << updated;
+ getline(ss, rs);
+ wait_for_finished_proposal(op,
+ new Monitor::C_Command(mon,op, 0, rs, get_last_committed() + 1));
+ return true;
+ }
+
} else if (prefix == "osd crush add-bucket") {
// os crush add-bucket <name> <type>
string name, typestr;