]> git.proxmox.com Git - ceph.git/blame - ceph/src/mon/ConfigKeyService.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / mon / ConfigKeyService.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2013 Inktank, Inc
7 *
8 * This is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License version 2.1, as published by the Free Software
11 * Foundation. See file COPYING.
12 *
13 */
14
15#include <sstream>
16#include <stdlib.h>
17#include <limits.h>
18
19#include "mon/Monitor.h"
20#include "mon/ConfigKeyService.h"
21#include "mon/MonitorDBStore.h"
31f18b77 22#include "mon/OSDMonitor.h"
7c673cae 23#include "common/errno.h"
31f18b77 24#include "include/stringify.h"
7c673cae 25
11fdf7f2 26#include "include/ceph_assert.h" // re-clobber ceph_assert()
7c673cae
FG
27#define dout_subsys ceph_subsys_mon
28#undef dout_prefix
29#define dout_prefix _prefix(_dout, mon, this)
30static ostream& _prefix(std::ostream *_dout, const Monitor *mon,
31 const ConfigKeyService *service) {
32 return *_dout << "mon." << mon->name << "@" << mon->rank
33 << "(" << mon->get_state_name() << ")." << service->get_name()
34 << "(" << service->get_epoch() << ") ";
35}
36
11fdf7f2 37const string CONFIG_PREFIX = "mon_config_key";
7c673cae
FG
38
39int ConfigKeyService::store_get(const string &key, bufferlist &bl)
40{
11fdf7f2 41 return mon->store->get(CONFIG_PREFIX, key, bl);
7c673cae
FG
42}
43
11fdf7f2 44void ConfigKeyService::get_store_prefixes(set<string>& s) const
7c673cae 45{
11fdf7f2 46 s.insert(CONFIG_PREFIX);
7c673cae
FG
47}
48
49void ConfigKeyService::store_put(const string &key, bufferlist &bl, Context *cb)
50{
51 MonitorDBStore::TransactionRef t = paxos->get_pending_transaction();
11fdf7f2 52 t->put(CONFIG_PREFIX, key, bl);
7c673cae
FG
53 if (cb)
54 paxos->queue_pending_finisher(cb);
55 paxos->trigger_propose();
56}
57
58void ConfigKeyService::store_delete(const string &key, Context *cb)
59{
60 MonitorDBStore::TransactionRef t = paxos->get_pending_transaction();
31f18b77 61 store_delete(t, key);
7c673cae
FG
62 if (cb)
63 paxos->queue_pending_finisher(cb);
64 paxos->trigger_propose();
65}
66
31f18b77
FG
67void ConfigKeyService::store_delete(
68 MonitorDBStore::TransactionRef t,
69 const string &key)
70{
11fdf7f2 71 t->erase(CONFIG_PREFIX, key);
31f18b77
FG
72}
73
7c673cae
FG
74bool ConfigKeyService::store_exists(const string &key)
75{
11fdf7f2 76 return mon->store->exists(CONFIG_PREFIX, key);
7c673cae
FG
77}
78
79void ConfigKeyService::store_list(stringstream &ss)
80{
81 KeyValueDB::Iterator iter =
11fdf7f2 82 mon->store->get_iterator(CONFIG_PREFIX);
7c673cae
FG
83
84 JSONFormatter f(true);
85 f.open_array_section("keys");
86
87 while (iter->valid()) {
88 string key(iter->key());
89 f.dump_string("key", key);
90 iter->next();
91 }
92 f.close_section();
93 f.flush(ss);
94}
95
31f18b77
FG
96bool ConfigKeyService::store_has_prefix(const string &prefix)
97{
98 KeyValueDB::Iterator iter =
11fdf7f2 99 mon->store->get_iterator(CONFIG_PREFIX);
31f18b77
FG
100
101 while (iter->valid()) {
102 string key(iter->key());
103 size_t p = key.find(prefix);
104 if (p != string::npos && p == 0) {
105 return true;
106 }
107 iter->next();
108 }
109 return false;
110}
111
28e407b8
AA
112static bool is_binary_string(const string& s)
113{
114 for (auto c : s) {
115 // \n and \t are escaped in JSON; other control characters are not.
116 if ((c < 0x20 && c != '\n' && c != '\t') || c >= 0x7f) {
117 return true;
118 }
119 }
120 return false;
121}
122
11fdf7f2 123void ConfigKeyService::store_dump(stringstream &ss, const string& prefix)
7c673cae
FG
124{
125 KeyValueDB::Iterator iter =
11fdf7f2
TL
126 mon->store->get_iterator(CONFIG_PREFIX);
127
128 dout(10) << __func__ << " prefix '" << prefix << "'" << dendl;
129 if (prefix.size()) {
130 iter->lower_bound(prefix);
131 }
7c673cae
FG
132
133 JSONFormatter f(true);
134 f.open_object_section("config-key store");
135
136 while (iter->valid()) {
11fdf7f2
TL
137 if (prefix.size() &&
138 iter->key().find(prefix) != 0) {
139 break;
140 }
28e407b8
AA
141 string s = iter->value().to_str();
142 if (is_binary_string(s)) {
143 ostringstream ss;
144 ss << "<<< binary blob of length " << s.size() << " >>>";
145 f.dump_string(iter->key().c_str(), ss.str());
146 } else {
147 f.dump_string(iter->key().c_str(), s);
148 }
7c673cae
FG
149 iter->next();
150 }
151 f.close_section();
152 f.flush(ss);
153}
154
31f18b77
FG
155void ConfigKeyService::store_delete_prefix(
156 MonitorDBStore::TransactionRef t,
157 const string &prefix)
158{
159 KeyValueDB::Iterator iter =
11fdf7f2 160 mon->store->get_iterator(CONFIG_PREFIX);
31f18b77
FG
161
162 while (iter->valid()) {
163 string key(iter->key());
164
165 size_t p = key.find(prefix);
166 if (p != string::npos && p == 0) {
167 store_delete(t, key);
168 }
169 iter->next();
170 }
171}
172
7c673cae
FG
173bool ConfigKeyService::service_dispatch(MonOpRequestRef op)
174{
175 Message *m = op->get_req();
11fdf7f2 176 ceph_assert(m != NULL);
7c673cae
FG
177 dout(10) << __func__ << " " << *m << dendl;
178
179 if (!in_quorum()) {
180 dout(1) << __func__ << " not in quorum -- waiting" << dendl;
181 paxos->wait_for_readable(op, new Monitor::C_RetryMessage(mon, op));
182 return false;
183 }
184
11fdf7f2 185 ceph_assert(m->get_type() == MSG_MON_COMMAND);
7c673cae
FG
186
187 MMonCommand *cmd = static_cast<MMonCommand*>(m);
188
11fdf7f2 189 ceph_assert(!cmd->cmd.empty());
7c673cae
FG
190
191 int ret = 0;
192 stringstream ss;
193 bufferlist rdata;
194
195 string prefix;
11fdf7f2 196 cmdmap_t cmdmap;
7c673cae
FG
197
198 if (!cmdmap_from_json(cmd->cmd, &cmdmap, ss)) {
199 return false;
200 }
201
11fdf7f2 202 cmd_getval(g_ceph_context, cmdmap, "prefix", prefix);
7c673cae 203 string key;
11fdf7f2 204 cmd_getval(g_ceph_context, cmdmap, "key", key);
7c673cae
FG
205
206 if (prefix == "config-key get") {
207 ret = store_get(key, rdata);
208 if (ret < 0) {
11fdf7f2 209 ceph_assert(!rdata.length());
7c673cae
FG
210 ss << "error obtaining '" << key << "': " << cpp_strerror(ret);
211 goto out;
212 }
213 ss << "obtained '" << key << "'";
214
c07f9fc5
FG
215 } else if (prefix == "config-key put" ||
216 prefix == "config-key set") {
7c673cae
FG
217 if (!mon->is_leader()) {
218 mon->forward_request_leader(op);
219 // we forward the message; so return now.
220 return true;
221 }
222
223 bufferlist data;
224 string val;
11fdf7f2 225 if (cmd_getval(g_ceph_context, cmdmap, "val", val)) {
7c673cae
FG
226 // they specified a value in the command instead of a file
227 data.append(val);
228 } else if (cmd->get_data_len() > 0) {
229 // they specified '-i <file>'
230 data = cmd->get_data();
231 }
11fdf7f2 232 if (data.length() > (size_t) g_conf()->mon_config_key_max_entry_size) {
7c673cae
FG
233 ret = -EFBIG; // File too large
234 ss << "error: entry size limited to "
11fdf7f2 235 << g_conf()->mon_config_key_max_entry_size << " bytes. "
7c673cae
FG
236 << "Use 'mon config key max entry size' to manually adjust";
237 goto out;
238 }
11fdf7f2
TL
239
240 std::string mgr_prefix = "mgr/";
241 if (key.size() >= mgr_prefix.size() &&
242 key.substr(0, mgr_prefix.size()) == mgr_prefix) {
243 // In <= mimic, we used config-key for mgr module configuration,
244 // and we bring values forward in an upgrade, but subsequent
245 // `set` operations will not be picked up. Warn user about this.
246 ss << "WARNING: it looks like you might be trying to set a ceph-mgr "
247 "module configuration key. Since Ceph 13.0.0 (Mimic), mgr module "
248 "configuration is done with `config set`, and new values "
249 "set using `config-key set` will be ignored.\n";
250 }
251
31f18b77 252 ss << "set " << key;
11fdf7f2
TL
253
254 // we'll reply to the message once the proposal has been handled
7c673cae 255 store_put(key, data,
31f18b77 256 new Monitor::C_Command(mon, op, 0, ss.str(), 0));
7c673cae
FG
257 // return for now; we'll put the message once it's done.
258 return true;
259
260 } else if (prefix == "config-key del" ||
261 prefix == "config-key rm") {
262 if (!mon->is_leader()) {
263 mon->forward_request_leader(op);
264 return true;
265 }
266
267 if (!store_exists(key)) {
268 ret = 0;
269 ss << "no such key '" << key << "'";
270 goto out;
271 }
272 store_delete(key, new Monitor::C_Command(mon, op, 0, "key deleted", 0));
273 // return for now; we'll put the message once it's done
274 return true;
275
276 } else if (prefix == "config-key exists") {
277 bool exists = store_exists(key);
278 ss << "key '" << key << "'";
279 if (exists) {
280 ss << " exists";
281 ret = 0;
282 } else {
283 ss << " doesn't exist";
284 ret = -ENOENT;
285 }
286
c07f9fc5
FG
287 } else if (prefix == "config-key list" ||
288 prefix == "config-key ls") {
7c673cae
FG
289 stringstream tmp_ss;
290 store_list(tmp_ss);
291 rdata.append(tmp_ss);
292 ret = 0;
293
294 } else if (prefix == "config-key dump") {
11fdf7f2
TL
295 string prefix;
296 cmd_getval(g_ceph_context, cmdmap, "key", prefix);
7c673cae 297 stringstream tmp_ss;
11fdf7f2 298 store_dump(tmp_ss, prefix);
7c673cae
FG
299 rdata.append(tmp_ss);
300 ret = 0;
31f18b77 301
7c673cae
FG
302 }
303
304out:
305 if (!cmd->get_source().is_mon()) {
306 string rs = ss.str();
307 mon->reply_command(op, ret, rs, rdata, 0);
308 }
309
310 return (ret == 0);
311}
312
31f18b77
FG
313string _get_dmcrypt_prefix(const uuid_d& uuid, const string k)
314{
315 return "dm-crypt/osd/" + stringify(uuid) + "/" + k;
316}
317
318int ConfigKeyService::validate_osd_destroy(
319 const int32_t id,
320 const uuid_d& uuid)
321{
322 string dmcrypt_prefix = _get_dmcrypt_prefix(uuid, "");
323 string daemon_prefix =
324 "daemon-private/osd." + stringify(id) + "/";
325
326 if (!store_has_prefix(dmcrypt_prefix) &&
327 !store_has_prefix(daemon_prefix)) {
328 return -ENOENT;
329 }
330 return 0;
331}
332
333void ConfigKeyService::do_osd_destroy(int32_t id, uuid_d& uuid)
334{
335 string dmcrypt_prefix = _get_dmcrypt_prefix(uuid, "");
336 string daemon_prefix =
337 "daemon-private/osd." + stringify(id) + "/";
338
339 MonitorDBStore::TransactionRef t = paxos->get_pending_transaction();
340 for (auto p : { dmcrypt_prefix, daemon_prefix }) {
341 store_delete_prefix(t, p);
342 }
343
344 paxos->trigger_propose();
345}
346
347int ConfigKeyService::validate_osd_new(
348 const uuid_d& uuid,
349 const string& dmcrypt_key,
350 stringstream& ss)
351{
352 string dmcrypt_prefix = _get_dmcrypt_prefix(uuid, "luks");
353 bufferlist value;
354 value.append(dmcrypt_key);
355
356 if (store_exists(dmcrypt_prefix)) {
357 bufferlist existing_value;
358 int err = store_get(dmcrypt_prefix, existing_value);
359 if (err < 0) {
360 dout(10) << __func__ << " unable to get dm-crypt key from store (r = "
361 << err << ")" << dendl;
362 return err;
363 }
364 if (existing_value.contents_equal(value)) {
365 // both values match; this will be an idempotent op.
366 return EEXIST;
367 }
368 ss << "dm-crypt key already exists and does not match";
369 return -EEXIST;
370 }
371 return 0;
372}
373
374void ConfigKeyService::do_osd_new(
375 const uuid_d& uuid,
376 const string& dmcrypt_key)
377{
11fdf7f2 378 ceph_assert(paxos->is_plugged());
31f18b77
FG
379
380 string dmcrypt_key_prefix = _get_dmcrypt_prefix(uuid, "luks");
381 bufferlist dmcrypt_key_value;
382 dmcrypt_key_value.append(dmcrypt_key);
383 // store_put() will call trigger_propose
384 store_put(dmcrypt_key_prefix, dmcrypt_key_value, nullptr);
385}