initial code repo
[stor4nfv.git] / src / ceph / src / mon / ConfigKeyService.cc
diff --git a/src/ceph/src/mon/ConfigKeyService.cc b/src/ceph/src/mon/ConfigKeyService.cc
new file mode 100644 (file)
index 0000000..29ae9d9
--- /dev/null
@@ -0,0 +1,341 @@
+// -*- 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"
+
+#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(const string &key, bufferlist &bl)
+{
+  return mon->store->get(STORE_PREFIX, key, bl);
+}
+
+void ConfigKeyService::get_store_prefixes(set<string>& s)
+{
+  s.insert(STORE_PREFIX);
+}
+
+void ConfigKeyService::store_put(const string &key, bufferlist &bl, Context *cb)
+{
+  MonitorDBStore::TransactionRef t = paxos->get_pending_transaction();
+  t->put(STORE_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(STORE_PREFIX, key);
+}
+
+bool ConfigKeyService::store_exists(const 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::store_has_prefix(const string &prefix)
+{
+  KeyValueDB::Iterator iter =
+    mon->store->get_iterator(STORE_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;
+}
+
+void ConfigKeyService::store_dump(stringstream &ss)
+{
+  KeyValueDB::Iterator iter =
+    mon->store->get_iterator(STORE_PREFIX);
+
+  JSONFormatter f(true);
+  f.open_object_section("config-key store");
+
+  while (iter->valid()) {
+    f.dump_string(iter->key().c_str(), iter->value().to_str());
+    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(STORE_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();
+  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;
+  }
+
+  assert(m->get_type() == MSG_MON_COMMAND);
+
+  MMonCommand *cmd = static_cast<MMonCommand*>(m);
+
+  assert(!cmd->cmd.empty());
+
+  int ret = 0;
+  stringstream ss;
+  bufferlist rdata;
+
+  string prefix;
+  map<string, cmd_vartype> 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) {
+      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;
+    }
+    // we'll reply to the message once the proposal has been handled
+    ss << "set " << key;
+    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") {
+    stringstream tmp_ss;
+    store_dump(tmp_ss);
+    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)
+{
+  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);
+}