]>
git.proxmox.com Git - ceph.git/blob - ceph/src/osd/OSDCap.cc
d5339a059367081504be031afd0b0a3b3ece567f
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) 2009-2011 New Dream Network
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.
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>
22 #include "common/config.h"
23 #include "common/debug.h"
24 #include "include/ipaddr.h"
29 ostream
& operator<<(ostream
& out
, const osd_rwxa_t
& p
)
38 if ((p
& OSD_CAP_X
) == OSD_CAP_X
) {
41 if (p
& OSD_CAP_CLS_R
)
43 if (p
& OSD_CAP_CLS_W
)
44 out
<< " class-write";
49 ostream
& operator<<(ostream
& out
, const OSDCapSpec
& s
)
52 return out
<< s
.allow
;
53 if (s
.class_name
.length()) {
54 out
<< "class '" << s
.class_name
<< "'";
55 if (!s
.method_name
.empty()) {
56 out
<< " '" << s
.method_name
<< "'";
62 ostream
& operator<<(ostream
& out
, const OSDCapPoolNamespace
& pns
)
64 if (!pns
.pool_name
.empty()) {
65 out
<< "pool " << pns
.pool_name
<< " ";
69 if (pns
.nspace
->empty()) {
79 ostream
& operator<<(ostream
&out
, const OSDCapPoolTag
&pt
)
81 out
<< "app " << pt
.application
<< " key " << pt
.key
<< " val " << pt
.value
86 ostream
& operator<<(ostream
& out
, const OSDCapMatch
& m
)
88 if (!m
.pool_namespace
.pool_name
.empty() || m
.pool_namespace
.nspace
) {
89 out
<< m
.pool_namespace
;
92 if (!m
.pool_tag
.application
.empty()) {
96 if (m
.object_prefix
.length()) {
97 out
<< "object_prefix " << m
.object_prefix
<< " ";
102 ostream
& operator<<(ostream
& out
, const OSDCapProfile
& m
)
104 out
<< "profile " << m
.name
;
105 out
<< m
.pool_namespace
;
109 bool OSDCapPoolNamespace::is_match(const std::string
& pn
,
110 const std::string
& ns
) const
112 if (!pool_name
.empty()) {
113 if (pool_name
!= pn
) {
118 if (!nspace
->empty() && nspace
->back() == '*' &&
119 boost::starts_with(ns
, nspace
->substr(0, nspace
->length() - 1))) {
130 bool OSDCapPoolNamespace::is_match_all() const
132 if (!pool_name
.empty())
139 bool OSDCapPoolTag::is_match(const app_map_t
& app_map
) const
141 if (application
.empty()) {
144 auto kv_map
= app_map
.find(application
);
145 if (kv_map
== app_map
.end()) {
148 if (!key
.compare("*") && !value
.compare("*")) {
151 if (!key
.compare("*")) {
152 for (auto it
: kv_map
->second
) {
153 if (it
.second
== value
) {
159 auto kv_val
= kv_map
->second
.find(key
);
160 if (kv_val
== kv_map
->second
.end()) {
163 if (!value
.compare("*")) {
166 return kv_val
->second
== value
;
169 bool OSDCapPoolTag::is_match_all() const {
170 return application
.empty();
173 bool OSDCapMatch::is_match(const string
& pn
, const string
& ns
,
174 const OSDCapPoolTag::app_map_t
& app_map
,
175 const string
& object
) const
177 if (!pool_namespace
.is_match(pn
, ns
)) {
179 } else if (!pool_tag
.is_match(app_map
)) {
183 if (object_prefix
.length()) {
184 if (object
.find(object_prefix
) != 0)
190 bool OSDCapMatch::is_match_all() const
192 if (!pool_namespace
.is_match_all()) {
194 } else if (!pool_tag
.is_match_all()) {
198 if (object_prefix
.length()) {
204 ostream
& operator<<(ostream
& out
, const OSDCapGrant
& g
)
207 if (g
.profile
.is_valid()) {
208 out
<< g
.profile
<< " [";
209 for (auto it
= g
.profile_grants
.cbegin();
210 it
!= g
.profile_grants
.cend(); ++it
) {
211 if (it
!= g
.profile_grants
.cbegin()) {
218 out
<< g
.match
<< g
.spec
;
220 if (g
.network
.size()) {
221 out
<< " network " << g
.network
;
227 void OSDCapGrant::set_network(const string
& n
)
230 network_valid
= ::parse_network(n
.c_str(), &network_parsed
, &network_prefix
);
233 bool OSDCapGrant::allow_all() const
235 if (profile
.is_valid()) {
236 return std::any_of(profile_grants
.cbegin(), profile_grants
.cend(),
237 [](const OSDCapGrant
& grant
) {
238 return grant
.allow_all();
242 return (match
.is_match_all() && spec
.allow_all());
245 bool OSDCapGrant::is_capable(
246 const string
& pool_name
,
248 const OSDCapPoolTag::app_map_t
& application_metadata
,
249 const string
& object
,
252 const std::vector
<OpInfo::ClassInfo
>& classes
,
253 const entity_addr_t
& addr
,
254 std::vector
<bool>* class_allowed
) const
256 osd_rwxa_t allow
= 0;
258 if (network
.size() &&
260 !network_contains(network_parsed
,
266 if (profile
.is_valid()) {
267 return std::any_of(profile_grants
.cbegin(), profile_grants
.cend(),
268 [&](const OSDCapGrant
& grant
) {
269 return grant
.is_capable(pool_name
, ns
,
270 application_metadata
,
272 op_may_write
, classes
, addr
,
276 if (match
.is_match(pool_name
, ns
, application_metadata
, object
)) {
277 allow
= allow
| spec
.allow
;
278 if ((op_may_read
&& !(allow
& OSD_CAP_R
)) ||
279 (op_may_write
&& !(allow
& OSD_CAP_W
))) {
282 if (!classes
.empty()) {
284 if (spec
.allow_all()) {
288 // compare this grant to each class in the operation
289 for (size_t i
= 0; i
< classes
.size(); ++i
) {
290 // check 'allow class foo [method_name]'
291 if (!spec
.class_name
.empty() &&
292 classes
[i
].class_name
== spec
.class_name
&&
293 (spec
.method_name
.empty() ||
294 classes
[i
].method_name
== spec
.method_name
)) {
295 (*class_allowed
)[i
] = true;
298 // check 'allow x | class-{rw}': must be on whitelist
299 if (!classes
[i
].whitelisted
) {
302 if ((classes
[i
].read
&& !(allow
& OSD_CAP_CLS_R
)) ||
303 (classes
[i
].write
&& !(allow
& OSD_CAP_CLS_W
))) {
306 (*class_allowed
)[i
] = true;
308 if (!std::all_of(class_allowed
->cbegin(), class_allowed
->cend(),
309 [](bool v
) { return v
; })) {
319 void OSDCapGrant::expand_profile()
321 if (profile
.name
== "read-only") {
322 // grants READ-ONLY caps to the OSD
323 profile_grants
.emplace_back(OSDCapMatch(profile
.pool_namespace
),
324 OSDCapSpec(osd_rwxa_t(OSD_CAP_R
)));
327 if (profile
.name
== "read-write") {
328 // grants READ-WRITE caps to the OSD
329 profile_grants
.emplace_back(OSDCapMatch(profile
.pool_namespace
),
330 OSDCapSpec(osd_rwxa_t(OSD_CAP_R
| OSD_CAP_W
)));
333 if (profile
.name
== "rbd") {
334 // RBD read-write grant
335 profile_grants
.emplace_back(OSDCapMatch(string(), "rbd_info"),
336 OSDCapSpec(osd_rwxa_t(OSD_CAP_R
)));
337 profile_grants
.emplace_back(OSDCapMatch(string(), "rbd_children"),
338 OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R
)));
339 profile_grants
.emplace_back(OSDCapMatch(string(), "rbd_mirroring"),
340 OSDCapSpec(osd_rwxa_t(OSD_CAP_CLS_R
)));
341 profile_grants
.emplace_back(OSDCapMatch(profile
.pool_namespace
.pool_name
),
342 OSDCapSpec("rbd", "metadata_list"));
343 profile_grants
.emplace_back(OSDCapMatch(profile
.pool_namespace
),
344 OSDCapSpec(osd_rwxa_t(OSD_CAP_R
|
348 if (profile
.name
== "rbd-read-only") {
349 // RBD read-only grant
350 profile_grants
.emplace_back(OSDCapMatch(profile
.pool_namespace
),
351 OSDCapSpec(osd_rwxa_t(OSD_CAP_R
|
353 profile_grants
.emplace_back(OSDCapMatch(profile
.pool_namespace
,
355 OSDCapSpec("rbd", "child_attach"));
356 profile_grants
.emplace_back(OSDCapMatch(profile
.pool_namespace
,
358 OSDCapSpec("rbd", "child_detach"));
362 bool OSDCap::allow_all() const
364 for (auto &grant
: grants
) {
365 if (grant
.allow_all()) {
372 void OSDCap::set_allow_all()
375 grants
.push_back(OSDCapGrant(OSDCapMatch(), OSDCapSpec(OSD_CAP_ANY
)));
378 bool OSDCap::is_capable(const string
& pool_name
, const string
& ns
,
379 const OSDCapPoolTag::app_map_t
& application_metadata
,
380 const string
& object
,
381 bool op_may_read
, bool op_may_write
,
382 const std::vector
<OpInfo::ClassInfo
>& classes
,
383 const entity_addr_t
& addr
) const
385 std::vector
<bool> class_allowed(classes
.size(), false);
386 for (auto &grant
: grants
) {
387 if (grant
.is_capable(pool_name
, ns
, application_metadata
,
388 object
, op_may_read
, op_may_write
, classes
, addr
,
398 namespace qi
= boost::spirit::qi
;
399 namespace ascii
= boost::spirit::ascii
;
400 namespace phoenix
= boost::phoenix
;
402 template <typename Iterator
>
403 struct OSDCapParser
: qi::grammar
<Iterator
, OSDCap()>
405 OSDCapParser() : OSDCapParser::base_type(osdcap
)
419 lexeme
['"' >> +(char_
- '"') >> '"'] |
420 lexeme
['\'' >> +(char_
- '\'') >> '\''];
422 lexeme
['"' >> *(char_
- '"') >> '"'] |
423 lexeme
['\'' >> *(char_
- '\'') >> '\''];
424 unquoted_word
%= +char_("a-zA-Z0-9_./-");
425 str
%= quoted_string
| unquoted_word
;
426 estr
%= equoted_string
| unquoted_word
;
427 network_str
%= +char_("/.:a-fA-F0-9][");
429 spaces
= +ascii::space
;
431 wildcard
= (lit('*') | lit("all")) [_val
= "*"];
433 pool_name
%= -(spaces
>> lit("pool") >> (lit('=') | spaces
) >> str
);
434 nspace
%= (spaces
>> lit("namespace")
435 >> (lit('=') | spaces
)
436 >> estr
>> -char_('*'));
438 // match := [pool[=]<poolname> [namespace[=]<namespace>]] [object_prefix <prefix>]
439 object_prefix
%= -(spaces
>> lit("object_prefix") >> spaces
>> str
);
440 pooltag
%= (spaces
>> lit("tag")
441 >> spaces
>> str
// application
442 >> spaces
>> (wildcard
| str
) // key
443 >> -spaces
>> lit('=') >> -spaces
>> (wildcard
| str
)); // value
446 pooltag
[_val
= phoenix::construct
<OSDCapMatch
>(_1
)] |
447 (nspace
>> pooltag
) [_val
= phoenix::construct
<OSDCapMatch
>(_1
, _2
)] |
448 (pool_name
>> nspace
>> object_prefix
) [_val
= phoenix::construct
<OSDCapMatch
>(_1
, _2
, _3
)] |
449 (pool_name
>> object_prefix
) [_val
= phoenix::construct
<OSDCapMatch
>(_1
, _2
)]
452 // rwxa := * | [r][w][x] [class-read] [class-write]
454 (spaces
>> wildcard
[_val
= OSD_CAP_ANY
]) |
458 ( lit('r')[_val
|= OSD_CAP_R
] ||
459 lit('w')[_val
|= OSD_CAP_W
] ||
460 lit('x')[_val
|= OSD_CAP_X
] )) ||
461 ( (spaces
>> lit("class-read")[_val
|= OSD_CAP_CLS_R
]) ||
462 (spaces
>> lit("class-write")[_val
|= OSD_CAP_CLS_W
]) ));
464 // capspec := * | rwx | class <name> [<method name>]
465 class_name
%= (spaces
>> lit("class") >> spaces
>> str
);
466 method_name
%= -(spaces
>> str
);
468 (rwxa
) [_val
= phoenix::construct
<OSDCapSpec
>(_1
)] |
469 (class_name
>> method_name
) [_val
= phoenix::construct
<OSDCapSpec
>(_1
, _2
)]);
471 // profile := profile <name> [pool[=]<pool> [namespace[=]<namespace>]]
472 profile_name
%= (lit("profile") >> (lit('=') | spaces
) >> str
);
474 (profile_name
>> pool_name
>> nspace
) [_val
= phoenix::construct
<OSDCapProfile
>(_1
, _2
, _3
)] |
475 (profile_name
>> pool_name
) [_val
= phoenix::construct
<OSDCapProfile
>(_1
, _2
)]);
477 // grant := allow match capspec
478 grant
= (*ascii::blank
>>
479 ((lit("allow") >> capspec
>> match
>>
480 -(spaces
>> lit("network") >> spaces
>> network_str
))
481 [_val
= phoenix::construct
<OSDCapGrant
>(_2
, _1
, _3
)] |
482 (lit("allow") >> match
>> capspec
>>
483 -(spaces
>> lit("network") >> spaces
>> network_str
))
484 [_val
= phoenix::construct
<OSDCapGrant
>(_1
, _2
, _3
)] |
485 (profile
>> -(spaces
>> lit("network") >> spaces
>> network_str
))
486 [_val
= phoenix::construct
<OSDCapGrant
>(_1
, _2
)]
488 // osdcap := grant [grant ...]
489 grants
%= (grant
% (lit(';') | lit(',')));
490 osdcap
= grants
[_val
= phoenix::construct
<OSDCap
>(_1
)];
492 qi::rule
<Iterator
> spaces
;
493 qi::rule
<Iterator
, unsigned()> rwxa
;
494 qi::rule
<Iterator
, string()> quoted_string
, equoted_string
;
495 qi::rule
<Iterator
, string()> unquoted_word
;
496 qi::rule
<Iterator
, string()> str
, estr
, network_str
;
497 qi::rule
<Iterator
, string()> wildcard
;
498 qi::rule
<Iterator
, string()> class_name
;
499 qi::rule
<Iterator
, string()> method_name
;
500 qi::rule
<Iterator
, OSDCapSpec()> capspec
;
501 qi::rule
<Iterator
, string()> pool_name
;
502 qi::rule
<Iterator
, string()> nspace
;
503 qi::rule
<Iterator
, string()> object_prefix
;
504 qi::rule
<Iterator
, OSDCapPoolTag()> pooltag
;
505 qi::rule
<Iterator
, OSDCapMatch()> match
;
506 qi::rule
<Iterator
, string()> profile_name
;
507 qi::rule
<Iterator
, OSDCapProfile()> profile
;
508 qi::rule
<Iterator
, OSDCapGrant()> grant
;
509 qi::rule
<Iterator
, std::vector
<OSDCapGrant
>()> grants
;
510 qi::rule
<Iterator
, OSDCap()> osdcap
;
513 bool OSDCap::parse(const string
& str
, ostream
*err
)
515 OSDCapParser
<string::const_iterator
> g
;
516 string::const_iterator iter
= str
.begin();
517 string::const_iterator end
= str
.end();
519 bool r
= qi::phrase_parse(iter
, end
, g
, ascii::space
, *this);
520 if (r
&& iter
== end
)
523 // Make sure no grants are kept after parsing failed!
527 *err
<< "osd capability parse failed, stopped at '" << std::string(iter
, end
)
528 << "' of '" << str
<< "'";