]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
src/script: scrape the description of all MON commands
authorSebastian Wagner <sebastian.wagner@suse.com>
Fri, 3 Apr 2020 10:32:52 +0000 (12:32 +0200)
committerSebastian Wagner <sebastian.wagner@suse.com>
Mon, 4 May 2020 15:37:47 +0000 (17:37 +0200)
We gather the MGR commands by importing all modules and then
concat all `Module.COMMAND` lists.

Then we use the C preprocessor to parse the MonCommands.h and
then pretent to output to be Python. Which actually works.

Signed-off-by: Sebastian Wagner <sebastian.wagner@suse.com>
src/script/gen_static_command_descriptions.py [new file with mode: 0644]

diff --git a/src/script/gen_static_command_descriptions.py b/src/script/gen_static_command_descriptions.py
new file mode 100644 (file)
index 0000000..18a6fa6
--- /dev/null
@@ -0,0 +1,164 @@
+"""
+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['rbd'] = mock.Mock()
+    sys.modules['cephfs'] = 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()
+
+    # 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))