]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
doc: build mon_command_api.rst using a homebrew extension
authorKefu Chai <kchai@redhat.com>
Thu, 7 Jan 2021 15:43:26 +0000 (23:43 +0800)
committerKefu Chai <kchai@redhat.com>
Fri, 8 Jan 2021 03:35:42 +0000 (11:35 +0800)
so we can build the command doc using ReadTheDoc infra, without adding
the generated rst file to the source repo.

before this change, all the commands are ordered alphabetically. after
this change, command docs are generated by two directives, and are
ordered separately. we could restructure the directives and merge them.

but let's leave it for a future change if this is important.

for more details on writing sphinx directives, see
https://www.sphinx-doc.org/en/master/extdev/markupapi.html and
https://docutils.sourceforge.io/docs/howto/rst-directives.html

Signed-off-by: Kefu Chai <kchai@redhat.com>
admin/build-doc
admin/doc-python-common-requirements.txt
admin/doc-requirements.txt
doc/.gitignore
doc/_ext/ceph_commands.py [new file with mode: 0644]
doc/api/mon_command_api.rst [new file with mode: 0644]
doc/conf.py
doc/scripts/gen_mon_command_api.py [deleted file]
src/script/gen_static_command_descriptions.py [deleted file]

index 3fdca1a4488af6213f9df331d232b43f93b75a46..bb2a46282c8b31c628a3a1dd71f7b270e8b99adf 100755 (executable)
@@ -64,11 +64,6 @@ install -d -m0755 \
     $TOPDIR/build-doc/output/html \
     $TOPDIR/build-doc/output/man
 
-# required by script/gen_static_command_descriptions.py, which imports ceph_argparse
-export PYTHONPATH=$TOPDIR/src/pybind
-
-$vdir/bin/python $TOPDIR/doc/scripts/gen_mon_command_api.py > $TOPDIR/doc/api/mon_command_api.rst
-
 for opt in "$@"; do
     case $opt in
         html|man|livehtml)
index e4768e3e4300d076ee06da65f82f69a9239b9110..21ca6e85c4ae44b266d642e97da2bb36093f8346 100644 (file)
@@ -1,3 +1 @@
-pcpp
-Jinja2
 src/python-common
index 9e1b4d609ee10c08e77fd6fb52019d096e0b46dc..3ad2f6edbfcce7a2d54b2edeb81b8a27b2e07ddc 100644 (file)
@@ -1,8 +1,10 @@
 Sphinx == 3.2.1
 git+https://github.com/ceph/sphinx-ditaa.git@py3#egg=sphinx-ditaa
 breathe >= 4.20.0
+Jinja2
 pyyaml >= 5.1.2
 Cython
+pcpp
 prettytable
 sphinx-autodoc-typehints
 sphinx-prompt
index 2999c5a87294222c94ac94ad9ee4945df6c84cf7..0c7c74746ae9461d84dd5ee3cd84fbc1e46cb881 100644 (file)
@@ -1,3 +1,2 @@
 /overview.png
 /object_store.png
