6 from docutils
.parsers
.rst
import directives
7 from docutils
.parsers
.rst
import Directive
8 from jinja2
import Template
9 from pcpp
.preprocessor
import Preprocessor
10 from sphinx
.util
import logging
11 from sphinx
.util
.console
import bold
13 logger
= logging
.getLogger(__name__
)
25 NOFORWARD
: 'no_forward',
27 DEPRECATED
: 'deprecated',
33 def __init__(self
, fs
):
36 def __contains__(self
, other
):
37 return other
in str(self
)
40 keys
= Flags
.VALS
.keys()
41 es
= {Flags
.VALS
[k
] for k
in keys
if self
.fs
& k
== k
}
42 return ', '.join(sorted(es
))
45 return bool(str(self
))
48 class CmdParam(object):
55 'CephPoolname': 'str',
56 'CephObjectname': 'str',
58 'CephEntityAddr': 'str',
63 'CephFilepath': 'str',
68 'CephString': 'string',
69 'CephChoices': 'choice',
71 'CephOsdName': 'osd.0',
72 'CephPoolname': 'poolname',
73 'CephObjectname': 'objectname',
75 'CephEntityAddr': 'entityaddr',
76 'CephIPAddr': '0.0.0.0',
80 'CephFilepath': '/path/to/file',
83 def __init__(self
, type, name
,
84 who
=None, n
=None, req
=True, range=None, strings
=None,
90 self
.req
= req
!= 'false'
91 self
.range = range.split('|') if range else []
92 self
.strings
= strings
.split('|') if strings
else []
93 self
.goodchars
= goodchars
99 if self
.type != 'CephString':
100 advanced
.append(self
.type + ' ')
102 advanced
.append('range= ``{}`` '.format('..'.join(self
.range)))
104 advanced
.append('strings=({}) '.format(' '.join(self
.strings
)))
106 advanced
.append('goodchars= ``{}`` '.format(self
.goodchars
))
108 advanced
.append('(can be repeated)')
110 advanced
= advanced
or ["(string)"]
111 return ' '.join(advanced
)
113 def mk_example_value(self
):
114 if self
.type == 'CephChoices' and self
.strings
:
115 return self
.strings
[0]
118 return CmdParam
.bash_example
[self
.type]
120 def mk_bash_example(self
, simple
):
121 val
= self
.mk_example_value()
123 if self
.type == 'CephBool':
124 return '--' + self
.name
126 if self
.type == "CephChoices" and self
.strings
:
128 elif self
.type == "CephString" and self
.name
!= 'who':
129 return 'my_' + self
.name
131 return CmdParam
.bash_example
[self
.type]
133 return '--{}={}'.format(self
.name
, val
)
136 class CmdCommand(object):
137 def __init__(self
, prefix
, args
, desc
,
138 module
=None, perm
=None, flags
=0, poll
=None):
140 self
.params
= sorted([CmdParam(**arg
) for arg
in args
],
141 key
=lambda p
: p
.req
, reverse
=True)
145 self
.flags
= Flags(flags
)
146 self
.needs_overload
= False
148 def is_reasonably_simple(self
):
149 if len(self
.params
) > 3:
151 if any(p
.n
for p
in self
.params
):
155 def mk_bash_example(self
):
156 simple
= self
.is_reasonably_simple()
157 line
= ' '.join(['ceph', self
.prefix
] + [p
.mk_bash_example(simple
) for p
in self
.params
])
163 def _parse_arg_desc(desc
):
165 return dict(kv
.split('=') for kv
in desc
.split(',') if kv
)
171 parsed
= [Sig
._parse
_arg
_desc
(s
) or s
for s
in cmd
.split()]
172 prefix
= [s
for s
in parsed
if isinstance(s
, str)]
173 params
= [s
for s
in parsed
if not isinstance(s
, str)]
174 return ' '.join(prefix
), params
177 def parse_args(args
):
178 return [Sig
._parse
_arg
_desc
(arg
) for arg
in args
.split()]
182 .. This file is automatically generated. do not modify
184 {% for command in commands %}
187 {{ command.prefix | length * '^' }}
189 {{ command.help | wordwrap(70)}}
195 {{ command.mk_bash_example() }}
196 {% if command.params %}
199 {% for param in command.params %}* **{{param.name}}**: {{ param.help() | wordwrap(70) | indent(2) }}
200 {% endfor %}{% endif %}
203 * *{{ command.module }}*
205 Required Permissions:
207 * *{{ command.perm }}*
209 {% if command.flags %}Command Flags:
211 * *{{ command.flags }}*
218 class CephMgrCommands(Directive
):
220 extracts commands from specified mgr modules
223 required_arguments
= 1
224 optional_arguments
= 0
225 final_argument_whitespace
= False
226 option_spec
= {'python_path': directives
.unchanged
}
228 def _normalize_path(self
, dirname
):
229 my_dir
= os
.path
.dirname(os
.path
.realpath(__file__
))
230 src_dir
= os
.path
.abspath(os
.path
.join(my_dir
, '../..'))
231 return os
.path
.join(src_dir
, dirname
)
233 def _is_mgr_module(self
, dirname
, name
):
234 if not os
.path
.isdir(os
.path
.join(dirname
, name
)):
236 if not os
.path
.isfile(os
.path
.join(dirname
, name
, '__init__.py')):
238 return name
not in ['tests']
240 @contextlib.contextmanager
241 def mocked_modules(self
):
242 # src/pybind/mgr/tests
243 from tests
import mock
244 mock_imports
= ['rados',
249 # make dashboard happy
250 mock_imports
+= ['OpenSSL',
256 'rook.rook_client.ceph',
260 mock_imports
+= ['pecan',
266 for m
in mock_imports
:
268 parts
= m
.split('=', 1)
271 args
['__version__'] = parts
[1]
272 sys
.modules
[mocked
] = mock
.Mock(**args
)
277 for m
in mock_imports
:
278 mocked
= m
.split('=', 1)[0]
279 sys
.modules
.pop(mocked
)
281 def _collect_module_commands(self
, name
):
282 with self
.mocked_modules():
283 logger
.info(bold(f
"loading mgr module '{name}'..."))
284 mgr_mod
= __import__(name
, globals(), locals(), [], 0)
289 return issubclass(x
, M
)
292 ms
= [c
for c
in mgr_mod
.__dict
__.values()
293 if subclass(c
) and 'Standby' not in c
.__name
__]
295 assert isinstance(m
.COMMANDS
, list)
298 def _normalize_command(self
, command
):
299 if 'handler' in command
:
300 del command
['handler']
302 command
['prefix'], command
['args'] = Sig
.parse_cmd(command
['cmd'])
305 command
['args'] = Sig
.parse_args(command
['args'])
306 command
['flags'] = (1 << 3)
307 command
['module'] = 'mgr'
310 def _render_cmds(self
, commands
):
311 rendered
= Template(TEMPLATE
).render(commands
=list(commands
))
312 lines
= rendered
.split("\n")
314 lineno
= self
.lineno
- self
.state_machine
.input_offset
- 1
315 source
= self
.state_machine
.input_lines
.source(lineno
)
316 self
.state_machine
.insert_input(lines
, source
)
319 module_path
= self
._normalize
_path
(self
.arguments
[0])
320 sys
.path
.insert(0, module_path
)
321 for path
in self
.options
.get('python_path', '').split(':'):
322 sys
.path
.insert(0, self
._normalize
_path
(path
))
323 os
.environ
['UNITTEST'] = 'true'
324 modules
= [name
for name
in os
.listdir(module_path
)
325 if self
._is
_mgr
_module
(module_path
, name
)]
326 commands
= sum([self
._collect
_module
_commands
(name
) for name
in modules
], [])
327 cmds
= [CmdCommand(**self
._normalize
_command
(c
)) for c
in commands
]
328 cmds
= [cmd
for cmd
in cmds
if 'hidden' not in cmd
.flags
]
329 cmds
= sorted(cmds
, key
=lambda cmd
: cmd
.prefix
)
330 self
._render
_cmds
(cmds
)
334 class MyProcessor(Preprocessor
):
338 self
.undef('__DATE__')
339 self
.undef('__TIME__')
340 self
.expand_linemacro
= False
341 self
.expand_filemacro
= False
342 self
.expand_countermacro
= False
343 self
.line_directive
= '#line'
344 self
.define("__PCPP_VERSION__ " + '')
345 self
.define("__PCPP_ALWAYS_FALSE__ 0")
346 self
.define("__PCPP_ALWAYS_TRUE__ 1")
354 DEPRECATED
= (1 << 2)
363 def COMMAND(cmd
, desc
, module
, perm
):
371 def COMMAND_WITH_FLAG(cmd
, desc
, module
, perm
, flag
):
385 exec(s
, globals(), locals())
389 class CephMonCommands(Directive
):
391 extracts commands from specified header file
394 required_arguments
= 1
395 optional_arguments
= 0
396 final_argument_whitespace
= True
399 my_dir
= os
.path
.dirname(os
.path
.realpath(__file__
))
400 return os
.path
.abspath(os
.path
.join(my_dir
, '../..'))
402 def _parse_headers(self
, headers
):
403 src_dir
= self
._src
_dir
()
404 src
= '\n'.join(f
'#include "{src_dir}/{header}"' for header
in headers
)
405 return MyProcessor().eval(src
)
407 def _normalize_command(self
, command
):
408 if 'handler' in command
:
409 del command
['handler']
410 command
['prefix'], command
['args'] = Sig
.parse_cmd(command
['cmd'])
414 def _render_cmds(self
, commands
):
415 rendered
= Template(TEMPLATE
).render(commands
=list(commands
))
416 lines
= rendered
.split("\n")
418 lineno
= self
.lineno
- self
.state_machine
.input_offset
- 1
419 source
= self
.state_machine
.input_lines
.source(lineno
)
420 self
.state_machine
.insert_input(lines
, source
)
423 headers
= self
.arguments
[0].split()
424 commands
= self
._parse
_headers
(headers
)
425 cmds
= [CmdCommand(**self
._normalize
_command
(c
)) for c
in commands
]
426 cmds
= [cmd
for cmd
in cmds
if 'hidden' not in cmd
.flags
]
427 cmds
= sorted(cmds
, key
=lambda cmd
: cmd
.prefix
)
428 self
._render
_cmds
(cmds
)
433 app
.add_directive("ceph-mgr-commands", CephMgrCommands
)
434 app
.add_directive("ceph-mon-commands", CephMonCommands
)
438 'parallel_read_safe': True,
439 'parallel_write_safe': True,