]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
mon: ConfigKeyService: stash config keys on the monitor 150/head
authorJoao Eduardo Luis <joao.luis@inktank.com>
Fri, 1 Mar 2013 22:34:16 +0000 (22:34 +0000)
committerJoao Eduardo Luis <joao.luis@inktank.com>
Thu, 28 Mar 2013 23:36:38 +0000 (23:36 +0000)
Building up on the Single-Paxos and our existing k/v store that backs
the monitor, we now introduce a simple service so that the monitors
act as a generic k/v store available to the cluster, in which a user
can stash (and later obtain) configuration keys at his own discretion.

Users can put, get, delete, list and check for values using the
following commands:

 - ceph config-key put <key> [<value>]
  or
 - ceph config-key put <key> [-i <in-file>]
  with 'value' and 'in-file' being optional; if these are not specified,
  'put' will act as 'touch' if 'key' does not exist, or will overwrite
  the value of 'key' with a zero byte value (i.e., truncates the
  contents of the value to zero)

 - ceph config-key get <key>
  or
 - ceph config-key get <key> -o <out-file>

 - ceph config-key delete <key>

 - ceph config-key list [-o <out-file]

 - ceph config-key exists <key>

Fixes: #4313
Signed-off-by: Joao Eduardo Luis <joao.luis@inktank.com>
src/Makefile.am
src/common/config_opts.h
src/mon/ConfigKeyService.cc [new file with mode: 0644]
src/mon/ConfigKeyService.h [new file with mode: 0644]
src/mon/Monitor.cc
src/mon/Monitor.h
src/mon/QuorumService.h

index e05f05c0939ed42539854a348d34dff4ddcee220..92739ce3bfa1b8f1a3923144eb6e8239d44eaef0 100644 (file)
@@ -1381,7 +1381,8 @@ libmon_a_SOURCES = \
        mon/MonitorStore.cc \
        os/LevelDBStore.cc \
        mon/HealthMonitor.cc \
-       mon/DataHealthService.cc
+       mon/DataHealthService.cc \
+       mon/ConfigKeyService.cc
 libmon_a_CXXFLAGS= ${AM_CXXFLAGS}
 noinst_LIBRARIES += libmon.a
 
@@ -1871,6 +1872,7 @@ noinst_HEADERS = \
         mon/DataHealthService.h\
         mon/Elector.h\
        mon/LogMonitor.h\
+       mon/ConfigKeyService.h\
         mon/HealthMonitor.h\
         mon/HealthService.h\
         mon/MDSMonitor.h\
index c15a52f0e3938e1d9ab446cedb53a3a6f0b28a78..e8278bf46325dd21a08584ebba99e77476031222 100644 (file)
@@ -165,6 +165,7 @@ OPTION(mon_max_log_entries_per_event, OPT_INT, 4096)
 OPTION(mon_health_data_update_interval, OPT_FLOAT, 60.0)
 OPTION(mon_data_avail_crit, OPT_INT, 5)
 OPTION(mon_data_avail_warn, OPT_INT, 30)
+OPTION(mon_config_key_max_entry_size, OPT_INT, 4096) // max num bytes per config-key entry
 OPTION(mon_sync_trim_timeout, OPT_DOUBLE, 30.0)
 OPTION(mon_sync_heartbeat_timeout, OPT_DOUBLE, 30.0)
 OPTION(mon_sync_heartbeat_interval, OPT_DOUBLE, 5.0)
