From: Loic Dachary Date: Sun, 16 Mar 2014 12:00:50 +0000 (+0100) Subject: mon: add the erasure-code-profile {set,get,rm,ls} MonCommand X-Git-Tag: v0.79~136^2~10 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=04d2fd1003145846a2959c565c4cab2488dd44c1;p=ceph.git mon: add the erasure-code-profile {set,get,rm,ls} MonCommand "erasure-code-profile set" parses the key=value pairs given in argument and stores them in OSDMap::erasure_code_profiles. The "erasure-code-profile get" supports plain text display if the Formatter is not set (or invalid). erasure-code-profile set myprofile a=b c=d is stored as OSDMap::erasure_code_profile["myprofile"] = {"a":"b", "c":"d"} "erasure-code-profile ls" displays a list of the profile names from OSDMap::erasure_code_profiles "erasure-code-profile rm" removes a profile, if it exists. Displays a message and succeeds if it does not exist. Signed-off-by: Loic Dachary --- diff --git a/src/mon/MonCommands.h b/src/mon/MonCommands.h index b272f0785e29..177d5a3d1497 100644 --- a/src/mon/MonCommands.h +++ b/src/mon/MonCommands.h @@ -455,6 +455,22 @@ COMMAND("osd setmaxosd " \ "set new maximum osd value", "osd", "rw", "cli,rest") COMMAND("osd pause", "pause osd", "osd", "rw", "cli,rest") COMMAND("osd unpause", "unpause osd", "osd", "rw", "cli,rest") +COMMAND("osd erasure-code-profile set " \ + "name=name,type=CephString,goodchars=[A-Za-z0-9-_.] " \ + "name=profile,type=CephString,n=N,req=false,goodchars=[A-Za-z0-9-_.=]", \ + "create erasure code profile with [ ...] pairs. Add a --force at the end to override an existing profile (VERY DANGEROUS)", \ + "osd", "rw", "cli,rest") +COMMAND("osd erasure-code-profile get " \ + "name=name,type=CephString,goodchars=[A-Za-z0-9-_.]", \ + "get erasure code profile ", \ + "osd", "r", "cli,rest") +COMMAND("osd erasure-code-profile rm " \ + "name=name,type=CephString,goodchars=[A-Za-z0-9-_.]", \ + "remove erasure code profile ", \ + "osd", "rw", "cli,rest") +COMMAND("osd erasure-code-profile ls", \ + "list all erasure code profiles", \ + "osd", "r", "cli,rest") COMMAND("osd set " \ "name=key,type=CephChoices,strings=pause|noup|nodown|noout|noin|nobackfill|norecover|noscrub|nodeep-scrub|notieragent", \ "set ", "osd", "rw", "cli,rest") diff --git a/src/mon/OSDMonitor.cc b/src/mon/OSDMonitor.cc index 272a8e2afbfa..464b769deb5a 100644 --- a/src/mon/OSDMonitor.cc +++ b/src/mon/OSDMonitor.cc @@ -2636,6 +2636,52 @@ stats_out: f->flush(rs); rs << "\n"; rdata.append(rs.str()); + } else if (prefix == "osd erasure-code-profile ls") { + const map > &profiles = + osdmap.get_erasure_code_profiles(); + if (f) + f->open_array_section("erasure-code-profiles"); + for(map >::const_iterator i = profiles.begin(); + i != profiles.end(); + i++) { + if (f) + f->dump_string("profile", i->first.c_str()); + else + rdata.append(i->first + "\n"); + } + if (f) { + f->close_section(); + ostringstream rs; + f->flush(rs); + rs << "\n"; + rdata.append(rs.str()); + } + } else if (prefix == "osd erasure-code-profile get") { + string name; + cmd_getval(g_ceph_context, cmdmap, "name", name); + if (!osdmap.has_erasure_code_profile(name)) { + ss << "unknown erasure code profile '" << name << "'"; + r = -ENOENT; + goto reply; + } + const map &profile = osdmap.get_erasure_code_profile(name); + if (f) + f->open_object_section("profile"); + for (map::const_iterator i = profile.begin(); + i != profile.end(); + i++) { + if (f) + f->dump_string(i->first.c_str(), i->second.c_str()); + else + rdata.append(i->first + "=" + i->second + "\n"); + } + if (f) { + f->close_section(); + ostringstream rs; + f->flush(rs); + rs << "\n"; + rdata.append(rs.str()); + } } else { // try prepare update return false; @@ -2904,10 +2950,28 @@ int OSDMonitor::check_cluster_features(uint64_t features, return 0; } -int OSDMonitor::prepare_pool_properties(const unsigned pool_type, - const vector &properties, - map *properties_map, - stringstream &ss) +bool OSDMonitor::erasure_code_profile_in_use(const map &pools, + const string &profile, + ostream &ss) +{ + bool found = false; + for (map::const_iterator p = pools.begin(); + p != pools.end(); + ++p) { + if (p->second.erasure_code_profile == profile) { + ss << osdmap.pool_name[p->first] << " "; + found = true; + } + } + if (found) { + ss << "pool(s) are using the erasure code profile '" << profile << "'"; + } + return found; +} + +int OSDMonitor::parse_erasure_code_profile(const vector &erasure_code_profile, + map *erasure_code_profile_map, + stringstream &ss) { if (pool_type == pg_pool_t::TYPE_ERASURE) { int r = get_str_map(g_conf->osd_pool_default_erasure_code_properties, @@ -3889,6 +3953,73 @@ bool OSDMonitor::prepare_command_impl(MMonCommand *m, get_last_committed() + 1)); return true; + } else if (prefix == "osd erasure-code-profile rm") { + string name; + cmd_getval(g_ceph_context, cmdmap, "name", name); + + if (erasure_code_profile_in_use(pending_inc.new_pools, name, ss)) + goto wait; + + if (erasure_code_profile_in_use(osdmap.pools, name, ss)) { + err = -EBUSY; + goto reply; + } + + if (osdmap.has_erasure_code_profile(name) || + pending_inc.new_erasure_code_profiles.count(name)) { + if (osdmap.has_erasure_code_profile(name)) { + pending_inc.old_erasure_code_profiles.push_back(name); + } else { + dout(20) << "erasure code profile rm " << name << ": creation canceled" << dendl; + pending_inc.new_erasure_code_profiles.erase(name); + } + + getline(ss, rs); + wait_for_finished_proposal(new Monitor::C_Command(mon, m, 0, rs, + get_last_committed() + 1)); + return true; + } else { + ss << "erasure-code-profile " << name << " does not exist"; + err = 0; + goto reply; + } + + } else if (prefix == "osd erasure-code-profile set") { + string name; + cmd_getval(g_ceph_context, cmdmap, "name", name); + vector profile; + cmd_getval(g_ceph_context, cmdmap, "profile", profile); + bool force; + if (profile.size() > 0 && profile.back() == "--force") { + profile.pop_back(); + force = true; + } else { + force = false; + } + map profile_map; + err = parse_erasure_code_profile(profile, &profile_map, ss); + if (err) + goto reply; + + if (osdmap.has_erasure_code_profile(name) && !force) { + err = -EPERM; + ss << "will not override erasure code profile " << name; + goto reply; + } + + if (pending_inc.has_erasure_code_profile(name)) { + dout(20) << "erasure code profile " << name << " try again" << dendl; + goto wait; + } else { + dout(20) << "erasure code profile " << name << " set" << dendl; + pending_inc.set_erasure_code_profile(name, profile_map); + } + + getline(ss, rs); + wait_for_finished_proposal(new Monitor::C_Command(mon, m, 0, rs, + get_last_committed() + 1)); + return true; + } else if (prefix == "osd crush rule create-erasure") { err = check_cluster_features(CEPH_FEATURE_CRUSH_V2, ss); if (err == -EAGAIN) diff --git a/src/mon/OSDMonitor.h b/src/mon/OSDMonitor.h index 4d1597eb6bfb..1747f98fa372 100644 --- a/src/mon/OSDMonitor.h +++ b/src/mon/OSDMonitor.h @@ -259,6 +259,12 @@ private: const map &properties, int *crush_ruleset, stringstream &ss); + bool erasure_code_profile_in_use(const map &pools, + const string &profile, + ostream &ss); + int parse_erasure_code_profile(const vector &erasure_code_profile, + map *erasure_code_profile_map, + stringstream &ss); int prepare_pool_size(const unsigned pool_type, const map &properties, unsigned *size, diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 5e4cd955d0a9..71f2a7feb9f3 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -236,6 +236,7 @@ check_SCRIPTS += \ test/mon/osd-pool-create.sh \ test/mon/misc.sh \ test/mon/osd-crush.sh \ + test/mon/osd-erasure-code-profile.sh \ test/mon/mkfs.sh \ test/ceph-disk.sh \ test/mon/mon-handle-forward.sh \ diff --git a/src/test/mon/osd-erasure-code-profile.sh b/src/test/mon/osd-erasure-code-profile.sh new file mode 100755 index 000000000000..32bca9bed0e4 --- /dev/null +++ b/src/test/mon/osd-erasure-code-profile.sh @@ -0,0 +1,177 @@ +#!/bin/bash +# +# Copyright (C) 2014 Cloudwatt +# +# Author: Loic Dachary +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU Library Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Library Public License for more details. +# +source test/mon/mon-test-helpers.sh + +function run() { + local dir=$1 + + export CEPH_ARGS + CEPH_ARGS+="--fsid=$(uuidgen) --auth-supported=none " + CEPH_ARGS+="--mon-host=127.0.0.1 " + + local id=a + call_TEST_functions $dir $id --public-addr 127.0.0.1 || return 1 +} + +function SHARE_MON_TEST_set() { + local dir=$1 + local id=$2 + + local profile=myprofile + # + # no key=value pairs : use the default configuration + # + ./ceph osd erasure-code-profile set $profile 2>&1 || return 1 + ./ceph osd erasure-code-profile get $profile | \ + grep plugin=jerasure || return 1 + ./ceph osd erasure-code-profile rm $profile + # + # key=value pairs override the default + # + ./ceph osd erasure-code-profile set $profile \ + key=value plugin=example || return 1 + ./ceph osd erasure-code-profile get $profile | \ + grep -e key=value -e plugin=example || return 1 + # + # --force is required to override an existing profile + # + ! ./ceph osd erasure-code-profile set $profile > $dir/out 2>&1 || return 1 + grep 'will not override' $dir/out || return 1 + ./ceph osd erasure-code-profile set $profile key=other --force || return 1 + ./ceph osd erasure-code-profile get $profile | \ + grep key=other || return 1 + + ./ceph osd erasure-code-profile rm $profile # cleanup +} + +function SHARE_MON_TEST_set_pending() { + local dir=$1 + local id=$2 + + # try again if the profile is pending + local profile=profile + # add to the pending OSD map without triggering a paxos proposal + result=$(echo '{"prefix":"osdmonitor_prepare_command","prepare":"osd erasure-code-profile set","name":"'$profile'"}' | nc -U $dir/$id/ceph-mon.$id.asok | cut --bytes=5-) + test $result = true || return 1 + ./ceph osd erasure-code-profile set $profile --force || return 1 + grep "$profile try again" $dir/$id/log || return 1 + + ./ceph osd erasure-code-profile rm $profile # cleanup +} + +function SHARE_MON_TEST_ls() { + local dir=$1 + local id=$2 + + local profile=myprofile + ! ./ceph osd erasure-code-profile ls | grep $profile || return 1 + ./ceph osd erasure-code-profile set $profile 2>&1 || return 1 + ./ceph osd erasure-code-profile ls | grep $profile || return 1 + ./ceph --format xml osd erasure-code-profile ls | \ + grep "$profile" || return 1 + + ./ceph osd erasure-code-profile rm $profile # cleanup +} + +function SHARE_MON_TEST_rm() { + local dir=$1 + local id=$2 + + local profile=myprofile + ./ceph osd erasure-code-profile set $profile 2>&1 || return 1 + ./ceph osd erasure-code-profile ls | grep $profile || return 1 + ./ceph osd erasure-code-profile rm $profile || return 1 + ! ./ceph osd erasure-code-profile ls | grep $profile || return 1 + ./ceph osd erasure-code-profile rm WRONG 2>&1 | \ + grep "WRONG does not exist" || return 1 + + ./ceph osd erasure-code-profile set $profile || return 1 + ./ceph osd pool create poolname 12 12 erasure $profile || return 1 + ! ./ceph osd erasure-code-profile rm $profile > $dir/out 2>&1 || return 1 + grep "poolname.*using.*$profile" $dir/out || return 1 + ./ceph osd pool delete poolname poolname --yes-i-really-really-mean-it || return 1 + ./ceph osd erasure-code-profile rm $profile || return 1 + + ./ceph osd erasure-code-profile rm $profile # cleanup +} + +function SHARE_MON_TEST_rm_pending() { + local dir=$1 + local id=$2 + + # try again if the profile is pending + local profile=myprofile + # add to the pending OSD map without triggering a paxos proposal + result=$(echo '{"prefix":"osdmonitor_prepare_command","prepare":"osd erasure-code-profile set","name":"'$profile'"}' | nc -U $dir/$id/ceph-mon.$id.asok | cut --bytes=5-) + test $result = true || return 1 + ./ceph osd erasure-code-profile rm $profile || return 1 + grep "$profile: creation canceled" $dir/$id/log || return 1 +} + +function SHARE_MON_TEST_get() { + local dir=$1 + local id=$2 + + local default_profile=default + ./ceph osd erasure-code-profile get $default_profile | \ + grep plugin=jerasure || return 1 + ./ceph --format xml osd erasure-code-profile get $default_profile | \ + grep 'jerasure' || return 1 + ! ./ceph osd erasure-code-profile get WRONG > $dir/out 2>&1 || return 1 + grep -q "unknown erasure code profile 'WRONG'" $dir/out || return 1 +} + +function TEST_format_invalid() { + local dir=$1 + + local profile=profile + # osd_pool_default_erasure-code-profile is + # valid JSON but not of the expected type + run_mon $dir a --public-addr 127.0.0.1 \ + --osd_pool_default_erasure-code-profile 1 + ! ./ceph osd erasure-code-profile set $profile > $dir/out 2>&1 || return 1 + cat $dir/out + grep 'must be a JSON object' $dir/out || return 1 +} + +function TEST_format_json() { + local dir=$1 + + # osd_pool_default_erasure-code-profile is JSON + expected='"plugin":"example"' + run_mon $dir a --public-addr 127.0.0.1 \ + --osd_pool_default_erasure-code-profile "{$expected}" + ./ceph --format json osd erasure-code-profile get default | \ + grep "$expected" || return 1 +} + +function TEST_format_plain() { + local dir=$1 + + # osd_pool_default_erasure-code-profile is plain text + expected='"plugin":"example"' + run_mon $dir a --public-addr 127.0.0.1 \ + --osd_pool_default_erasure-code-profile "plugin=example" + ./ceph --format json osd erasure-code-profile get default | \ + grep "$expected" || return 1 +} + +main osd-erasure-code-profile + +# Local Variables: +# compile-command: "cd ../.. ; make -j4 && test/mon/osd-erasure-code-profile.sh" +# End: diff --git a/src/test/pybind/test_ceph_argparse.py b/src/test/pybind/test_ceph_argparse.py index f26a85436312..2f445a4d1e88 100755 --- a/src/test/pybind/test_ceph_argparse.py +++ b/src/test/pybind/test_ceph_argparse.py @@ -770,6 +770,55 @@ class TestOSD(TestArgparse): def test_unpause(self): self.check_no_arg('osd', 'unpause') + def test_erasure_code_profile_set(self): + self.assert_valid_command(['osd', 'erasure-code-profile', 'set', + 'name']) + self.assert_valid_command(['osd', 'erasure-code-profile', 'set', + 'name', 'A=B']) + self.assert_valid_command(['osd', 'erasure-code-profile', 'set', + 'name', 'A=B', 'C=D']) + assert_equal({}, validate_command(sigdict, ['osd', + 'erasure-code-profile', + 'set'])) + assert_equal({}, validate_command(sigdict, ['osd', + 'erasure-code-profile', + 'set', + '!!!!'])) + assert_equal({}, validate_command(sigdict, ['osd', + 'erasure-code-profile', + 'set', + 'name', + '!!!!'])) + + def test_erasure_code_profile_get(self): + self.assert_valid_command(['osd', 'erasure-code-profile', 'get', + 'name']) + assert_equal({}, validate_command(sigdict, ['osd', + 'erasure-code-profile', + 'get'])) + assert_equal({}, validate_command(sigdict, ['osd', + 'erasure-code-profile', + 'get', + '!!!!'])) + + def test_erasure_code_profile_rm(self): + self.assert_valid_command(['osd', 'erasure-code-profile', 'rm', + 'name']) + assert_equal({}, validate_command(sigdict, ['osd', + 'erasure-code-profile', + 'rm'])) + assert_equal({}, validate_command(sigdict, ['osd', + 'erasure-code-profile', + 'rm', + '!!!!'])) + + def test_erasure_code_profile_ls(self): + self.assert_valid_command(['osd', 'erasure-code-profile', 'ls']) + assert_equal({}, validate_command(sigdict, ['osd', + 'erasure-code-profile', + 'ls', + 'toomany'])) + def test_set_unset(self): for action in ('set', 'unset'): for flag in ('pause', 'noup', 'nodown', 'noout', 'noin',