1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2013 Inktank, Inc
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.
19 #include "mon/Monitor.h"
20 #include "mon/ConfigKeyService.h"
21 #include "mon/MonitorDBStore.h"
22 #include "mon/OSDMonitor.h"
23 #include "common/errno.h"
24 #include "include/stringify.h"
26 #define dout_subsys ceph_subsys_mon
28 #define dout_prefix _prefix(_dout, mon, this)
29 static 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() << ") ";
36 const string
ConfigKeyService::STORE_PREFIX
= "mon_config_key";
38 int ConfigKeyService::store_get(const string
&key
, bufferlist
&bl
)
40 return mon
->store
->get(STORE_PREFIX
, key
, bl
);
43 void ConfigKeyService::get_store_prefixes(set
<string
>& s
)
45 s
.insert(STORE_PREFIX
);
48 void ConfigKeyService::store_put(const string
&key
, bufferlist
&bl
, Context
*cb
)
50 MonitorDBStore::TransactionRef t
= paxos
->get_pending_transaction();
51 t
->put(STORE_PREFIX
, key
, bl
);
53 paxos
->queue_pending_finisher(cb
);
54 paxos
->trigger_propose();
57 void ConfigKeyService::store_delete(const string
&key
, Context
*cb
)
59 MonitorDBStore::TransactionRef t
= paxos
->get_pending_transaction();
62 paxos
->queue_pending_finisher(cb
);
63 paxos
->trigger_propose();
66 void ConfigKeyService::store_delete(
67 MonitorDBStore::TransactionRef t
,
70 t
->erase(STORE_PREFIX
, key
);
73 bool ConfigKeyService::store_exists(const string
&key
)
75 return mon
->store
->exists(STORE_PREFIX
, key
);
78 void ConfigKeyService::store_list(stringstream
&ss
)
80 KeyValueDB::Iterator iter
=
81 mon
->store
->get_iterator(STORE_PREFIX
);
83 JSONFormatter
f(true);
84 f
.open_array_section("keys");
86 while (iter
->valid()) {
87 string
key(iter
->key());
88 f
.dump_string("key", key
);
95 bool ConfigKeyService::store_has_prefix(const string
&prefix
)
97 KeyValueDB::Iterator iter
=
98 mon
->store
->get_iterator(STORE_PREFIX
);
100 while (iter
->valid()) {
101 string
key(iter
->key());
102 size_t p
= key
.find(prefix
);
103 if (p
!= string::npos
&& p
== 0) {
111 static bool is_binary_string(const string
& s
)
114 // \n and \t are escaped in JSON; other control characters are not.
115 if ((c
< 0x20 && c
!= '\n' && c
!= '\t') || c
>= 0x7f) {
122 void ConfigKeyService::store_dump(stringstream
&ss
)
124 KeyValueDB::Iterator iter
=
125 mon
->store
->get_iterator(STORE_PREFIX
);
127 JSONFormatter
f(true);
128 f
.open_object_section("config-key store");
130 while (iter
->valid()) {
131 string s
= iter
->value().to_str();
132 if (is_binary_string(s
)) {
134 ss
<< "<<< binary blob of length " << s
.size() << " >>>";
135 f
.dump_string(iter
->key().c_str(), ss
.str());
137 f
.dump_string(iter
->key().c_str(), s
);
145 void ConfigKeyService::store_delete_prefix(
146 MonitorDBStore::TransactionRef t
,
147 const string
&prefix
)
149 KeyValueDB::Iterator iter
=
150 mon
->store
->get_iterator(STORE_PREFIX
);
152 while (iter
->valid()) {
153 string
key(iter
->key());
155 size_t p
= key
.find(prefix
);
156 if (p
!= string::npos
&& p
== 0) {
157 store_delete(t
, key
);
163 bool ConfigKeyService::service_dispatch(MonOpRequestRef op
)
165 Message
*m
= op
->get_req();
167 dout(10) << __func__
<< " " << *m
<< dendl
;
170 dout(1) << __func__
<< " not in quorum -- waiting" << dendl
;
171 paxos
->wait_for_readable(op
, new Monitor::C_RetryMessage(mon
, op
));
175 assert(m
->get_type() == MSG_MON_COMMAND
);
177 MMonCommand
*cmd
= static_cast<MMonCommand
*>(m
);
179 assert(!cmd
->cmd
.empty());
186 map
<string
, cmd_vartype
> cmdmap
;
188 if (!cmdmap_from_json(cmd
->cmd
, &cmdmap
, ss
)) {
192 cmd_getval(g_ceph_context
, cmdmap
, "prefix", prefix
);
194 cmd_getval(g_ceph_context
, cmdmap
, "key", key
);
196 if (prefix
== "config-key get") {
197 ret
= store_get(key
, rdata
);
199 assert(!rdata
.length());
200 ss
<< "error obtaining '" << key
<< "': " << cpp_strerror(ret
);
203 ss
<< "obtained '" << key
<< "'";
205 } else if (prefix
== "config-key put" ||
206 prefix
== "config-key set") {
207 if (!mon
->is_leader()) {
208 mon
->forward_request_leader(op
);
209 // we forward the message; so return now.
215 if (cmd_getval(g_ceph_context
, cmdmap
, "val", val
)) {
216 // they specified a value in the command instead of a file
218 } else if (cmd
->get_data_len() > 0) {
219 // they specified '-i <file>'
220 data
= cmd
->get_data();
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";
229 // we'll reply to the message once the proposal has been handled
232 new Monitor::C_Command(mon
, op
, 0, ss
.str(), 0));
233 // return for now; we'll put the message once it's done.
236 } else if (prefix
== "config-key del" ||
237 prefix
== "config-key rm") {
238 if (!mon
->is_leader()) {
239 mon
->forward_request_leader(op
);
243 if (!store_exists(key
)) {
245 ss
<< "no such key '" << key
<< "'";
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
252 } else if (prefix
== "config-key exists") {
253 bool exists
= store_exists(key
);
254 ss
<< "key '" << key
<< "'";
259 ss
<< " doesn't exist";
263 } else if (prefix
== "config-key list" ||
264 prefix
== "config-key ls") {
267 rdata
.append(tmp_ss
);
270 } else if (prefix
== "config-key dump") {
273 rdata
.append(tmp_ss
);
279 if (!cmd
->get_source().is_mon()) {
280 string rs
= ss
.str();
281 mon
->reply_command(op
, ret
, rs
, rdata
, 0);
287 string
_get_dmcrypt_prefix(const uuid_d
& uuid
, const string k
)
289 return "dm-crypt/osd/" + stringify(uuid
) + "/" + k
;
292 int ConfigKeyService::validate_osd_destroy(
296 string dmcrypt_prefix
= _get_dmcrypt_prefix(uuid
, "");
297 string daemon_prefix
=
298 "daemon-private/osd." + stringify(id
) + "/";
300 if (!store_has_prefix(dmcrypt_prefix
) &&
301 !store_has_prefix(daemon_prefix
)) {
307 void ConfigKeyService::do_osd_destroy(int32_t id
, uuid_d
& uuid
)
309 string dmcrypt_prefix
= _get_dmcrypt_prefix(uuid
, "");
310 string daemon_prefix
=
311 "daemon-private/osd." + stringify(id
) + "/";
313 MonitorDBStore::TransactionRef t
= paxos
->get_pending_transaction();
314 for (auto p
: { dmcrypt_prefix
, daemon_prefix
}) {
315 store_delete_prefix(t
, p
);
318 paxos
->trigger_propose();
321 int ConfigKeyService::validate_osd_new(
323 const string
& dmcrypt_key
,
326 string dmcrypt_prefix
= _get_dmcrypt_prefix(uuid
, "luks");
328 value
.append(dmcrypt_key
);
330 if (store_exists(dmcrypt_prefix
)) {
331 bufferlist existing_value
;
332 int err
= store_get(dmcrypt_prefix
, existing_value
);
334 dout(10) << __func__
<< " unable to get dm-crypt key from store (r = "
335 << err
<< ")" << dendl
;
338 if (existing_value
.contents_equal(value
)) {
339 // both values match; this will be an idempotent op.
342 ss
<< "dm-crypt key already exists and does not match";
348 void ConfigKeyService::do_osd_new(
350 const string
& dmcrypt_key
)
352 assert(paxos
->is_plugged());
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);