]> git.proxmox.com Git - ceph.git/blob - ceph/src/osd/OSDCap.cc
buildsys: switch source download to quincy
[ceph.git] / ceph / src / osd / OSDCap.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) 2009-2011 New Dream Network
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 <boost/config/warning_disable.hpp>
16 #include <boost/spirit/include/qi.hpp>
17 #include <boost/spirit/include/phoenix_operator.hpp>
18 #include <boost/spirit/include/phoenix.hpp>
19 #include <boost/algorithm/string/predicate.hpp>
20
21 #include "OSDCap.h"
22 #include "common/config.h"
23 #include "common/debug.h"
24 #include "include/ipaddr.h"
25
26 using std::ostream;
27 using std::string;
28 using std::vector;
29
30 ostream& operator<<(ostream& out, const osd_rwxa_t& p)
31 {
32 if (p == OSD_CAP_ANY)
33 return out << "*";
34
35 if (p & OSD_CAP_R)
36 out << "r";
37 if (p & OSD_CAP_W)
38 out << "w";
39 if ((p & OSD_CAP_X) == OSD_CAP_X) {
40 out << "x";
41 } else {
42 if (p & OSD_CAP_CLS_R)
43 out << " class-read";
44 if (p & OSD_CAP_CLS_W)
45 out << " class-write";
46 }
47 return out;
48 }
49
50 ostream& operator<<(ostream& out, const OSDCapSpec& s)
51 {
52 if (s.allow)
53 return out << s.allow;
54 if (s.class_name.length()) {
55 out << "class '" << s.class_name << "'";
56 if (!s.method_name.empty()) {
57 out << " '" << s.method_name << "'";
58 }
59 }
60 return out;
61 }
62
63 ostream& operator<<(ostream& out, const OSDCapPoolNamespace& pns)
64 {
65 if (!pns.pool_name.empty()) {
66 out << "pool " << pns.pool_name << " ";
67 }
68 if (pns.nspace) {
69 out << "namespace ";
70 if (pns.nspace->empty()) {
71 out << "\"\"";
72 } else {
73 out << *pns.nspace;
74 }
75 out << " ";
76 }
77 return out;
78 }
79
80 ostream& operator<<(ostream &out, const OSDCapPoolTag &pt)
81 {
82 out << "app " << pt.application << " key " << pt.key << " val " << pt.value
83 << " ";
84 return out;
85 }
86
87 ostream& operator<<(ostream& out, const OSDCapMatch& m)
88 {
89 if (!m.pool_namespace.pool_name.empty() || m.pool_namespace.nspace) {
90 out << m.pool_namespace;
91 }
92
93 if (!m.pool_tag.application.empty()) {
94 out << m.pool_tag;
95 }
96
97 if (m.object_prefix.length()) {
98 out << "object_prefix " << m.object_prefix << " ";
99 }
100 return out;
101 }
102
103 ostream& operator<<(ostream& out, const OSDCapProfile& m)
104 {
105 out << "profile " << m.name;
106 out << m.pool_namespace;
107 return out;
108 }
109
110 bool OSDCapPoolNamespace::is_match(const std::string& pn,
111 const std::string& ns) const
112 {
113 if (!pool_name.empty()) {
114 if (pool_name != pn) {
115 return false;
116 }
117 }
118 if (nspace) {
119 if (!nspace->empty() && nspace->back() == '*' &&
120 boost::starts_with(ns, nspace->substr(0, nspace->length() - 1))) {
121 return true;
122 }
123
124 if (*nspace != ns) {
125 return false;
126 }
127 }
128 return true;
129 }
130
131 bool OSDCapPoolNamespace::is_match_all() const
132 {
133 if (!pool_name.empty())
134 return false;
135 if (nspace)
136 return false;
137 return true;
138 }
139
140 bool OSDCapPoolTag::is_match(const app_map_t& app_map) const
141 {
142 if (application.empty()) {
143 return true;
144 }
145 auto kv_map = app_map.find(application);
146 if (kv_map == app_map.end()) {
147 return false;
148 }
149 if (!key.compare("*") && !value.compare("*")) {
150 return true;
151 }
152 if (!key.compare("*")) {
153 for (auto it : kv_map->second) {
154 if (it.second == value) {
155 return true;
156 }
157 }
158 return false;
159 }
160 auto kv_val = kv_map->second.find(key);
161 if (kv_val == kv_map->second.end()) {
162 return false;
163 }
164 if (!value.compare("*")) {
165 return true;
166 }
167 return kv_val->second == value;
168 }
169
170 bool OSDCapPoolTag::is_match_all() const {
171 return application.empty();
172 }
173
174 bool OSDCapMatch::is_match(const string& pn, const string& ns,
175 const OSDCapPoolTag::app_map_t& app_map,
176 const string& object) const
177 {
178 if (!pool_namespace.is_match(pn, ns)) {
179 return false;
180 } else if (!pool_tag.is_match(app_map)) {
181 return false;
182 }
183
184 if (object_prefix.length()) {
185 if (object.find(object_prefix) != 0)
186 return false;
187 }
188 return true;
189 }
190
191 bool OSDCapMatch::is_match_all() const
192 {
193 if (!pool_namespace.is_match_all()) {
194 return false;
195 } else if (!pool_tag.is_match_all()) {
196 return false;
197 }
198
199 if (object_prefix.length()) {
200 return false;
201 }
202 return true;
203 }
204
205 ostream& operator<<(ostream& out, const OSDCapGrant& g)
206 {
207 out << "grant(";
208 if (g.profile.is_valid()) {
209 out << g.profile << " [";
210 for (auto it = g.profile_grants.cbegin();
211 it != g.profile_grants.cend(); ++it) {
212 if (it != g.profile_grants.cbegin()) {
213 out << ",";
214 }
215 out << *it;
216 }
217 out << "]";
218 } else {
219 out << g.match << g.spec;
220 }
221 if (g.network.size()) {
222 out << " network " << g.network;
223 }
224 out << ")";
225 return out;
226 }
227
228 void OSDCapGrant::set_network(const string& n)
229 {
230 network = n;
231 network_valid = ::parse_network(n.c_str(), &network_parsed, &network_prefix);
232 }
233
234 bool OSDCapGrant::allow_all() const
235 {
236 if (profile.is_valid()) {
237 return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
238 [](const OSDCapGrant& grant) {
239 return grant.allow_all();
240 });
241 }
242
243 return (match.is_match_all() && spec.allow_all());
244 }
245
246 bool OSDCapGrant::is_capable(
247 const string& pool_name,
248 const string& ns,
249 const OSDCapPoolTag::app_map_t& application_metadata,
250 const string& object,
251 bool op_may_read,
252 bool op_may_write,
253 const std::vector<OpInfo::ClassInfo>& classes,
254 const entity_addr_t& addr,
255 std::vector<bool>* class_allowed) const
256 {
257 osd_rwxa_t allow = 0;
258
259 if (network.size() &&
260 (!network_valid ||
261 !network_contains(network_parsed,
262 network_prefix,
263 addr))) {
264 return false;
265 }
266
267 if (profile.is_valid()) {
268 return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
269 [&](const OSDCapGrant& grant) {
270 return grant.is_capable(pool_name, ns,
271 application_metadata,
272 object, op_may_read,
273 op_may_write, classes, addr,
274 class_allowed);
275 });
276 } else {
277 if (match.is_match(pool_name, ns, application_metadata, object)) {
278 allow = allow | spec.allow;
279 if ((op_may_read && !(allow & OSD_CAP_R)) ||
280 (op_may_write && !(allow & OSD_CAP_W))) {
281 return false;
282 }
283 if (!classes.empty()) {
284 // check 'allow *'
285 if (spec.allow_all()) {
286 return true;
287 }
288
289 // compare this grant to each class in the operation
290 for (size_t i = 0; i < classes.size(); ++i) {
291 // check 'allow class foo [method_name]'
292 if (!spec.class_name.empty() &&
293 classes[i].class_name == spec.class_name &&
294 (spec.method_name.empty() ||
295 classes[i].method_name == spec.method_name)) {
296 (*class_allowed)[i] = true;
297 continue;
298 }
299 // check 'allow x | class-{rw}': must be on allow list
300 if (!classes[i].allowed) {
301 continue;
302 }
303 if ((classes[i].read && !(allow & OSD_CAP_CLS_R)) ||
304 (classes[i].write && !(allow & OSD_CAP_CLS_W))) {
305 continue;
306 }
307 (*class_allowed)[i] = true;
308 }
309 if (!std::all_of(class_allowed->cbegin(), class_allowed->cend(),
310 [](bool v) { return v; })) {
311 return false;
312 }
313 }
314 return true;
315 }
316 }
317 return false;
318 }
319
320 void OSDCapGrant::expand_profile()
321 {
322 if (profile.name == "read-only") {
323 // grants READ-ONLY caps to the OSD
324 profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
325 OSDCapSpec(osd_rwxa_t(OSD_CAP_R)));
326 return;
327 }
328 if (profile.name == "read-write") {
329 // grants READ-WRITE caps to the OSD
330 profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
331 OSDCapSpec(osd_rwxa_t(OSD_CAP_R | OSD_CAP_W)));
332 }
333
334 if (profile.name == "rbd") {
335 // RBD read-write grant
336 profile_grants.emplace_back(OSDCapMatch(string(), "rbd_info"),
337 OSDCapSpec(osd_rwxa_t(OSD_CAP_R)));
338 profile_grants.emplace_back(OSDCapMatch(string(), "rbd_children"),
339 OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
340 profile_grants.emplace_back(OSDCapMatch(string(), "rbd_mirroring"),
341 OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
342 profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace.pool_name),
343 OSDCapSpec("rbd", "metadata_list"));
344 profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
345 OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
346 OSD_CAP_W |
347 OSD_CAP_X)));
348 }
349 if (profile.name == "rbd-read-only") {
350 // RBD read-only grant
351 profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace),
352 OSDCapSpec(osd_rwxa_t(OSD_CAP_R |
353 OSD_CAP_CLS_R)));
354 profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace,
355 "rbd_header."),
356 OSDCapSpec("rbd", "child_attach"));
357 profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace,
358 "rbd_header."),
359 OSDCapSpec("rbd", "child_detach"));
360 }
361 }
362
363 bool OSDCap::allow_all() const
364 {
365 for (auto &grant : grants) {
366 if (grant.allow_all()) {
367 return true;
368 }
369 }
370 return false;
371 }
372
373 void OSDCap::set_allow_all()
374 {
375 grants.clear();
376 grants.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY)));
377 }
378
379 bool OSDCap::is_capable(const string& pool_name, const string& ns,
380 const OSDCapPoolTag::app_map_t& application_metadata,
381 const string& object,
382 bool op_may_read, bool op_may_write,
383 const std::vector<OpInfo::ClassInfo>& classes,
384 const entity_addr_t& addr) const
385 {
386 std::vector<bool> class_allowed(classes.size(), false);
387 for (auto &grant : grants) {
388 if (grant.is_capable(pool_name, ns, application_metadata,
389 object, op_may_read, op_may_write, classes, addr,
390 &class_allowed)) {
391 return true;
392 }
393 }
394 return false;
395 }
396
397
398 // grammar
399 namespace qi = boost::spirit::qi;
400 namespace ascii = boost::spirit::ascii;
401 namespace phoenix = boost::phoenix;
402
403 template <typename Iterator>
404 struct OSDCapParser : qi::grammar<Iterator, OSDCap()>
405 {
406 OSDCapParser() : OSDCapParser::base_type(osdcap)
407 {
408 using qi::char_;
409 using qi::int_;
410 using qi::lexeme;
411 using qi::alnum;
412 using qi::_val;
413 using qi::_1;
414 using qi::_2;
415 using qi::_3;
416 using qi::eps;
417 using qi::lit;
418
419 quoted_string %=
420 lexeme['"' >> +(char_ - '"') >> '"'] |
421 lexeme['\'' >> +(char_ - '\'') >> '\''];
422 equoted_string %=
423 lexeme['"' >> *(char_ - '"') >> '"'] |
424 lexeme['\'' >> *(char_ - '\'') >> '\''];
425 unquoted_word %= +char_("a-zA-Z0-9_./-");
426 str %= quoted_string | unquoted_word;
427 estr %= equoted_string | unquoted_word;
428 network_str %= +char_("/.:a-fA-F0-9][");
429
430 spaces = +ascii::space;
431
432 wildcard = (lit('*') | lit("all")) [_val = "*"];
433
434 pool_name %= -(spaces >> lit("pool") >> (lit('=') | spaces) >> str);
435 nspace %= (spaces >> lit("namespace")
436 >> (lit('=') | spaces)
437 >> estr >> -char_('*'));
438
439 // match := [pool[=]<poolname> [namespace[=]<namespace>]] [object_prefix <prefix>]
440 object_prefix %= -(spaces >> lit("object_prefix") >> spaces >> str);
441 pooltag %= (spaces >> lit("tag")
442 >> spaces >> str // application
443 >> spaces >> (wildcard | str) // key
444 >> -spaces >> lit('=') >> -spaces >> (wildcard | str)); // value
445
446 match = (
447 pooltag [_val = phoenix::construct<OSDCapMatch>(_1)] |
448 (nspace >> pooltag) [_val = phoenix::construct<OSDCapMatch>(_1, _2)] |
449 (pool_name >> nspace >> object_prefix) [_val = phoenix::construct<OSDCapMatch>(_1, _2, _3)] |
450 (pool_name >> object_prefix) [_val = phoenix::construct<OSDCapMatch>(_1, _2)]
451 );
452
453 // rwxa := * | [r][w][x] [class-read] [class-write]
454 rwxa =
455 (spaces >> wildcard[_val = OSD_CAP_ANY]) |
456 ( eps[_val = 0] >>
457 (
458 spaces >>
459 ( lit('r')[_val |= OSD_CAP_R] ||
460 lit('w')[_val |= OSD_CAP_W] ||
461 lit('x')[_val |= OSD_CAP_X] )) ||
462 ( (spaces >> lit("class-read")[_val |= OSD_CAP_CLS_R]) ||
463 (spaces >> lit("class-write")[_val |= OSD_CAP_CLS_W]) ));
464
465 // capspec := * | rwx | class <name> [<method name>]
466 class_name %= (spaces >> lit("class") >> spaces >> str);
467 method_name %= -(spaces >> str);
468 capspec = (
469 (rwxa) [_val = phoenix::construct<OSDCapSpec>(_1)] |
470 (class_name >> method_name) [_val = phoenix::construct<OSDCapSpec>(_1, _2)]);
471
472 // profile := profile <name> [pool[=]<pool> [namespace[=]<namespace>]]
473 profile_name %= (lit("profile") >> (lit('=') | spaces) >> str);
474 profile = (
475 (profile_name >> pool_name >> nspace) [_val = phoenix::construct<OSDCapProfile>(_1, _2, _3)] |
476 (profile_name >> pool_name) [_val = phoenix::construct<OSDCapProfile>(_1, _2)]);
477
478 // grant := allow match capspec
479 grant = (*ascii::blank >>
480 ((lit("allow") >> capspec >> match >>
481 -(spaces >> lit("network") >> spaces >> network_str))
482 [_val = phoenix::construct<OSDCapGrant>(_2, _1, _3)] |
483 (lit("allow") >> match >> capspec >>
484 -(spaces >> lit("network") >> spaces >> network_str))
485 [_val = phoenix::construct<OSDCapGrant>(_1, _2, _3)] |
486 (profile >> -(spaces >> lit("network") >> spaces >> network_str))
487 [_val = phoenix::construct<OSDCapGrant>(_1, _2)]
488 ) >> *ascii::blank);
489 // osdcap := grant [grant ...]
490 grants %= (grant % (lit(';') | lit(',')));
491 osdcap = grants [_val = phoenix::construct<OSDCap>(_1)];
492 }
493 qi::rule<Iterator> spaces;
494 qi::rule<Iterator, unsigned()> rwxa;
495 qi::rule<Iterator, string()> quoted_string, equoted_string;
496 qi::rule<Iterator, string()> unquoted_word;
497 qi::rule<Iterator, string()> str, estr, network_str;
498 qi::rule<Iterator, string()> wildcard;
499 qi::rule<Iterator, string()> class_name;
500 qi::rule<Iterator, string()> method_name;
501 qi::rule<Iterator, OSDCapSpec()> capspec;
502 qi::rule<Iterator, string()> pool_name;
503 qi::rule<Iterator, string()> nspace;
504 qi::rule<Iterator, string()> object_prefix;
505 qi::rule<Iterator, OSDCapPoolTag()> pooltag;
506 qi::rule<Iterator, OSDCapMatch()> match;
507 qi::rule<Iterator, string()> profile_name;
508 qi::rule<Iterator, OSDCapProfile()> profile;
509 qi::rule<Iterator, OSDCapGrant()> grant;
510 qi::rule<Iterator, std::vector<OSDCapGrant>()> grants;
511 qi::rule<Iterator, OSDCap()> osdcap;
512 };
513
514 bool OSDCap::parse(const string& str, ostream *err)
515 {
516 OSDCapParser<string::const_iterator> g;
517 string::const_iterator iter = str.begin();
518 string::const_iterator end = str.end();
519
520 bool r = qi::phrase_parse(iter, end, g, ascii::space, *this);
521 if (r && iter == end)
522 return true;
523
524 // Make sure no grants are kept after parsing failed!
525 grants.clear();
526
527 if (err)
528 *err << "osd capability parse failed, stopped at '" << std::string(iter, end)
529 << "' of '" << str << "'";
530
531 return false;
532 }