]> git.proxmox.com Git - ceph.git/blame - ceph/src/osd/OSDCap.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / osd / OSDCap.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) 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>
11fdf7f2 19#include <boost/algorithm/string/predicate.hpp>
7c673cae
FG
20
21#include "OSDCap.h"
22#include "common/config.h"
23#include "common/debug.h"
11fdf7f2 24#include "include/ipaddr.h"
7c673cae
FG
25
26using std::ostream;
f67539c2 27using std::string;
7c673cae
FG
28using std::vector;
29
31f18b77 30ostream& operator<<(ostream& out, const osd_rwxa_t& p)
c07f9fc5 31{
7c673cae
FG
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
50ostream& operator<<(ostream& out, const OSDCapSpec& s)
51{
52 if (s.allow)
53 return out << s.allow;
11fdf7f2
TL
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 }
7c673cae
FG
60 return out;
61}
62
c07f9fc5
FG
63ostream& 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
11fdf7f2
TL
80ostream& operator<<(ostream &out, const OSDCapPoolTag &pt)
81{
82 out << "app " << pt.application << " key " << pt.key << " val " << pt.value
83 << " ";
84 return out;
85}
86
7c673cae
FG
87ostream& operator<<(ostream& out, const OSDCapMatch& m)
88{
11fdf7f2 89 if (!m.pool_namespace.pool_name.empty() || m.pool_namespace.nspace) {
c07f9fc5 90 out << m.pool_namespace;
7c673cae 91 }
c07f9fc5 92
11fdf7f2
TL
93 if (!m.pool_tag.application.empty()) {
94 out << m.pool_tag;
95 }
96
7c673cae
FG
97 if (m.object_prefix.length()) {
98 out << "object_prefix " << m.object_prefix << " ";
99 }
7c673cae
FG
100 return out;
101}
102
c07f9fc5 103ostream& operator<<(ostream& out, const OSDCapProfile& m)
7c673cae 104{
c07f9fc5
FG
105 out << "profile " << m.name;
106 out << m.pool_namespace;
107 return out;
108}
109
110bool OSDCapPoolNamespace::is_match(const std::string& pn,
111 const std::string& ns) const
112{
113 if (!pool_name.empty()) {
114 if (pool_name != pn) {
7c673cae 115 return false;
c07f9fc5 116 }
7c673cae 117 }
c07f9fc5 118 if (nspace) {
eafe8130 119 if (!nspace->empty() && nspace->back() == '*' &&
11fdf7f2
TL
120 boost::starts_with(ns, nspace->substr(0, nspace->length() - 1))) {
121 return true;
122 }
123
c07f9fc5 124 if (*nspace != ns) {
7c673cae 125 return false;
c07f9fc5 126 }
7c673cae 127 }
c07f9fc5
FG
128 return true;
129}
130
131bool 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
11fdf7f2
TL
140bool 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
170bool OSDCapPoolTag::is_match_all() const {
171 return application.empty();
172}
173
c07f9fc5 174bool OSDCapMatch::is_match(const string& pn, const string& ns,
11fdf7f2
TL
175 const OSDCapPoolTag::app_map_t& app_map,
176 const string& object) const
c07f9fc5 177{
11fdf7f2
TL
178 if (!pool_namespace.is_match(pn, ns)) {
179 return false;
180 } else if (!pool_tag.is_match(app_map)) {
c07f9fc5 181 return false;
7c673cae 182 }
c07f9fc5 183
7c673cae
FG
184 if (object_prefix.length()) {
185 if (object.find(object_prefix) != 0)
186 return false;
187 }
188 return true;
189}
190
191bool OSDCapMatch::is_match_all() const
192{
11fdf7f2 193if (!pool_namespace.is_match_all()) {
7c673cae 194 return false;
11fdf7f2 195 } else if (!pool_tag.is_match_all()) {
7c673cae 196 return false;
c07f9fc5
FG
197 }
198
199 if (object_prefix.length()) {
7c673cae 200 return false;
c07f9fc5 201 }
7c673cae
FG
202 return true;
203}
204
205ostream& operator<<(ostream& out, const OSDCapGrant& g)
206{
c07f9fc5
FG
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 }
11fdf7f2
TL
221 if (g.network.size()) {
222 out << " network " << g.network;
223 }
c07f9fc5
FG
224 out << ")";
225 return out;
7c673cae
FG
226}
227
11fdf7f2
TL
228void OSDCapGrant::set_network(const string& n)
229{
230 network = n;
231 network_valid = ::parse_network(n.c_str(), &network_parsed, &network_prefix);
232}
233
c07f9fc5 234bool OSDCapGrant::allow_all() const
7c673cae 235{
c07f9fc5
FG
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 }
7c673cae 242
c07f9fc5 243 return (match.is_match_all() && spec.allow_all());
7c673cae
FG
244}
245
11fdf7f2
TL
246bool 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,
9f95a23c 253 const std::vector<OpInfo::ClassInfo>& classes,
11fdf7f2
TL
254 const entity_addr_t& addr,
255 std::vector<bool>* class_allowed) const
7c673cae 256{
7c673cae 257 osd_rwxa_t allow = 0;
11fdf7f2
TL
258
259 if (network.size() &&
260 (!network_valid ||
261 !network_contains(network_parsed,
262 network_prefix,
263 addr))) {
264 return false;
265 }
266
c07f9fc5
FG
267 if (profile.is_valid()) {
268 return std::any_of(profile_grants.cbegin(), profile_grants.cend(),
269 [&](const OSDCapGrant& grant) {
11fdf7f2
TL
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 });
c07f9fc5 276 } else {
11fdf7f2 277 if (match.is_match(pool_name, ns, application_metadata, object)) {
c07f9fc5 278 allow = allow | spec.allow;
7c673cae 279 if ((op_may_read && !(allow & OSD_CAP_R)) ||
c07f9fc5
FG
280 (op_may_write && !(allow & OSD_CAP_W))) {
281 return false;
282 }
283 if (!classes.empty()) {
7c673cae 284 // check 'allow *'
c07f9fc5 285 if (spec.allow_all()) {
7c673cae 286 return true;
c07f9fc5
FG
287 }
288
7c673cae 289 // compare this grant to each class in the operation
c07f9fc5 290 for (size_t i = 0; i < classes.size(); ++i) {
11fdf7f2
TL
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)) {
c07f9fc5 296 (*class_allowed)[i] = true;
7c673cae
FG
297 continue;
298 }
f67539c2
TL
299 // check 'allow x | class-{rw}': must be on allow list
300 if (!classes[i].allowed) {
7c673cae 301 continue;
c07f9fc5 302 }
7c673cae
FG
303 if ((classes[i].read && !(allow & OSD_CAP_CLS_R)) ||
304 (classes[i].write && !(allow & OSD_CAP_CLS_W))) {
305 continue;
306 }
c07f9fc5
FG
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;
7c673cae 312 }
7c673cae
FG
313 }
314 return true;
315 }
316 }
317 return false;
318}
319
c07f9fc5
FG
320void 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
f6b5b4d7
TL
336 profile_grants.emplace_back(OSDCapMatch(string(), "rbd_info"),
337 OSDCapSpec(osd_rwxa_t(OSD_CAP_R)));
11fdf7f2 338 profile_grants.emplace_back(OSDCapMatch(string(), "rbd_children"),
c07f9fc5 339 OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
11fdf7f2 340 profile_grants.emplace_back(OSDCapMatch(string(), "rbd_mirroring"),
c07f9fc5 341 OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R)));
494da23a
TL
342 profile_grants.emplace_back(OSDCapMatch(profile.pool_namespace.pool_name),
343 OSDCapSpec("rbd", "metadata_list"));
c07f9fc5
FG
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)));
11fdf7f2
TL
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"));
c07f9fc5
FG
360 }
361}
362
363bool 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
373void OSDCap::set_allow_all()
374{
375 grants.clear();
376 grants.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY)));
377}
378
379bool OSDCap::is_capable(const string& pool_name, const string& ns,
11fdf7f2
TL
380 const OSDCapPoolTag::app_map_t& application_metadata,
381 const string& object,
c07f9fc5 382 bool op_may_read, bool op_may_write,
9f95a23c 383 const std::vector<OpInfo::ClassInfo>& classes,
11fdf7f2 384 const entity_addr_t& addr) const
c07f9fc5
FG
385{
386 std::vector<bool> class_allowed(classes.size(), false);
387 for (auto &grant : grants) {
11fdf7f2
TL
388 if (grant.is_capable(pool_name, ns, application_metadata,
389 object, op_may_read, op_may_write, classes, addr,
390 &class_allowed)) {
c07f9fc5
FG
391 return true;
392 }
393 }
394 return false;
395}
396
7c673cae
FG
397
398// grammar
399namespace qi = boost::spirit::qi;
400namespace ascii = boost::spirit::ascii;
401namespace phoenix = boost::phoenix;
402
403template <typename Iterator>
404struct 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_ - '\'') >> '\''];
11fdf7f2 425 unquoted_word %= +char_("a-zA-Z0-9_./-");
7c673cae
FG
426 str %= quoted_string | unquoted_word;
427 estr %= equoted_string | unquoted_word;
11fdf7f2 428 network_str %= +char_("/.:a-fA-F0-9][");
7c673cae
FG
429
430 spaces = +ascii::space;
431
11fdf7f2
TL
432 wildcard = (lit('*') | lit("all")) [_val = "*"];
433
7c673cae 434 pool_name %= -(spaces >> lit("pool") >> (lit('=') | spaces) >> str);
11fdf7f2
TL
435 nspace %= (spaces >> lit("namespace")
436 >> (lit('=') | spaces)
437 >> estr >> -char_('*'));
c07f9fc5 438
11fdf7f2 439 // match := [pool[=]<poolname> [namespace[=]<namespace>]] [object_prefix <prefix>]
7c673cae 440 object_prefix %= -(spaces >> lit("object_prefix") >> spaces >> str);
11fdf7f2
TL
441 pooltag %= (spaces >> lit("tag")
442 >> spaces >> str // application
443 >> spaces >> (wildcard | str) // key
444 >> -spaces >> lit('=') >> -spaces >> (wildcard | str)); // value
7c673cae 445
c07f9fc5 446 match = (
11fdf7f2
TL
447 pooltag [_val = phoenix::construct<OSDCapMatch>(_1)] |
448 (nspace >> pooltag) [_val = phoenix::construct<OSDCapMatch>(_1, _2)] |
c07f9fc5 449 (pool_name >> nspace >> object_prefix) [_val = phoenix::construct<OSDCapMatch>(_1, _2, _3)] |
11fdf7f2
TL
450 (pool_name >> object_prefix) [_val = phoenix::construct<OSDCapMatch>(_1, _2)]
451 );
7c673cae
FG
452
453 // rwxa := * | [r][w][x] [class-read] [class-write]
454 rwxa =
11fdf7f2 455 (spaces >> wildcard[_val = OSD_CAP_ANY]) |
7c673cae
FG
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
11fdf7f2 465 // capspec := * | rwx | class <name> [<method name>]
c07f9fc5 466 class_name %= (spaces >> lit("class") >> spaces >> str);
11fdf7f2 467 method_name %= -(spaces >> str);
c07f9fc5 468 capspec = (
11fdf7f2
TL
469 (rwxa) [_val = phoenix::construct<OSDCapSpec>(_1)] |
470 (class_name >> method_name) [_val = phoenix::construct<OSDCapSpec>(_1, _2)]);
c07f9fc5
FG
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)]);
7c673cae
FG
477
478 // grant := allow match capspec
c07f9fc5 479 grant = (*ascii::blank >>
11fdf7f2
TL
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)]
c07f9fc5 488 ) >> *ascii::blank);
7c673cae
FG
489 // osdcap := grant [grant ...]
490 grants %= (grant % (lit(';') | lit(',')));
c07f9fc5 491 osdcap = grants [_val = phoenix::construct<OSDCap>(_1)];
7c673cae
FG
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;
11fdf7f2
TL
497 qi::rule<Iterator, string()> str, estr, network_str;
498 qi::rule<Iterator, string()> wildcard;
c07f9fc5 499 qi::rule<Iterator, string()> class_name;
11fdf7f2 500 qi::rule<Iterator, string()> method_name;
7c673cae
FG
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;
11fdf7f2 505 qi::rule<Iterator, OSDCapPoolTag()> pooltag;
7c673cae 506 qi::rule<Iterator, OSDCapMatch()> match;
c07f9fc5
FG
507 qi::rule<Iterator, string()> profile_name;
508 qi::rule<Iterator, OSDCapProfile()> profile;
7c673cae
FG
509 qi::rule<Iterator, OSDCapGrant()> grant;
510 qi::rule<Iterator, std::vector<OSDCapGrant>()> grants;
511 qi::rule<Iterator, OSDCap()> osdcap;
512};
513
514bool 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)
11fdf7f2
TL
528 *err << "osd capability parse failed, stopped at '" << std::string(iter, end)
529 << "' of '" << str << "'";
7c673cae
FG
530
531 return false;
532}