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