summaryrefslogtreecommitdiffstats
path: root/src/mon/ConfigKeyService.cc
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/mon/ConfigKeyService.cc385
1 files changed, 385 insertions, 0 deletions
diff --git a/src/mon/ConfigKeyService.cc b/src/mon/ConfigKeyService.cc
new file mode 100644
index 00000000..38a22d16
--- /dev/null
+++ b/src/mon/ConfigKeyService.cc
@@ -0,0 +1,385 @@
+// -*- 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 "mon/Monitor.h"
+#include "mon/ConfigKeyService.h"
+#include "mon/MonitorDBStore.h"
+#include "mon/OSDMonitor.h"
+#include "common/errno.h"
+#include "include/stringify.h"
+
+#include "include/ceph_assert.h" // re-clobber ceph_assert()
+#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 CONFIG_PREFIX = "mon_config_key";
+
+int ConfigKeyService::store_get(const string &key, bufferlist &bl)
+{
+ return mon->store->get(CONFIG_PREFIX, key, bl);
+}
+
+void ConfigKeyService::get_store_prefixes(set<string>& s) const
+{
+ s.insert(CONFIG_PREFIX);
+}
+
+void ConfigKeyService::store_put(const string &key, bufferlist &bl, Context *cb)
+{
+ MonitorDBStore::TransactionRef t = paxos->get_pending_transaction();
+ t->put(CONFIG_PREFIX, key, bl);
+ if (cb)
+ paxos->queue_pending_finisher(cb);
+ paxos->trigger_propose();
+}
+
+void ConfigKeyService::store_delete(const string &key, Context *cb)
+{
+ MonitorDBStore::TransactionRef t = paxos->get_pending_transaction();
+ store_delete(t, key);
+ if (cb)
+ paxos->queue_pending_finisher(cb);
+ paxos->trigger_propose();
+}
+
+void ConfigKeyService::store_delete(
+ MonitorDBStore::TransactionRef t,
+ const string &key)
+{
+ t->erase(CONFIG_PREFIX, key);
+}
+
+bool ConfigKeyService::store_exists(const string &key)
+{
+ return mon->store->exists(CONFIG_PREFIX, key);
+}
+
+void ConfigKeyService::store_list(stringstream &ss)
+{
+ KeyValueDB::Iterator iter =
+ mon->store->get_iterator(CONFIG_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::store_has_prefix(const string &prefix)
+{
+ KeyValueDB::Iterator iter =
+ mon->store->get_iterator(CONFIG_PREFIX);
+
+ while (iter->valid()) {
+ string key(iter->key());
+ size_t p = key.find(prefix);
+ if (p != string::npos && p == 0) {
+ return true;
+ }
+ iter->next();
+ }
+ return false;
+}
+
+static bool is_binary_string(const string& s)
+{
+ for (auto c : s) {
+ // \n and \t are escaped in JSON; other control characters are not.
+ if ((c < 0x20 && c != '\n' && c != '\t') || c >= 0x7f) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void ConfigKeyService::store_dump(stringstream &ss, const string& prefix)
+{
+ KeyValueDB::Iterator iter =
+ mon->store->get_iterator(CONFIG_PREFIX);
+
+ dout(10) << __func__ << " prefix '" << prefix << "'" << dendl;
+ if (prefix.size()) {
+ iter->lower_bound(prefix);
+ }
+
+ JSONFormatter f(true);
+ f.open_object_section("config-key store");
+
+ while (iter->valid()) {
+ if (prefix.size() &&
+ iter->key().find(prefix) != 0) {
+ break;
+ }
+ string s = iter->value().to_str();
+ if (is_binary_string(s)) {
+ ostringstream ss;
+ ss << "<<< binary blob of length " << s.size() << " >>>";
+ f.dump_string(iter->key().c_str(), ss.str());
+ } else {
+ f.dump_string(iter->key().c_str(), s);
+ }
+ iter->next();
+ }
+ f.close_section();
+ f.flush(ss);
+}
+
+void ConfigKeyService::store_delete_prefix(
+ MonitorDBStore::TransactionRef t,
+ const string &prefix)
+{
+ KeyValueDB::Iterator iter =
+ mon->store->get_iterator(CONFIG_PREFIX);
+
+ while (iter->valid()) {
+ string key(iter->key());
+
+ size_t p = key.find(prefix);
+ if (p != string::npos && p == 0) {
+ store_delete(t, key);
+ }
+ iter->next();
+ }
+}
+
+bool ConfigKeyService::service_dispatch(MonOpRequestRef op)
+{
+ Message *m = op->get_req();
+ ceph_assert(m != NULL);
+ dout(10) << __func__ << " " << *m << dendl;
+
+ if (!in_quorum()) {
+ dout(1) << __func__ << " not in quorum -- waiting" << dendl;
+ paxos->wait_for_readable(op, new Monitor::C_RetryMessage(mon, op));
+ return false;
+ }
+
+ ceph_assert(m->get_type() == MSG_MON_COMMAND);
+
+ MMonCommand *cmd = static_cast<MMonCommand*>(m);
+
+ ceph_assert(!cmd->cmd.empty());
+
+ int ret = 0;
+ stringstream ss;
+ bufferlist rdata;
+
+ string prefix;
+ cmdmap_t cmdmap;
+
+ if (!cmdmap_from_json(cmd->cmd, &cmdmap, ss)) {
+ return false;
+ }
+
+ cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
+ string key;
+ cmd_getval(g_ceph_context, cmdmap, "key", key);
+
+ if (prefix == "config-key get") {
+ ret = store_get(key, rdata);
+ if (ret < 0) {
+ ceph_assert(!rdata.length());
+ ss << "error obtaining '" << key << "': " << cpp_strerror(ret);
+ goto out;
+ }
+ ss << "obtained '" << key << "'";
+
+ } else if (prefix == "config-key put" ||
+ prefix == "config-key set") {
+ if (!mon->is_leader()) {
+ mon->forward_request_leader(op);
+ // we forward the message; so return now.
+ return true;
+ }
+
+ bufferlist data;
+ string val;
+ if (cmd_getval(g_ceph_context, cmdmap, "val", val)) {
+ // they specified a value in the command instead of a file
+ data.append(val);
+ } 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;
+ }
+
+ std::string mgr_prefix = "mgr/";
+ if (key.size() >= mgr_prefix.size() &&
+ key.substr(0, mgr_prefix.size()) == mgr_prefix) {
+ // In <= mimic, we used config-key for mgr module configuration,
+ // and we bring values forward in an upgrade, but subsequent
+ // `set` operations will not be picked up. Warn user about this.
+ ss << "WARNING: it looks like you might be trying to set a ceph-mgr "
+ "module configuration key. Since Ceph 13.0.0 (Mimic), mgr module "
+ "configuration is done with `config set`, and new values "
+ "set using `config-key set` will be ignored.\n";
+ }
+
+ ss << "set " << key;
+
+ // we'll reply to the message once the proposal has been handled
+ store_put(key, data,
+ new Monitor::C_Command(mon, op, 0, ss.str(), 0));
+ // return for now; we'll put the message once it's done.
+ return true;
+
+ } else if (prefix == "config-key del" ||
+ prefix == "config-key rm") {
+ if (!mon->is_leader()) {
+ mon->forward_request_leader(op);
+ return true;
+ }
+
+ if (!store_exists(key)) {
+ ret = 0;
+ ss << "no such key '" << key << "'";
+ goto out;
+ }
+ store_delete(key, new Monitor::C_Command(mon, op, 0, "key deleted", 0));
+ // return for now; we'll put the message once it's done
+ return true;
+
+ } else if (prefix == "config-key exists") {
+ bool exists = store_exists(key);
+ ss << "key '" << key << "'";
+ if (exists) {
+ ss << " exists";
+ ret = 0;
+ } else {
+ ss << " doesn't exist";
+ ret = -ENOENT;
+ }
+
+ } else if (prefix == "config-key list" ||
+ prefix == "config-key ls") {
+ stringstream tmp_ss;
+ store_list(tmp_ss);
+ rdata.append(tmp_ss);
+ ret = 0;
+
+ } else if (prefix == "config-key dump") {
+ string prefix;
+ cmd_getval(g_ceph_context, cmdmap, "key", prefix);
+ stringstream tmp_ss;
+ store_dump(tmp_ss, prefix);
+ rdata.append(tmp_ss);
+ ret = 0;
+
+ }
+
+out:
+ if (!cmd->get_source().is_mon()) {
+ string rs = ss.str();
+ mon->reply_command(op, ret, rs, rdata, 0);
+ }
+
+ return (ret == 0);
+}
+
+string _get_dmcrypt_prefix(const uuid_d& uuid, const string k)
+{
+ return "dm-crypt/osd/" + stringify(uuid) + "/" + k;
+}
+
+int ConfigKeyService::validate_osd_destroy(
+ const int32_t id,
+ const uuid_d& uuid)
+{
+ string dmcrypt_prefix = _get_dmcrypt_prefix(uuid, "");
+ string daemon_prefix =
+ "daemon-private/osd." + stringify(id) + "/";
+
+ if (!store_has_prefix(dmcrypt_prefix) &&
+ !store_has_prefix(daemon_prefix)) {
+ return -ENOENT;
+ }
+ return 0;
+}
+
+void ConfigKeyService::do_osd_destroy(int32_t id, uuid_d& uuid)
+{
+ string dmcrypt_prefix = _get_dmcrypt_prefix(uuid, "");
+ string daemon_prefix =
+ "daemon-private/osd." + stringify(id) + "/";
+
+ MonitorDBStore::TransactionRef t = paxos->get_pending_transaction();
+ for (auto p : { dmcrypt_prefix, daemon_prefix }) {
+ store_delete_prefix(t, p);
+ }
+
+ paxos->trigger_propose();
+}
+
+int ConfigKeyService::validate_osd_new(
+ const uuid_d& uuid,
+ const string& dmcrypt_key,
+ stringstream& ss)
+{
+ string dmcrypt_prefix = _get_dmcrypt_prefix(uuid, "luks");
+ bufferlist value;
+ value.append(dmcrypt_key);
+
+ if (store_exists(dmcrypt_prefix)) {
+ bufferlist existing_value;
+ int err = store_get(dmcrypt_prefix, existing_value);
+ if (err < 0) {
+ dout(10) << __func__ << " unable to get dm-crypt key from store (r = "
+ << err << ")" << dendl;
+ return err;
+ }
+ if (existing_value.contents_equal(value)) {
+ // both values match; this will be an idempotent op.
+ return EEXIST;
+ }
+ ss << "dm-crypt key already exists and does not match";
+ return -EEXIST;
+ }
+ return 0;
+}
+
+void ConfigKeyService::do_osd_new(
+ const uuid_d& uuid,
+ const string& dmcrypt_key)
+{
+ ceph_assert(paxos->is_plugged());
+
+ string dmcrypt_key_prefix = _get_dmcrypt_prefix(uuid, "luks");
+ bufferlist dmcrypt_key_value;
+ dmcrypt_key_value.append(dmcrypt_key);
+ // store_put() will call trigger_propose
+ store_put(dmcrypt_key_prefix, dmcrypt_key_value, nullptr);
+}