-/api/mon_command_api.rst
\ No newline at end of file
diff --git a/doc/_ext/ceph_commands.py b/doc/_ext/ceph_commands.py
new file mode 100644 (file)
index 0000000..234c5e5
--- /dev/null
@@ -0,0 +1,429 @@
+import io
+import os
+import sys
+import contextlib
+
+from docutils import nodes
+from docutils.parsers.rst import directives
+from docutils.parsers.rst import Directive
+from jinja2 import Template
+from pcpp.preprocessor import Preprocessor
+from sphinx.util import logging
+from sphinx.util.console import bold
+
+logger = logging.getLogger(__name__)
+
+
+class Flags:
+    NOFORWARD = (1 << 0)
+    OBSOLETE = (1 << 1)
+    DEPRECATED = (1 << 2)
+    MGR = (1 << 3)
+    POLL = (1 << 4)
+    HIDDEN = (1 << 5)
+
+    VALS = {
+        NOFORWARD: 'no_forward',
+        OBSOLETE: 'obsolete',
+        DEPRECATED: 'deprecated',
+        MGR: 'mgr',
+        POLL: 'poll',
+        HIDDEN: 'hidden',
+    }
+
+    def __init__(self, fs):
+        self.fs = fs
+
+    def __contains__(self, other):
+        return other in str(self)
+
+    def __str__(self):
+        keys = Flags.VALS.keys()
+        es = {Flags.VALS[k] for k in keys if self.fs & k == k}
+        return ', '.join(sorted(es))
+
+    def __bool__(self):
+        return bool(str(self))
+
+
+class CmdParam(object):
+    t = {
+        'CephInt': 'int',
+        'CephString': 'str',
+        'CephChoices': 'str',
+        'CephPgid': 'str',
+        'CephOsdName': 'str',
+        'CephPoolname': 'str',
+        'CephObjectname': 'str',
+        'CephUUID': 'str',
+        'CephEntityAddr': 'str',
+        'CephIPAddr': 'str',
+        'CephName': 'str',
+        'CephBool': 'bool',
+        'CephFloat': 'float',
+        'CephFilepath': 'str',
+    }
+
+    bash_example = {
+        'CephInt': '1',
+        'CephString': 'string',
+        'CephChoices': 'choice',
+        'CephPgid': '0',
+        'CephOsdName': 'osd.0',
+        'CephPoolname': 'poolname',
+        'CephObjectname': 'objectname',
+        'CephUUID': 'uuid',
+        'CephEntityAddr': 'entityaddr',
+        'CephIPAddr': '0.0.0.0',
+        'CephName': 'name',
+        'CephBool': 'true',
+        'CephFloat': '0.0',
+        'CephFilepath': '/path/to/file',
+    }
+
+    def __init__(self, type, name, who=None, n=None, req=True, range=None, strings=None,
+                 goodchars=None):
+        self.type = type
+        self.name = name
+        self.who = who
+        self.n = n == 'N'
+        self.req = req != 'false'
+        self.range = range.split('|') if range else []
+        self.strings = strings.split('|') if strings else []
+        self.goodchars = goodchars
+
+        assert who == None
+
+    def help(self):
+        advanced = []
+        if self.type != 'CephString':
+            advanced.append(self.type + ' ')
+        if self.range:
+            advanced.append('range= ``{}`` '.format('..'.join(self.range)))
+        if self.strings:
+            advanced.append('strings=({}) '.format(' '.join(self.strings)))
+        if self.goodchars:
+            advanced.append('goodchars= ``{}`` '.format(self.goodchars))
+        if self.n:
+            advanced.append('(can be repeated)')
+
+        advanced = advanced or ["(string)"]
+        return ' '.join(advanced)
+
+    def mk_example_value(self):
+        if self.type == 'CephChoices' and self.strings:
+            return self.strings[0]
+        if self.range:
+            return self.range[0]
+        return CmdParam.bash_example[self.type]
+
+    def mk_bash_example(self, simple):
+        val = self.mk_example_value()
+
+        if self.type == 'CephBool':
+            return '--' + self.name
+        if simple:
+            if self.type == "CephChoices" and self.strings:
+                return val
+            elif self.type == "CephString" and self.name != 'who':
+                return 'my_' + self.name
+            else:
+                return CmdParam.bash_example[self.type]
+        else:
+            return '--{}={}'.format(self.name, val)
+
+
+class CmdCommand(object):
+    def __init__(self, sig, desc, module=None, perm=None, flags=0, poll=None):
+        self.sig = [s for s in sig if isinstance(s, str)]
+        self.params = sorted([CmdParam(**s) for s in sig if not isinstance(s, str)],
+                             key=lambda p: p.req, reverse=True)
+        self.help = desc
+        self.module = module
+        self.perm = perm
+        self.flags = Flags(flags)
+        self.needs_overload = False
+
+    def prefix(self):
+        return ' '.join(self.sig)
+
+    def is_reasonably_simple(self):
+        if len(self.params) > 3:
+            return False
+        if any(p.n for p in self.params):
+            return False
+        return True
+
+    def mk_bash_example(self):
+        simple = self.is_reasonably_simple()
+        line = ' '.join(['ceph', self.prefix()] + [p.mk_bash_example(simple) for p in self.params])
+        return line
+
+
+class Sig:
+    @staticmethod
+    def _param_to_sig(p):
+        try:
+            return {kv.split('=')[0]: kv.split('=')[1] for kv in p.split(',')}
+        except IndexError:
+            return p
+
+    @staticmethod
+    def from_cmd(cmd):
+        sig = cmd.split()
+        return [Sig._param_to_sig(s) or s for s in sig]
+
+
+TEMPLATE = '''
+.. This file is automatically generated. do not modify
+
+{% for command in commands %}
+
+{{ command.prefix() }}
+{{ command.prefix() | length * '^' }}
+
+{{ command.help | wordwrap(70)}}
+
+Example command:
+
+.. code-block:: bash
+
+    {{ command.mk_bash_example() }}
+{% if command.params %}
+Parameters:
+
+{% for param in command.params %}* **{{param.name}}**: {{ param.help() | wordwrap(70) | indent(2) }}
+{% endfor %}{% endif %}
+Ceph Module:
+
+* *{{ command.module }}*
+
+Required Permissions:
+
+* *{{ command.perm }}*
+
+{% if command.flags %}Command Flags:
+
+* *{{ command.flags }}*
+{% endif %}
+{% endfor %}
+
+'''
+
+class CephMgrCommands(Directive):
+    """
+    extracts commands from specified mgr modules
+    """
+    has_content = True
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = False
+
+    def _normalize_path(self, dirname):
+        my_dir = os.path.dirname(os.path.realpath(__file__))
+        src_dir = os.path.abspath(os.path.join(my_dir, '../..'))
+        return os.path.join(src_dir, dirname)
+
+    def _is_mgr_module(self, dirname, name):
+        if not os.path.isdir(os.path.join(dirname, name)):
+            return False
+        if not os.path.isfile(os.path.join(dirname, name, '__init__.py')):
+            return False
+        return name not in ['tests']
+
+    @contextlib.contextmanager
+    def mocked_modules(self):
+        # src/pybind/mgr/tests
+        from tests import mock
+        mock_imports = ['rados',
+                        'rbd',
+                        'cephfs',
+                        'dateutil',
+                        'dateutil.parser']
+        # make dashboard happy
+        mock_imports += ['ceph_argparse',
+                         'OpenSSL',
+                         'jwt',
+                         'bcrypt',
+                         'scipy',
+                         'jsonpatch',
+                         'rook.rook_client',
+                         'rook.rook_client.ceph',
+                         'cherrypy=3.2.3']
+
+        # make restful happy
+        mock_imports += ['pecan',
+                         'pecan.rest',
+                         'pecan.hooks',
+                         'werkzeug',
+                         'werkzeug.serving']
+
+        for m in mock_imports:
+            args = {}
+            parts = m.split('=', 1)
+            mocked = parts[0]
+            if len(parts) > 1:
+                args['__version__'] = parts[1]
+            sys.modules[mocked] = mock.Mock(**args)
+
+        try:
+            yield
+        finally:
+            for m in mock_imports:
+                mocked = m.split('=', 1)[0]
+                sys.modules.pop(mocked)
+
+    def _collect_module_commands(self, name):
+        with self.mocked_modules():
+            logger.info(bold(f"loading mgr module '{name}'..."))
+            mgr_mod = __import__(name, globals(), locals(), [], 0)
+            from tests import M
+            def subclass(x):
+                try:
+                    return issubclass(x, M)
+                except TypeError:
+                    return False
+            ms = [c for c in mgr_mod.__dict__.values()
+                  if subclass(c) and 'Standby' not in c.__name__]
+            [m] = ms
+            assert isinstance(m.COMMANDS, list)
+            return m.COMMANDS
+
+    def _normalize_command(self, command):
+        if 'handler' in command:
+            del command['handler']
+        command['sig'] = Sig.from_cmd(command['cmd'])
+        del command['cmd']
+        command['flags'] = (1 << 3)
+        command['module'] = 'mgr'
+        return command
+
+    def _render_cmds(self, commands):
+        rendered = Template(TEMPLATE).render(commands=list(commands))
+        lines = rendered.split("\n")
+        assert lines
+        source = self.state_machine.input_lines.source(self.lineno -
+                                                       self.state_machine.input_offset - 1)
+        self.state_machine.insert_input(lines, source)
+
+    def run(self):
+        module_path = self._normalize_path(self.arguments[0])
+        sys.path.insert(0, module_path)
+        os.environ['UNITTEST'] = 'true'
+        modules = [name for name in os.listdir(module_path)
+                   if self._is_mgr_module(module_path, name)]
+        commands = sum([self._collect_module_commands(name) for name in modules], [])
+        cmds = [CmdCommand(**self._normalize_command(c)) for c in commands]
+        cmds = [cmd for cmd in cmds if 'hidden' not in cmd.flags]
+        cmds = sorted(cmds, key=lambda cmd: cmd.sig)
+        self._render_cmds(cmds)
+        return []
+
+class MyProcessor(Preprocessor):
+    def __init__(self):
+        super().__init__()
+        self.cmds = []
+        self.undef('__DATE__')
+        self.undef('__TIME__')
+        self.expand_linemacro = False
+        self.expand_filemacro = False
+        self.expand_countermacro = False
+        self.line_directive = '#line'
+        self.define("__PCPP_VERSION__ " + '')
+        self.define("__PCPP_ALWAYS_FALSE__ 0")
+        self.define("__PCPP_ALWAYS_TRUE__ 1")
+
+    def eval(self, src):
+        _cmds = []
+
+        NONE = 0
+        NOFORWARD = (1 << 0)
+        OBSOLETE = (1 << 1)
+        DEPRECATED = (1 << 2)
+        MGR = (1 << 3)
+        POLL = (1 << 4)
+        HIDDEN = (1 << 5)
+        TELL = (1 << 6)
+
+        def FLAG(a):
+            return a
+
+        def COMMAND(cmd, desc, module, perm):
+            _cmds.append({
+                'cmd': cmd,
+                'desc': desc,
+                'module': module,
+                'perm': perm
+            })
+
+        def COMMAND_WITH_FLAG(cmd, desc, module, perm, flag):
+            _cmds.append({
+                'cmd': cmd,
+                'desc': desc,
+                'module': module,
+                'perm': perm,
+                'flags': flag
+            })
+
+        self.parse(src)
+        out = io.StringIO()
+        self.write(out)
+        out.seek(0)
+        s = out.read()
+        exec(s, globals(), locals())
+        return _cmds
+
+
+class CephMonCommands(Directive):
+    """
+    extracts commands from specified header file
+    """
+    has_content = True
+    required_arguments = 1
+    optional_arguments = 0
+    final_argument_whitespace = True
+
+
+    def _src_dir(self):
+        my_dir = os.path.dirname(os.path.realpath(__file__))
+        return os.path.abspath(os.path.join(my_dir, '../..'))
+
+    def _parse_headers(self, headers):
+        src_dir = self._src_dir()
+        src = '\n'.join(f'#include "{src_dir}/{header}"' for header in headers)
+        return MyProcessor().eval(src)
+
+    def _normalize_command(self, command):
+        if 'handler' in command:
+            del command['handler']
+        command['sig'] = Sig.from_cmd(command['cmd'])
+        del command['cmd']
+        return command
+
+    def _render_cmds(self, commands):
+        rendered = Template(TEMPLATE).render(commands=list(commands))
+        lines = rendered.split("\n")
+        assert lines
+        source = self.state_machine.input_lines.source(self.lineno -
+                                                       self.state_machine.input_offset - 1)
+        self.state_machine.insert_input(lines, source)
+
+    def run(self):
+        headers = self.arguments[0].split()
+        commands = self._parse_headers(headers)
+        cmds = [CmdCommand(**self._normalize_command(c)) for c in commands]
+        cmds = [cmd for cmd in cmds if 'hidden' not in cmd.flags]
+        cmds = sorted(cmds, key=lambda cmd: cmd.sig)
+        self._render_cmds(cmds)
+        return []
+
+
+def setup(app):
+    app.add_directive("ceph-mgr-commands", CephMgrCommands)
+    app.add_directive("ceph-mon-commands", CephMonCommands)
+
+    return {
+        'version': '0.1',
+        'parallel_read_safe': True,
+        'parallel_write_safe': True,
+    }
diff --git a/doc/api/mon_command_api.rst b/doc/api/mon_command_api.rst
new file mode 100644 (file)
index 0000000..1ef7594
--- /dev/null
@@ -0,0 +1,2 @@
+.. ceph-mgr-commands:: src/pybind/mgr
+.. ceph-mon-commands:: src/mon/MonCommands.h src/mgr/MgrCommands.h
index cbc05b1ba16fce654e05d9b04c027e2eff753159..8ba08573ae7d2db7041539afb95e000e8cbd761e 100644 (file)
@@ -102,6 +102,7 @@ extensions = [
     'sphinx_autodoc_typehints',
     'sphinx_substitution_extensions',
     'breathe',
+    'ceph_commands',
     'ceph_releases',
     'sphinxcontrib.openapi'
     ]