diff --git a/src/mon/ConfigKeyService.cc b/src/mon/ConfigKeyService.cc
new file mode 100644 (file)
index 0000000..3319d9e
--- /dev/null
@@ -0,0 +1,220 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank, Inc
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+
+#include <sstream>
+#include <stdlib.h>
+#include <limits.h>
+
+#include <boost/intrusive_ptr.hpp>
+#include "include/assert.h"
+
+#include "mon/Monitor.h"
+#include "mon/QuorumService.h"
+#include "mon/ConfigKeyService.h"
+#include "mon/MonitorDBStore.h"
+
+#include "common/config.h"
+
+#define dout_subsys ceph_subsys_mon
+#undef dout_prefix
+#define dout_prefix _prefix(_dout, mon, this)
+static ostream& _prefix(std::ostream *_dout, const Monitor *mon,
+                        const ConfigKeyService *service) {
+  return *_dout << "mon." << mon->name << "@" << mon->rank
+               << "(" << mon->get_state_name() << ")." << service->get_name()
+                << "(" << service->get_epoch() << ") ";
+}
+
+const string ConfigKeyService::STORE_PREFIX = "mon_config_key";
+
+int ConfigKeyService::store_get(string key, bufferlist &bl)
+{
+  if (!store_exists(key))
+    return -ENOENT;
+
+  return mon->store->get(STORE_PREFIX, key, bl);
+}
+
+void ConfigKeyService::store_put(string key, bufferlist &bl, Context *cb)
+{
+  bufferlist proposal_bl;
+  MonitorDBStore::Transaction t;
+  t.put(STORE_PREFIX, key, bl);
+  t.encode(proposal_bl);
+
+  paxos->propose_new_value(proposal_bl, cb);
+}
+
+void ConfigKeyService::store_delete(string key, Context *cb)
+{
+  bufferlist proposal_bl;
+  MonitorDBStore::Transaction t;
+  t.erase(STORE_PREFIX, key);
+  t.encode(proposal_bl);
+  paxos->propose_new_value(proposal_bl, cb);
+}
+
+bool ConfigKeyService::store_exists(string key)
+{
+  return mon->store->exists(STORE_PREFIX, key);
+}
+
+void ConfigKeyService::store_list(stringstream &ss)
+{
+  KeyValueDB::Iterator iter =
+    mon->store->get_iterator(STORE_PREFIX);
+
+  JSONFormatter f(true);
+  f.open_array_section("keys");
+
+  while (iter->valid()) {
+    string key(iter->key());
+    f.dump_string("key", key);
+    iter->next();
+  }
+  f.close_section();
+  f.flush(ss);
+}
+
+
+bool ConfigKeyService::service_dispatch(Message *m)
+{
+  dout(10) << __func__ << " " << *m << dendl;
+  if (!in_quorum()) {
+    dout(1) << __func__ << " not in quorum -- ignore message" << dendl;
+    m->put();
+    return false;
+  }
+
+  assert(m != NULL);
+  assert(m->get_type() == MSG_MON_COMMAND);
+
+  MMonCommand *cmd = static_cast<MMonCommand*>(m);
+
+  assert(!cmd->cmd.empty());
+  assert(cmd->cmd[0] == "config-key");
+
+  int ret = 0;
+  stringstream ss;
+  bufferlist rdata;
+
+  if (cmd->cmd.size() < 2) {
+    ret = -EINVAL;
+    ss << "usage: config-key <get|put|list|exists|delete> [<key>]";
+    goto out;
+  }
+
+  if (cmd->cmd[1] == "get") {
+    if (cmd->cmd.size() != 3) {
+      ret = -EINVAL;
+      ss << "usage: config-key get <key> -o <file>";
+      goto out;
+    }
+    ret = store_get(cmd->cmd[2], rdata);
+    if (ret < 0) {
+      assert(!rdata.length());
+      ss << "error obtaining '" << cmd->cmd[2] << "': "
+         << cpp_strerror(ret);
+      goto out;
+    }
+    ss << "obtained '" << cmd->cmd[2] << "'";
+  } else if (cmd->cmd[1] == "put") {
+    if (!mon->is_leader()) {
+      mon->forward_request_leader(cmd);
+      // we forward the message; so return now.
+      return true;
+    }
+
+    bufferlist data;
+    if (cmd->cmd.size() < 3 || cmd->cmd.size() > 4) {
+      ret = -EINVAL;
+      ss << "usage: store put <key> [-i <file>|<value>]";
+      goto out;
+    } else if (cmd->cmd.size() == 4) {
+      // they specified a value in the command instead of a file
+      data.append(cmd->cmd[3]);
+    } else if (cmd->get_data_len() > 0) {
+      // they specified '-i <file>'
+      data = cmd->get_data();
+    }
+    if (data.length() > (size_t) g_conf->mon_config_key_max_entry_size) {
+      ret = -EFBIG; // File too large
+      ss << "error: entry size limited to "
+         << g_conf->mon_config_key_max_entry_size << " bytes. "
+         << "Use 'mon config key max entry size' to manually adjust";
+      goto out;
+    }
+    // we'll reply to the message once the proposal has been handled
+    store_put(cmd->cmd[2], data,
+        new Monitor::C_Command(mon, cmd, 0, "value stored", 0));
+    // return for now; we'll put the message once it's done.
+    return true;
+  } else if (cmd->cmd[1] == "delete") {
+    if (!mon->is_leader()) {
+      mon->forward_request_leader(cmd);
+      return true;
+    }
+
+    if (cmd->cmd.size() != 3) {
+      ret = -EINVAL;
+      ss << "usage: config-key delete <key>";
+      goto out;
+    }
+    if (!store_exists(cmd->cmd[2])) {
+      ret = 0;
+      ss << "no such key '" << cmd->cmd[2] << "'";
+      goto out;
+    }
+    store_delete(cmd->cmd[2],
+        new Monitor::C_Command(mon, cmd, 0, "key deleted", 0));
+    // return for now; we'll put the message once it's done
+    return true;
+  } else if (cmd->cmd[1] == "exists") {
+    if (cmd->cmd.size() != 3) {
+      ret = -EINVAL;
+      ss << "usage: config-key exists <key>";
+      goto out;
+    }
+    bool exists = store_exists(cmd->cmd[2]);
+    ss << "key '" << cmd->cmd[2] << "'";
+    if (exists) {
+      ss << " exists";
+      ret = 0;
+    } else {
+      ss << " doesn't exist";
+      ret = -ENOENT;
+    }
+  } else if (cmd->cmd[1] == "list") {
+    if (cmd->cmd.size() > 2) {
+      ret = -EINVAL;
+      ss << "usage: config-key list";
+      goto out;
+    }
+    stringstream tmp_ss;
+    store_list(tmp_ss);
+    rdata.append(tmp_ss);
+    ret = 0;
+  }
+
+out:
+  if (!cmd->get_source().is_mon()) {
+    string rs = ss.str();
+    mon->reply_command(cmd, ret, rs, rdata, 0);
+  } else {
+    cmd->put();
+  }
+
+  return (ret == 0);
+}
+
diff --git a/src/mon/ConfigKeyService.h b/src/mon/ConfigKeyService.h
new file mode 100644 (file)
index 0000000..aee85bc
--- /dev/null
@@ -0,0 +1,84 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank, Inc
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation.  See file COPYING.
+ *
+ */
+#ifndef CEPH_MON_CONFIG_KEY_SERVICE_H
+#define CEPH_MON_CONFIG_KEY_SERVICE_H
+
+#include <boost/intrusive_ptr.hpp>
+#include "include/assert.h"
+
+#include "mon/Monitor.h"
+#include "mon/QuorumService.h"
+
+#include "messages/MMonHealth.h"
+
+#include "common/config.h"
+#include "common/Formatter.h"
+
+class Paxos;
+
+class ConfigKeyService : public QuorumService
+{
+  Paxos *paxos;
+
+  int store_get(string key, bufferlist &bl);
+  void store_put(string key, bufferlist &bl, Context *cb = NULL);
+  void store_delete(string key, Context *cb = NULL);
+  void store_list(stringstream &ss);
+  bool store_exists(string key);
+
+  static const string STORE_PREFIX;
+
+protected:
+  virtual void service_shutdown() { }
+
+public:
+  ConfigKeyService(Monitor *m, Paxos *p) :
+    QuorumService(m),
+    paxos(p)
+  { }
+  virtual ~ConfigKeyService() { }
+  ConfigKeyService *get() {
+    return static_cast<ConfigKeyService *>(RefCountedObject::get());
+  }
+
+
+  /**
+   * @defgroup ConfigKeyService_Inherited_h Inherited abstract methods
+   * @{
+   */
+  virtual void init() { }
+  virtual void get_health(Formatter *f,
+                          list<pair<health_status_t,string> > *detail) { }
+  virtual bool service_dispatch(Message *m);
+
+  virtual void start_epoch() { }
+  virtual void finish_epoch() { }
+  virtual void cleanup() { }
+  virtual void service_tick() { }
+
+  virtual int get_type() {
+    return QuorumService::SERVICE_CONFIG_KEY;
+  }
+
+  virtual string get_name() const {
+    return "config_key";
+  }
+
+  /**
+   * @} // ConfigKeyService_Inherited_h
+   */
+};
+typedef boost::intrusive_ptr<ConfigKeyService> ConfigKeyServiceRef;
+
+#endif // CEPH_MON_CONFIG_KEY_SERVICE_H
index 93f55607137c80ea0e553436f3ca85ff13f0375c..e2015456dcdcdda2aeb15e03d4fe6e08dfd0e751 100644 (file)
@@ -71,6 +71,7 @@
 #include "AuthMonitor.h"
 #include "mon/QuorumService.h"
 #include "mon/HealthMonitor.h"
