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