diff --git a/doc/scripts/gen_mon_command_api.py b/doc/scripts/gen_mon_command_api.py
deleted file mode 100644 (file)
index dc09210..0000000
+++ /dev/null
@@ -1,206 +0,0 @@
-import os
-import json
-import sys
-from subprocess import check_output
-
-from jinja2 import Template
-
-
-class Flags:
-    NOFORWARD = (1 << 0)
-    OBSOLETE = (1 << 1)
-    DEPRECATED = (1 << 2)
-    MGR = (1 << 3)
-    POLL = (1 << 4)
-    HIDDEN = (1 << 5)
-
-    VALS = {
-        NOFORWARD: 'no_forward',
-        OBSOLETE: 'obsolete',
-        DEPRECATED: 'deprecated',
-        MGR: 'mgr',
-        POLL: 'poll',
-        HIDDEN: 'hidden',
-    }
-
-    def __init__(self, fs):
-        self.fs = fs
-
-    def __contains__(self, other):
-        return other in str(self)
-
-    def __str__(self):
-        keys = Flags.VALS.keys()
-        es = {Flags.VALS[k] for k in keys if self.fs & k == k}
-        return ', '.join(sorted(es))
-
-    def __bool__(self):
-        return bool(str(self))
-
-
-class CmdParam(object):
-    t = {
-        'CephInt': 'int',
-        'CephString': 'str',
-        'CephChoices': 'str',
-        'CephPgid': 'str',
-        'CephOsdName': 'str',
-        'CephPoolname': 'str',
-        'CephObjectname': 'str',
-        'CephUUID': 'str',
-        'CephEntityAddr': 'str',
-        'CephIPAddr': 'str',
-        'CephName': 'str',
-        'CephBool': 'bool',
-        'CephFloat': 'float',
-        'CephFilepath': 'str',
-    }
-
-    bash_example = {
-        'CephInt': '1',
-        'CephString': 'string',
-        'CephChoices': 'choice',
-        'CephPgid': '0',
-        'CephOsdName': 'osd.0',
-        'CephPoolname': 'poolname',
-        'CephObjectname': 'objectname',
-        'CephUUID': 'uuid',
-        'CephEntityAddr': 'entityaddr',
-        'CephIPAddr': '0.0.0.0',
-        'CephName': 'name',
-        'CephBool': 'true',
-        'CephFloat': '0.0',
-        'CephFilepath': '/path/to/file',
-    }
-
-    def __init__(self, type, name, who=None, n=None, req=True, range=None, strings=None,
-                 goodchars=None):
-        self.type = type
-        self.name = name
-        self.who = who
-        self.n = n == 'N'
-        self.req = req != 'false'
-        self.range = range.split('|') if range else []
-        self.strings = strings.split('|') if strings else []
-        self.goodchars = goodchars
-
-        assert who == None
-
-    def help(self):
-        advanced = []
-        if self.type != 'CephString':
-            advanced.append(self.type + ' ')
-        if self.range:
-            advanced.append('range= ``{}`` '.format('..'.join(self.range)))
-        if self.strings:
-            advanced.append('strings=({}) '.format(' '.join(self.strings)))
-        if self.goodchars:
-            advanced.append('goodchars= ``{}`` '.format(self.goodchars))
-        if self.n:
-            advanced.append('(can be repeated)')
-
-        advanced = advanced or ["(string)"]
-        return ' '.join(advanced)
-
-    def mk_example_value(self):
-        if self.type == 'CephChoices' and self.strings:
-            return self.strings[0]
-        if self.range:
-            return self.range[0]
-        return CmdParam.bash_example[self.type]
-
-    def mk_bash_example(self, simple):
-        val = self.mk_example_value()
-
-        if self.type == 'CephBool':
-            return '--' + self.name
-        if simple:
-            if self.type == "CephChoices" and self.strings:
-                return val
-            elif self.type == "CephString" and self.name != 'who':
-                return 'my_' + self.name
-            else:
-                return CmdParam.bash_example[self.type]
-        else:
-            return '--{}={}'.format(self.name, val)
-
-
-class CmdCommand(object):
-    def __init__(self, sig, desc, module=None, perm=None, flags=0, poll=None):
-        self.sig = [s for s in sig if isinstance(s, str)]
-        self.params = sorted([CmdParam(**s) for s in sig if not isinstance(s, str)],
-                             key=lambda p: p.req, reverse=True)
-        self.help = desc
-        self.module = module
-        self.perm = perm
-        self.flags = Flags(flags)
-        self.needs_overload = False
-
-    def prefix(self):
-        return ' '.join(self.sig)
-
-    def is_reasonably_simple(self):
-        if len(self.params) > 3:
-            return False
-        if any(p.n for p in self.params):
-            return False
-        return True
-
-    def mk_bash_example(self):
-        simple = self.is_reasonably_simple()
-        line = ' '.join(['ceph', self.prefix()] + [p.mk_bash_example(simple) for p in self.params])
-        return line
-
-
-tpl = '''
-.. This file is automatically generated. do not modify
-
-{% for command in commands %}
-
-{{ command.prefix() }}
-{{ command.prefix() | length * '^' }}
-
-{{ command.help | wordwrap(70)}}
-
-Example command:
-
-.. code-block:: bash
-
-    {{ command.mk_bash_example() }}
-{% if command.params %}
-Parameters:
-
-{% for param in command.params %}* **{{param.name}}**: {{ param.help() | wordwrap(70) | indent(2) }}
-{% endfor %}{% endif %}
-Ceph Module:
-
-* *{{ command.module }}*
-
-Required Permissions:
-
-* *{{ command.perm }}*
-
-{% if command.flags %}Command Flags:
-
-* *{{ command.flags }}*
-{% endif %}
-{% endfor %}
-
-'''
-
-def mk_sigs(all):
-    sigs = [CmdCommand(**e) for e in all]
-    sigs = [s for s in sigs if 'hidden' not in s.flags]
-    sigs = sorted(sigs, key=lambda f: f.sig)
-
-
-    tm = Template(tpl)
-    msg = tm.render(commands=list(sigs))
-
-    print(msg)
-
-
-if __name__ == '__main__':
-    script_dir = os.path.dirname(os.path.realpath(__file__))
-    commands = json.loads(check_output([sys.executable, script_dir + '/../../src/script/gen_static_command_descriptions.py']))
-    mk_sigs(commands)
diff --git a/src/script/gen_static_command_descriptions.py b/src/script/gen_static_command_descriptions.py
deleted file mode 100644 (file)
index 225f960..0000000
+++ /dev/null
@@ -1,169 +0,0 @@
-"""
-Prints a statically compiled list of all commands.
-
-See
-
-* /admin/doc-requirements.txt
-* /doc/scripts/gen_mon_command_api.py
-
-Rational for putting this file here is to allow others to make use of this output.
-"""
-import json
-import os
-import io
-import sys
-
-from pcpp.preprocessor import Preprocessor, OutputDirective, Action
-
-os.environ['UNITTEST'] = 'true'
-
-script_dir = os.path.dirname(os.path.realpath(__file__))
-
-mgr_dir = os.path.abspath(script_dir + '/../../src/pybind/mgr')
-
-sys.path.insert(0, mgr_dir)
-
-from tests import mock, M
-
-
-def param_to_sig(p):
-    try:
-        return {kv.split('=')[0]: kv.split('=')[1] for kv in p.split(',')}
-    except IndexError:
-        return p
-
-def cmd_to_sig(cmd):
-    sig = cmd.split()
-    return [param_to_sig(s) or s for s in sig]
-
-
-def list_mgr_module(m_name):
-    sys.modules['rados'] = mock.Mock()
-    sys.modules['rbd'] = mock.Mock()
-    sys.modules['cephfs'] = mock.Mock()
-    sys.modules['dateutil'] = mock.Mock()
-    sys.modules['dateutil.parser'] = mock.Mock()
-
-    # make dashboard happy
-    sys.modules['OpenSSL'] = mock.Mock()
-    sys.modules['jwt'] = mock.Mock()
-    sys.modules['bcrypt'] = mock.Mock()
-
-    sys.modules['scipy'] = mock.Mock()
-    sys.modules['jsonpatch'] = mock.Mock()
-    sys.modules['rook.rook_client'] = mock.Mock()
-    sys.modules['rook.rook_client.ceph'] = mock.Mock()
-
-    sys.modules['cherrypy'] = mock.Mock(__version__="3.2.3")
-
-    # make restful happy:
-    sys.modules['pecan'] = mock.Mock()
-    sys.modules['pecan.rest'] = mock.Mock()
-    sys.modules['pecan.hooks'] = mock.Mock()
-    sys.modules['werkzeug'] = mock.Mock()
-    sys.modules['werkzeug.serving'] = mock.Mock()
-
-    mgr_mod = __import__(m_name, globals(), locals(), [], 0)
-
-    def subclass(x):
-        try:
-            return issubclass(x, M)
-        except TypeError:
-            return False
-
-    ms = [c for c in mgr_mod.__dict__.values() if subclass(c) and 'Standby' not in c.__name__]
-    [m] = ms
-    assert isinstance(m.COMMANDS, list)
-    return m.COMMANDS
-
-
-def from_mgr_modules():
-    names = [name for name in os.listdir(mgr_dir)
-             if os.path.isdir(os.path.join(mgr_dir, name)) and
-             os.path.isfile(os.path.join(mgr_dir, name, '__init__.py')) and
-             name not in ['tests']]
-
-    comms = sum([list_mgr_module(name) for name in names], [])
-    for c in comms:
-        if 'handler' in c:
-            del c['handler']
-        c['sig'] = cmd_to_sig(c['cmd'])
-        del c['cmd']
-        c['flags'] = (1 << 3)
-        c['module'] = 'mgr'
-    return comms
-
-
-def from_mon_commands_h():
-    input_str = """
-    #include "{script_dir}/../mon/MonCommands.h"
-    #include "{script_dir}/../mgr/MgrCommands.h"
-    """.format(script_dir=script_dir)
-
-    cmds = []
-
-    class MyProcessor(Preprocessor):
-        def __init__(self):
-            super(MyProcessor, self).__init__()
-            self.undef('__DATE__')
-            self.undef('__TIME__')
-            self.expand_linemacro = False
-            self.expand_filemacro = False
-            self.expand_countermacro = False
-            self.line_directive = '#line'
-            self.define("__PCPP_VERSION__ " + '')
-            self.define("__PCPP_ALWAYS_FALSE__ 0")
-            self.define("__PCPP_ALWAYS_TRUE__ 1")
-            self.parse(input_str)
-            out = io.StringIO()
-            self.write(out)
-            out.seek(0)
-            s = out.read()
-
-            NONE = 0
-            NOFORWARD = (1 << 0)
-            OBSOLETE = (1 << 1)
-            DEPRECATED = (1 << 2)
-            MGR = (1 << 3)
-            POLL = (1 << 4)
-            HIDDEN = (1 << 5)
-            TELL = (1 << 6)
-
-            def FLAG(a):
-                return a
-
-            def COMMAND(cmd, desc, module, perm):
-                cmds.append({
-                    'cmd': cmd,
-                    'desc': desc,
-                    'module': module,
-                    'perm': perm
-                })
-
-            def COMMAND_WITH_FLAG(cmd, desc, module, perm, flag):
-                cmds.append({
-                    'cmd': cmd,
-                    'desc': desc,
-                    'module': module,
-                    'perm': perm,
-                    'flags': flag
-                })
-
-            exec(s, globals(), locals())
-
-    MyProcessor()
-    for c in cmds:
-        if 'handler' in c:
-            del c['handler']
-        c['sig'] = cmd_to_sig(c['cmd'])
-        del c['cmd']
-    return cmds
-
-
-def gen_commands_dicts():
-    comms = from_mon_commands_h() + from_mgr_modules()
-    comms = sorted(comms, key=lambda c: [e for e in c['sig'] if isinstance(e, str)])
-    return comms
-
-if __name__ == '__main__':
-    print(json.dumps(gen_commands_dicts(), indent=2, sort_keys=True))