+#include "mon/ConfigKeyService.h"
 
 #include "auth/AuthMethodList.h"
 #include "auth/KeyRing.h"
@@ -87,6 +88,7 @@ static ostream& _prefix(std::ostream *_dout, const Monitor *mon) {
 }
 
 const string Monitor::MONITOR_NAME = "monitor";
+const string Monitor::MONITOR_STORE_PREFIX = "monitor_store";
 
 long parse_pos_long(const char *s, ostream *pss)
 {
@@ -167,6 +169,7 @@ Monitor::Monitor(CephContext* cct_, string nm, MonitorDBStore *s,
   paxos_service[PAXOS_AUTH] = new AuthMonitor(this, paxos, "auth");
 
   health_monitor = QuorumServiceRef(new HealthMonitor(this));
+  config_key_service = ConfigKeyServiceRef(new ConfigKeyService(this, paxos));
 
   mon_caps = new MonCaps();
   mon_caps->set_allow_all(true);
@@ -2401,6 +2404,16 @@ void Monitor::handle_command(MMonCommand *m)
     return;
   }
 
+  if (m->cmd[0] == "config-key") {
+    if (!access_all) {
+      r = -EACCES;
+      rs = "access denied";
+      goto out;
+    }
+    config_key_service->dispatch(m);
+    return;
+  }
+
   if (m->cmd[0] == "fsid") {
     stringstream ss;
     ss << monmap->fsid;
index f0cbe39d0128d83307ca7dd22bd086c7db5ceffe..a0507142a709d549b9b7c1685c8f31d3ff9f2195 100644 (file)
@@ -146,6 +146,7 @@ private:
 public:
   MonitorDBStore *store;
   static const string MONITOR_NAME;
+  static const string MONITOR_STORE_PREFIX;
 
   // -- monitor state --
 private:
@@ -1244,6 +1245,7 @@ public:
   friend class LogMonitor;
 
   boost::intrusive_ptr<QuorumService> health_monitor;
+  boost::intrusive_ptr<QuorumService> config_key_service;
 
   // -- sessions --
   MonSessionMap session_map;
index 34e94f11a0eddf30c8f204802f40f0371fdaff9f..dfa4f4165e9521d6aa8b74a20f55a6570bd3d8c6 100644 (file)
@@ -26,7 +26,6 @@
 #include "common/config.h"
 
 #include "mon/Monitor.h"
-#include "messages/MMonQuorumService.h"
 
 class QuorumService : public RefCountedObject
 {
@@ -47,7 +46,8 @@ class QuorumService : public RefCountedObject
 public:
   enum {
     SERVICE_HEALTH                   = 0x01,
-    SERVICE_TIMECHECK                = 0x02
+    SERVICE_TIMECHECK                = 0x02,
+    SERVICE_CONFIG_KEY               = 0x03,
   };
 
 protected:
@@ -116,7 +116,7 @@ public:
     return epoch;
   }
 
-  bool dispatch(MMonQuorumService *m) {
+  bool dispatch(Message *m) {
     return service_dispatch(m);
   }