From a021ce624542be36cf3b3c0b8e64d752019e4bc7 Mon Sep 17 00:00:00 2001 From: Joao Eduardo Luis Date: Fri, 1 Mar 2013 22:34:16 +0000 Subject: [PATCH] mon: ConfigKeyService: stash config keys on the monitor 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 [] or - ceph config-key put [-i ] 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 or - ceph config-key get -o - ceph config-key delete - ceph config-key list [-o Fixes: #4313 Signed-off-by: Joao Eduardo Luis --- src/Makefile.am | 4 +- src/common/config_opts.h | 1 + src/mon/ConfigKeyService.cc | 220 ++++++++++++++++++++++++++++++++++++ src/mon/ConfigKeyService.h | 84 ++++++++++++++ src/mon/Monitor.cc | 13 +++ src/mon/Monitor.h | 2 + src/mon/QuorumService.h | 6 +- 7 files changed, 326 insertions(+), 4 deletions(-) create mode 100644 src/mon/ConfigKeyService.cc create mode 100644 src/mon/ConfigKeyService.h diff --git a/src/Makefile.am b/src/Makefile.am index e05f05c0939ed..92739ce3bfa1b 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -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\ diff --git a/src/common/config_opts.h b/src/common/config_opts.h index c15a52f0e3938..e8278bf46325d 100644 --- a/src/common/config_opts.h +++ b/src/common/config_opts.h @@ -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 index 0000000000000..3319d9e80a83b --- /dev/null +++ b/src/mon/ConfigKeyService.cc @@ -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 +#include +#include + +#include +#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(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 []"; + goto out; + } + + if (cmd->cmd[1] == "get") { + if (cmd->cmd.size() != 3) { + ret = -EINVAL; + ss << "usage: config-key get -o "; + 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 [-i |]"; + 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 ' + 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 "; + 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 "; + 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 index 0000000000000..aee85bc085997 --- /dev/null +++ b/src/mon/ConfigKeyService.h @@ -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 +#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(RefCountedObject::get()); + } + + + /** + * @defgroup ConfigKeyService_Inherited_h Inherited abstract methods + * @{ + */ + virtual void init() { } + virtual void get_health(Formatter *f, + list > *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 ConfigKeyServiceRef; + +#endif // CEPH_MON_CONFIG_KEY_SERVICE_H diff --git a/src/mon/Monitor.cc b/src/mon/Monitor.cc index 93f55607137c8..e2015456dcdcd 100644 --- a/src/mon/Monitor.cc +++ b/src/mon/Monitor.cc @@ -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; diff --git a/src/mon/Monitor.h b/src/mon/Monitor.h index f0cbe39d0128d..a0507142a709d 100644 --- a/src/mon/Monitor.h +++ b/src/mon/Monitor.h @@ -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 health_monitor; + boost::intrusive_ptr config_key_service; // -- sessions -- MonSessionMap session_map; diff --git a/src/mon/QuorumService.h b/src/mon/QuorumService.h index 34e94f11a0edd..dfa4f4165e952 100644 --- a/src/mon/QuorumService.h +++ b/src/mon/QuorumService.h @@ -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); } -- 2.39.5