Skip to content

Commit ab5575f

Browse files
committed
feat: intelmqctl interface
1 parent d2cac08 commit ab5575f

5 files changed

Lines changed: 332 additions & 192 deletions

File tree

intelmq/bin/intelmqctl.py

Lines changed: 18 additions & 169 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,11 @@
2929
from intelmq.lib.datatypes import ReturnType, MESSAGES, LogLevel
3030
from intelmq.lib.processmanager import *
3131
from intelmq.lib.pipeline import PipelineFactory
32+
from intelmq.lib.cli import get_parser, run_handler
3233
import intelmq.lib.upgrades as upgrades
3334

35+
from mininterface import run as mrun
36+
3437
yaml = YAML(typ="safe", pure=True)
3538

3639
try:
@@ -213,167 +216,9 @@ def __init__(self, interactive: bool = False, returntype: ReturnType = ReturnTyp
213216
self.abort('Invalid process manager given: %r, should be one of %r.' '' % (self._processmanagertype, list(process_managers().keys())))
214217

215218
if self._interactive:
216-
parser = argparse.ArgumentParser(
217-
prog=APPNAME,
218-
description=DESCRIPTION,
219-
epilog=EPILOG,
220-
formatter_class=argparse.RawDescriptionHelpFormatter,
221-
)
222-
223-
parser.add_argument('--version', '-v', action='version', version=VERSION)
224-
parser.add_argument('--type', '-t', choices=[i.name.lower() for i in ReturnType], default='text', help='choose if it should return regular text or other machine-readable')
225-
parser.add_argument('--quiet', '-q', action='store_true', help='Quiet mode, useful for reloads initiated scripts like logrotate')
226-
227-
subparsers = parser.add_subparsers(title='subcommands')
228-
229-
parser_list = subparsers.add_parser('list', help='Listing bots or queues')
230-
parser_list.add_argument('kind', choices=['bots', 'queues', 'queues-and-status'])
231-
parser_list.add_argument('--non-zero', '--quiet', '-q', action='store_true',
232-
help='Only list non-empty queues '
233-
'or the IDs of enabled bots.')
234-
parser_list.add_argument('--count', '--sum', '-s', action='store_true',
235-
help='Only show the total '
236-
'number of messages in queues. '
237-
'Only valid for listing queues.')
238-
parser_list.add_argument('--configured', '-c', action='store_true',
239-
help='Only show configured bots')
240-
parser_list.set_defaults(func=self.list)
241-
242-
parser_clear = subparsers.add_parser('clear', help='Clear a queue')
243-
parser_clear.add_argument('queue', help='queue name')
244-
parser_clear.set_defaults(func=self.clear_queue)
245-
246-
parser_log = subparsers.add_parser('log', help='Get last log lines of a bot')
247-
parser_log.add_argument('bot_id', help='bot id', choices=self._configured_bots_list())
248-
parser_log.add_argument('number_of_lines', help='number of lines',
249-
default=10, type=int, nargs='?')
250-
parser_log.add_argument('log_level', help='logging level', choices=[i.name for i in LogLevel], default='INFO', nargs='?')
251-
parser_log.set_defaults(func=self.read_bot_log)
252-
253-
parser_run = subparsers.add_parser('run', help='Run a bot interactively')
254-
parser_run.add_argument('bot_id', choices=self._configured_bots_list())
255-
parser_run.add_argument('--loglevel', '-l', nargs='?', default=None, choices=[i.name for i in LogLevel])
256-
parser_run_subparsers = parser_run.add_subparsers(title='run-subcommands')
257-
258-
parser_run_console = parser_run_subparsers.add_parser('console', help='Get a ipdb live console.')
259-
parser_run_console.add_argument('console_type', nargs='?',
260-
help='You may specify which console should be run. Default is ipdb (if installed)'
261-
' or pudb (if installed) or pdb but you may want to use another one.')
262-
parser_run_console.set_defaults(run_subcommand="console")
263-
264-
parser_run_message = parser_run_subparsers.add_parser('message',
265-
help='Debug bot\'s pipelines. Get the message in the'
266-
' input pipeline, pop it (cut it) and display it, or'
267-
' send the message directly to bot\'s output pipeline(s).')
268-
parser_run_message.add_argument('message_action_kind',
269-
choices=["get", "pop", "send"],
270-
help='get: show the next message in the source pipeline. '
271-
'pop: show and delete the next message in the source pipeline '
272-
'send: Send the given message to the destination pipeline(s).')
273-
parser_run_message.add_argument('msg', nargs='?', help='If send was chosen, put here the message in JSON.')
274-
parser_run_message.set_defaults(run_subcommand="message")
275-
276-
parser_run_process = parser_run_subparsers.add_parser('process', help='Single run of bot\'s process() method.')
277-
parser_run_process.add_argument('--show-sent', '-s', action='store_true',
278-
help='If message is sent through, displays it.')
279-
parser_run_process.add_argument('--dryrun', '-d', action='store_true',
280-
help='Never really pop the message from the input pipeline '
281-
'nor send to output pipeline.')
282-
parser_run_process.add_argument('--msg', '-m',
283-
help='Trick the bot to process this JSON '
284-
'instead of the Message in its pipeline.')
285-
parser_run_process.set_defaults(run_subcommand="process")
286-
parser_run.set_defaults(func=self.bot_run)
287-
288-
parser_check = subparsers.add_parser('check',
289-
help='Check installation and configuration')
290-
parser_check.add_argument('--quiet', '-q', action='store_true',
291-
help='Only print warnings and errors.')
292-
parser_check.add_argument('--no-connections', '-C', action='store_true',
293-
help='Do not test the connections to services like redis.')
294-
parser_check.set_defaults(func=self.check)
295-
296-
parser_help = subparsers.add_parser('help',
297-
help='Show the help')
298-
parser_help.set_defaults(func=parser.print_help)
299-
300-
parser_start = subparsers.add_parser('start', help='Start a bot or botnet')
301-
parser_start.add_argument('bot_id', nargs='?',
302-
choices=self._configured_bots_list())
303-
parser_start.add_argument('--group', help='Start a group of bots',
304-
choices=BOT_GROUP.keys())
305-
parser_start.set_defaults(func=self.bot_start)
306-
307-
parser_stop = subparsers.add_parser('stop', help='Stop a bot or botnet')
308-
parser_stop.add_argument('bot_id', nargs='?',
309-
choices=self._configured_bots_list())
310-
parser_stop.add_argument('--group', help='Stop a group of bots',
311-
choices=BOT_GROUP.keys())
312-
parser_stop.set_defaults(func=self.bot_stop)
313-
314-
parser_restart = subparsers.add_parser('restart', help='Restart a bot or botnet')
315-
parser_restart.add_argument('bot_id', nargs='?',
316-
choices=self._configured_bots_list())
317-
parser_restart.add_argument('--group', help='Restart a group of bots',
318-
choices=BOT_GROUP.keys())
319-
parser_restart.set_defaults(func=self.bot_restart)
320-
321-
parser_reload = subparsers.add_parser('reload', help='Reload a bot or botnet')
322-
parser_reload.add_argument('bot_id', nargs='?',
323-
choices=self._configured_bots_list())
324-
parser_reload.add_argument('--group', help='Reload a group of bots',
325-
choices=BOT_GROUP.keys())
326-
parser_reload.set_defaults(func=self.bot_reload)
327-
328-
parser_status = subparsers.add_parser('status', help='Status of a bot or botnet')
329-
parser_status.add_argument('bot_id', nargs='?',
330-
choices=self._configured_bots_list())
331-
parser_status.add_argument('--group', help='Get status of a group of bots',
332-
choices=BOT_GROUP.keys())
333-
parser_status.set_defaults(func=self.bot_status)
334-
335-
parser_status = subparsers.add_parser('enable', help='Enable a bot')
336-
parser_status.add_argument('bot_id',
337-
choices=self._configured_bots_list())
338-
parser_status.set_defaults(func=self.bot_enable)
339-
340-
parser_status = subparsers.add_parser('disable', help='Disable a bot')
341-
parser_status.add_argument('bot_id',
342-
choices=self._configured_bots_list())
343-
parser_status.set_defaults(func=self.bot_disable)
344-
345-
parser_upgrade_conf = subparsers.add_parser('upgrade-config',
346-
help='Upgrade IntelMQ configuration to a newer version.')
347-
parser_upgrade_conf.add_argument('-p', '--previous',
348-
help='Use this version as the previous one.')
349-
parser_upgrade_conf.add_argument('-d', '--dry-run',
350-
action='store_true', default=False,
351-
help='Do not write any files.')
352-
parser_upgrade_conf.add_argument('-u', '--function',
353-
help='Run this upgrade function.',
354-
choices=upgrades.__all__)
355-
parser_upgrade_conf.add_argument('-f', '--force',
356-
action='store_true',
357-
help='Force running the upgrade procedure.')
358-
parser_upgrade_conf.add_argument('--state-file',
359-
help='The state file location to use.',
360-
default=STATE_FILE_PATH)
361-
parser_upgrade_conf.add_argument('--no-backup',
362-
help='Do not create backups of state and configuration files.',
363-
action='store_true')
364-
parser_upgrade_conf.set_defaults(func=self.upgrade_conf)
365-
366-
parser_debug = subparsers.add_parser('debug', help='Get debugging output.')
367-
parser_debug.add_argument('--get-paths', help='Give all paths',
368-
action='append_const', dest='sections',
369-
const='paths')
370-
parser_debug.add_argument('--get-environment-variables',
371-
help='Give environment variables',
372-
action='append_const', dest='sections',
373-
const='environment_variables')
374-
parser_debug.set_defaults(func=self.debug)
375-
376-
self.parser = parser
219+
self.parser = mrun(get_parser(self, self._configured_bots_list(), BOT_GROUP.keys(), upgrades.__all__),
220+
prog=APPNAME, description=DESCRIPTION, epilog=EPILOG,
221+
add_version=VERSION)
377222
else:
378223
self._processmanager = process_managers()[self._processmanagertype](
379224
self._interactive,
@@ -404,15 +249,11 @@ def load_defaults_configuration(self, silent=False):
404249
setattr(self._parameters, option, value)
405250

406251
def run(self):
407-
results = None
408-
args = self.parser.parse_args()
409-
if 'func' not in args:
410-
sys.exit(self.parser.print_help())
411-
args_dict = vars(args).copy()
252+
m =self.parser
253+
args = m.env
412254

413255
self._quiet = args.quiet
414-
self._returntype = ReturnType[args.type.upper()]
415-
del args_dict['type'], args_dict['quiet'], args_dict['func']
256+
self._returntype = args.type
416257

417258
self._logging_level = 'WARNING' if self._quiet else 'INFO'
418259
self._logger.setLevel(self._logging_level)
@@ -427,7 +268,7 @@ def run(self):
427268
self._quiet
428269
)
429270

430-
retval, results = args.func(**args_dict)
271+
retval, results = run_handler(args.command)
431272

432273
if self._returntype is ReturnType.JSON:
433274
print(json.dumps(results, indent=4))
@@ -438,6 +279,14 @@ def bot_run(self, **kwargs):
438279
# the bot_run method is special in that it mixes plain text
439280
# and json in its output, therefore it is printed here
440281
# and not in the calling `run` method.
282+
283+
# Adapt to newer interface. NOTE we should rather refactor the _processmanager.bot_run to accept these parameters.
284+
subc = kwargs["subcommand"]
285+
del kwargs["subcommand"]
286+
if kwargs["loglevel"]:
287+
kwargs["loglevel"] = kwargs["loglevel"].value
288+
kwargs = {**kwargs, **subc}
289+
441290
retval, results = self._processmanager.bot_run(**kwargs)
442291
print(results)
443292
return retval, None

intelmq/bin/intelmqsetup.py

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
Pip does not (and cannot) create `/opt/intelmq`/user-given ROOT_DIR, as described in
1818
https://github.com/certtools/intelmq/issues/819
1919
"""
20-
import argparse
2120
import os
2221
import shutil
2322
import stat
@@ -31,6 +30,8 @@
3130
from tempfile import NamedTemporaryFile
3231
from typing import Optional
3332

33+
from mininterface import run as mrun
34+
3435
try:
3536
import intelmq_api
3637
import intelmq_api.version
@@ -53,6 +54,7 @@
5354
from intelmq import (CONFIG_DIR, DEFAULT_LOGGING_PATH, ROOT_DIR, VAR_RUN_PATH,
5455
VAR_STATE_PATH, STATE_FILE_PATH)
5556
from intelmq.bin.intelmqctl import IntelMQController
57+
from intelmq.lib.setup_cli import SetupConfig
5658

5759

5860
FILE_OUTPUT_PATH = Path(VAR_STATE_PATH) / 'file-output/'
@@ -298,31 +300,12 @@ def intelmqsetup_manager_generate():
298300

299301

300302
def main():
301-
parser = argparse.ArgumentParser("Set's up directories and example "
302-
"configurations for IntelMQ.")
303-
parser.add_argument('--skip-ownership', action='store_true',
304-
help='Skip setting file ownership')
305-
parser.add_argument('--state-file',
306-
help='The state file location to use.',
307-
default=STATE_FILE_PATH)
308-
parser.add_argument('--webserver-user',
309-
help='The webserver to use instead of auto-detection.')
310-
parser.add_argument('--webserver-configuration-directory',
311-
help='The webserver configuration directory to use instead of auto-detection.')
312-
parser.add_argument('--skip-api',
313-
help='Skip set-up of intelmq-api.',
314-
action='store_true')
315-
parser.add_argument('--skip-webserver',
316-
help='Skip all operations on the webserver configuration, affects the API and Manager.',
317-
action='store_true')
318-
parser.add_argument('--skip-manager',
319-
help='Skip set-up of intelmq-manager.',
320-
action='store_true')
321-
args = parser.parse_args()
303+
m = mrun(SetupConfig, ask_on_empty_cli=True)
304+
args = m.env
322305

323306
basic_checks(skip_ownership=args.skip_ownership)
324307
intelmqsetup_core(ownership=not args.skip_ownership,
325-
state_file=args.state_file)
308+
state_file=str(args.state_file))
326309
if intelmq_api and not args.skip_api:
327310
print(f'Running setup for intelmq-api (version {intelmq_api.version.__version__}).')
328311
intelmqsetup_api(ownership=not args.skip_ownership,

0 commit comments

Comments
 (0)