From d792e8d528768eeccc6ad71af5aa3fd81780eb2e Mon Sep 17 00:00:00 2001 From: xie xingguo Date: Thu, 10 Aug 2017 17:28:04 +0800 Subject: [PATCH] crush: "osd crush class rename" support In 076a6abd80cc90ebcb901f908f880ef030721b2a I killed the 'class rename' command and thought it was totally useless but I was wrong. Consider the following user case: (1) randomly choose some OSDs(e.g., from different hosts) and try to make them for private use only, say, by grouping them into 'pool1' (2) ceph osd crush set-device-class pool1 'OSDs from (1)' (3) ceph osd crush rule create-replicated rule_for_pool1 default host pool1 (4) ceph osd pool rename pool1 pool2 (5) ceph osd crush class rename pool1 pool2 From the above user case, we need to safely change a pool name without worrying any risk of data migration. That is why the 'osd crush class rename' command is still needed here. Signed-off-by: xie xingguo --- qa/standalone/crush/crush-classes.sh | 12 ++++++++++ src/crush/CrushWrapper.cc | 36 ++++++++++++++++++++++++++++ src/crush/CrushWrapper.h | 1 + src/mon/MonCommands.h | 5 ++++ src/mon/OSDMonitor.cc | 36 ++++++++++++++++++++++++++++ 5 files changed, 90 insertions(+) diff --git a/qa/standalone/crush/crush-classes.sh b/qa/standalone/crush/crush-classes.sh index 24696fcd31935..160f9d8c1a12d 100755 --- a/qa/standalone/crush/crush-classes.sh +++ b/qa/standalone/crush/crush-classes.sh @@ -201,6 +201,18 @@ function TEST_mon_classes() { # test set-device-class implicitly change class ceph osd crush set-device-class hdd osd.0 || return 1 expect_failure $dir EBUSY ceph osd crush set-device-class nvme osd.0 || return 1 + + # test class rename + ceph osd crush rm-device-class all || return 1 + ceph osd crush set-device-class class_1 all || return 1 + ceph osd crush class ls | grep 'class_1' || return 1 + ceph osd crush tree --show-shadow | grep 'class_1' || return 1 + ceph osd crush rule create-replicated class_1_rule default host class_1 || return 1 + ceph osd crush class rename class_1 class_2 + ceph osd crush class ls | grep 'class_1' && return 1 + ceph osd crush tree --show-shadow | grep 'class_1' && return 1 + ceph osd crush class ls | grep 'class_2' || return 1 + ceph osd crush tree --show-shadow | grep 'class_2' || return 1 } main crush-classes "$@" diff --git a/src/crush/CrushWrapper.cc b/src/crush/CrushWrapper.cc index 8f2d63e77c8cc..26350e63975f8 100644 --- a/src/crush/CrushWrapper.cc +++ b/src/crush/CrushWrapper.cc @@ -1364,6 +1364,42 @@ int CrushWrapper::get_parent_of_type(int item, int type) const return item; } +int CrushWrapper::rename_class(const string& srcname, const string& dstname) +{ + auto i = class_rname.find(srcname); + if (i == class_rname.end()) + return -ENOENT; + auto j = class_rname.find(dstname); + if (j != class_rname.end()) + return -EEXIST; + + int class_id = i->second; + assert(class_name.count(class_id)); + // rename any shadow buckets of old class name + for (auto &it: class_map) { + if (it.first < 0 && it.second == class_id) { + string old_name = get_item_name(it.first); + size_t pos = old_name.find("~"); + assert(pos != string::npos); + string name_no_class = old_name.substr(0, pos); + string old_class_name = old_name.substr(pos + 1); + assert(old_class_name == srcname); + string new_name = name_no_class + "~" + dstname; + // we do not use set_item_name + // because the name is intentionally invalid + name_map[it.first] = new_name; + have_rmaps = false; + } + } + + // rename class + class_rname.erase(srcname); + class_name.erase(class_id); + class_rname[dstname] = class_id; + class_name[class_id] = dstname; + return 0; +} + int CrushWrapper::populate_classes( const std::map>& old_class_bucket) { diff --git a/src/crush/CrushWrapper.h b/src/crush/CrushWrapper.h index 737ba583ecece..3808db08062ca 100644 --- a/src/crush/CrushWrapper.h +++ b/src/crush/CrushWrapper.h @@ -1214,6 +1214,7 @@ public: const std::map>& old_class_bucket, const std::set& used_ids, int *clone); + int rename_class(const string& srcname, const string& dstname); int populate_classes( const std::map>& old_class_bucket); bool _class_is_dead(int class_id); diff --git a/src/mon/MonCommands.h b/src/mon/MonCommands.h index ef3c094b2339e..57845ab3d27f1 100644 --- a/src/mon/MonCommands.h +++ b/src/mon/MonCommands.h @@ -555,6 +555,11 @@ COMMAND("osd crush rm-device-class " \ "remove class of the osd(s) [...]," \ "or use to remove all.", \ "osd", "rw", "cli,rest") +COMMAND("osd crush class rename " \ + "name=srcname,type=CephString,goodchars=[A-Za-z0-9-_] " \ + "name=dstname,type=CephString,goodchars=[A-Za-z0-9-_]", \ + "rename crush device class to ", \ + "osd", "rw", "cli,rest") COMMAND("osd crush create-or-move " \ "name=id,type=CephOsdName " \ "name=weight,type=CephFloat,range=0.0 " \ diff --git a/src/mon/OSDMonitor.cc b/src/mon/OSDMonitor.cc index 1d71a2070add9..b1d509b21fc7d 100644 --- a/src/mon/OSDMonitor.cc +++ b/src/mon/OSDMonitor.cc @@ -7454,7 +7454,43 @@ bool OSDMonitor::prepare_command_impl(MonOpRequestRef op, new Monitor::C_Command(mon,op, 0, rs, get_last_committed() + 1)); return true; } + } else if (prefix == "osd crush class rename") { + string srcname, dstname; + if (!cmd_getval(g_ceph_context, cmdmap, "srcname", srcname)) { + err = -EINVAL; + goto reply; + } + if (!cmd_getval(g_ceph_context, cmdmap, "dstname", dstname)) { + err = -EINVAL; + goto reply; + } + CrushWrapper newcrush; + _get_pending_crush(newcrush); + + if (!newcrush.class_exists(srcname)) { + err = -ENOENT; + ss << "class '" << srcname << "' does not exist"; + goto reply; + } + + if (newcrush.class_exists(dstname)) { + err = -EEXIST; + ss << "class '" << dstname << "' already exists"; + goto reply; + } + + err = newcrush.rename_class(srcname, dstname); + if (err < 0) { + ss << "fail to rename '" << srcname << "' to '" << dstname << "' : " + << cpp_strerror(err); + goto reply; + } + + pending_inc.crush.clear(); + newcrush.encode(pending_inc.crush, mon->get_quorum_con_features()); + ss << "rename class '" << srcname << "' to '" << dstname << "'"; + goto update; } else if (prefix == "osd crush add-bucket") { // os crush add-bucket string name, typestr; -- 2.39.5