58 |
58 |
from ganeti import _autoconf
|
59 |
59 |
|
60 |
60 |
import ganeti.rapi.rlib2 # pylint: disable=W0611
|
|
61 |
import ganeti.rapi.connector # pylint: disable=W0611
|
61 |
62 |
|
62 |
63 |
|
63 |
64 |
#: Regular expression for man page names
|
... | ... | |
65 |
66 |
|
66 |
67 |
_TAB_WIDTH = 2
|
67 |
68 |
|
|
69 |
RAPI_URI_ENCODE_RE = re.compile("[^_a-z0-9]+", re.I)
|
|
70 |
|
|
71 |
RAPI_ACCESS_TEXT = {
|
|
72 |
rapi.RAPI_ACCESS_WRITE: "write",
|
|
73 |
rapi.RAPI_ACCESS_READ: "read",
|
|
74 |
}
|
|
75 |
|
|
76 |
assert frozenset(RAPI_ACCESS_TEXT.keys()) == rapi.RAPI_ACCESS_ALL
|
|
77 |
|
68 |
78 |
|
69 |
79 |
class ReSTError(Exception):
|
70 |
80 |
"""Custom class for generating errors in Sphinx.
|
... | ... | |
425 |
435 |
options=options, content=content)
|
426 |
436 |
|
427 |
437 |
|
|
438 |
def _EncodeRapiResourceLink(method, uri):
|
|
439 |
"""Encodes a RAPI resource URI for use as a link target.
|
|
440 |
|
|
441 |
"""
|
|
442 |
parts = [RAPI_URI_ENCODE_RE.sub("-", uri.lower()).strip("-")]
|
|
443 |
|
|
444 |
if method is not None:
|
|
445 |
parts.append(method.lower())
|
|
446 |
|
|
447 |
return "rapi-res-%s" % "+".join(filter(None, parts))
|
|
448 |
|
|
449 |
|
|
450 |
def _MakeRapiResourceLink(method, uri):
|
|
451 |
"""Generates link target name for RAPI resource.
|
|
452 |
|
|
453 |
"""
|
|
454 |
if uri in ["/", "/2"]:
|
|
455 |
# Don't link these
|
|
456 |
return None
|
|
457 |
|
|
458 |
elif uri == "/version":
|
|
459 |
return _EncodeRapiResourceLink(method, uri)
|
|
460 |
|
|
461 |
elif uri.startswith("/2/"):
|
|
462 |
return _EncodeRapiResourceLink(method, uri[len("/2/"):])
|
|
463 |
|
|
464 |
else:
|
|
465 |
raise ReSTError("Unhandled URI '%s'" % uri)
|
|
466 |
|
|
467 |
|
|
468 |
def _BuildRapiAccessTable(res):
|
|
469 |
"""Build a table with access permissions needed for all RAPI resources.
|
|
470 |
|
|
471 |
"""
|
|
472 |
for (uri, handler) in utils.NiceSort(res.items(), key=compat.fst):
|
|
473 |
reslink = _MakeRapiResourceLink(None, uri)
|
|
474 |
if not reslink:
|
|
475 |
# No link was generated
|
|
476 |
continue
|
|
477 |
|
|
478 |
yield ":ref:`%s <%s>`" % (uri, reslink)
|
|
479 |
|
|
480 |
for (method, op_attr, _, _) in sorted(rapi.baserlib.OPCODE_ATTRS):
|
|
481 |
if not (hasattr(handler, method) or hasattr(handler, op_attr)):
|
|
482 |
# Handler doesn't support method
|
|
483 |
continue
|
|
484 |
|
|
485 |
access = rapi.baserlib.GetHandlerAccess(handler, method)
|
|
486 |
|
|
487 |
perms = map(RAPI_ACCESS_TEXT.__getitem__, access)
|
|
488 |
|
|
489 |
if not perms:
|
|
490 |
perms.append("*everyone*")
|
|
491 |
|
|
492 |
yield (" | :ref:`%s <%s>`: %s" %
|
|
493 |
(method, _MakeRapiResourceLink(method, uri),
|
|
494 |
utils.CommaJoin(perms)))
|
|
495 |
|
|
496 |
|
|
497 |
class RapiAccessTable(s_compat.Directive):
|
|
498 |
"""Custom directive to generate table of all RAPI resources.
|
|
499 |
|
|
500 |
See also <http://docutils.sourceforge.net/docs/howto/rst-directives.html>.
|
|
501 |
|
|
502 |
"""
|
|
503 |
has_content = False
|
|
504 |
required_arguments = 0
|
|
505 |
optional_arguments = 0
|
|
506 |
final_argument_whitespace = False
|
|
507 |
option_spec = {}
|
|
508 |
|
|
509 |
def run(self):
|
|
510 |
resources = \
|
|
511 |
rapi.connector.GetHandlers("[node_name]", "[instance_name]",
|
|
512 |
"[group_name]", "[network_name]", "[job_id]",
|
|
513 |
"[disk_index]", "[resource]",
|
|
514 |
translate=self._TranslateResourceUri)
|
|
515 |
|
|
516 |
include_text = "\n".join(_BuildRapiAccessTable(resources))
|
|
517 |
|
|
518 |
# Inject into state machine
|
|
519 |
include_lines = docutils.statemachine.string2lines(include_text, _TAB_WIDTH,
|
|
520 |
convert_whitespace=1)
|
|
521 |
self.state_machine.insert_input(include_lines, self.__class__.__name__)
|
|
522 |
|
|
523 |
return []
|
|
524 |
|
|
525 |
@classmethod
|
|
526 |
def _TranslateResourceUri(cls, *args):
|
|
527 |
"""Translates a resource URI for use in documentation.
|
|
528 |
|
|
529 |
@see: L{rapi.connector.GetHandlers}
|
|
530 |
|
|
531 |
"""
|
|
532 |
return "".join(map(cls._UriPatternToString, args))
|
|
533 |
|
|
534 |
@staticmethod
|
|
535 |
def _UriPatternToString(value):
|
|
536 |
"""Converts L{rapi.connector.UriPattern} to strings.
|
|
537 |
|
|
538 |
"""
|
|
539 |
if isinstance(value, rapi.connector.UriPattern):
|
|
540 |
return value.content
|
|
541 |
else:
|
|
542 |
return value
|
|
543 |
|
|
544 |
|
428 |
545 |
def setup(app):
|
429 |
546 |
"""Sphinx extension callback.
|
430 |
547 |
|
... | ... | |
434 |
551 |
app.add_directive("opcode_result", OpcodeResult)
|
435 |
552 |
app.add_directive("pyassert", PythonAssert)
|
436 |
553 |
app.add_role("pyeval", PythonEvalRole)
|
|
554 |
app.add_directive("rapi_access_table", RapiAccessTable)
|
437 |
555 |
|
438 |
556 |
app.add_config_value("enable_manpages", False, True)
|
439 |
557 |
app.add_role("manpage", _ManPageRole)
|