]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
mon: add the erasure-code-profile {set,get,rm,ls} MonCommand
authorLoic Dachary <loic@dachary.org>
Sun, 16 Mar 2014 12:00:50 +0000 (13:00 +0100)
committerLoic Dachary <loic@dachary.org>
Mon, 17 Mar 2014 23:23:31 +0000 (00:23 +0100)
"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 <loic@dachary.org>
src/mon/MonCommands.h
src/mon/OSDMonitor.cc
src/mon/OSDMonitor.h
src/test/Makefile.am
src/test/mon/osd-erasure-code-profile.sh [new file with mode: 0755]
src/test/pybind/test_ceph_argparse.py

index b272f0785e29109d612ccbf7800172765f445505..177d5a3d1497b4803ced963ca2dad4ad4c990ab9 100644 (file)
@@ -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 <name> with [<key[=value]> ...] 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 <name>", \
+       "osd", "r", "cli,rest")
+COMMAND("osd erasure-code-profile rm " \
+       "name=name,type=CephString,goodchars=[A-Za-z0-9-_.]", \
+       "remove erasure code profile <name>", \
+       "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 <key>", "osd", "rw", "cli,rest")
index 272a8e2afbfa059112294cdd8a3000175de73a6d..464b769deb5a8e3917d963b03a50371a5a6ea502 100644 (file)
@@ -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<string,map<string,string> > &profiles =
+      osdmap.get_erasure_code_profiles();
+    if (f)
+      f->open_array_section("erasure-code-profiles");
+    for(map<string,map<string,string> >::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<string,string> &profile = osdmap.get_erasure_code_profile(name);
+    if (f)
+      f->open_object_section("profile");
+    for (map<string,string>::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<string> &properties,
-                                       map<string,string> *properties_map,
-                                       stringstream &ss)
+bool OSDMonitor::erasure_code_profile_in_use(const map<int64_t, pg_pool_t> &pools,
+                                            const string &profile,
+                                            ostream &ss)
+{
+  bool found = false;
+  for (map<int64_t, pg_pool_t>::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<string> &erasure_code_profile,
+                                          map<string,string> *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<string> 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<string,string> 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)
index 4d1597eb6bfb4157e3190e252dd44976fa41abf1..1747f98fa37261776d53306cef2823ae672ddeda 100644 (file)
@@ -259,6 +259,12 @@ private:
                                 const map<string,string> &properties,
                                 int *crush_ruleset,
                                 stringstream &ss);
+  bool erasure_code_profile_in_use(const map<int64_t, pg_pool_t> &pools,
+                                  const string &profile,
+                                  ostream &ss);
+  int parse_erasure_code_profile(const vector<string> &erasure_code_profile,
+                                map<string,string> *erasure_code_profile_map,
+                                stringstream &ss);
   int prepare_pool_size(const unsigned pool_type,
                        const map<string,string> &properties,
                        unsigned *size,
index 5e4cd955d0a9f76b2cbb5ae26400caf9b850992d..71f2a7feb9f36258bb1f43102226d09b51320640 100644 (file)
@@ -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 (executable)
index 0000000..32bca9b
--- /dev/null
@@ -0,0 +1,177 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 Cloudwatt <libre.licensing@cloudwatt.com>
+#
+# Author: Loic Dachary <loic@dachary.org>
+#
+# 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>$profile</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 '<plugin>jerasure</plugin>' || 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:
index f26a854363125b9ceaf5e856ec742defb311ddea..2f445a4d1e88914953f1766f6dc7030fcc017578 100755 (executable)
@@ -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',