From 3ef230143d046dd53b877b224b8ebab1fdc61338 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 27 Feb 2021 22:02:45 +0100 Subject: Adding container-shell.py. Signed-off-by: Daniel Baumann --- Makefile | 4 +- bin.py/container-shell.py | 151 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 153 insertions(+), 2 deletions(-) create mode 100755 bin.py/container-shell.py diff --git a/Makefile b/Makefile index 2b1fb37..92e498c 100644 --- a/Makefile +++ b/Makefile @@ -62,7 +62,7 @@ install: build mkdir -p $(DESTDIR)/etc/$(PROJECT)/$(PROGRAM)/hooks mkdir -p $(DESTDIR)/usr/bin - cp -r bin/* $(DESTDIR)/usr/bin + cp -r bin/* bin.py/* $(DESTDIR)/usr/bin mkdir -p $(DESTDIR)/usr/lib/$(PROJECT) cp -r lib/* $(DESTDIR)/usr/lib/$(PROJECT) @@ -180,7 +180,7 @@ uninstall: rm -rf $(DESTDIR)/usr/lib/$(PROJECT)/$(PROGRAM) rmdir --ignore-fail-on-non-empty --parents $(DESTDIR)/usr/lib || true - for FILE in bin/*; \ + for FILE in bin/* bin.py/*; \ do \ rm -f $(DESTDIR)/usr/bin/$$(basename $${FILE}); \ done diff --git a/bin.py/container-shell.py b/bin.py/container-shell.py new file mode 100755 index 0000000..727cbf8 --- /dev/null +++ b/bin.py/container-shell.py @@ -0,0 +1,151 @@ +#!/usr/bin/python3 + +# Copyright (C) 2014-2021 Daniel Baumann +# +# SPDX-License-Identifier: GPL-3.0+ +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +from prompt_toolkit import PromptSession + +from prompt_toolkit.auto_suggest import AutoSuggestFromHistory +from prompt_toolkit.completion import NestedCompleter +from prompt_toolkit.history import FileHistory +from prompt_toolkit.key_binding import KeyBindings +from prompt_toolkit.shortcuts import set_title +from prompt_toolkit.styles import Style + +from pathlib import Path +from subprocess import Popen, PIPE + +import os +import socket + +home = str(Path.home()) +hostname = socket.getfqdn() + +container_completer = NestedCompleter.from_nested_dict({ + 'auto': { '--force': None, '--start': None, '--stop': None, }, + 'console': { '--name': None, }, + 'create': { '--name': None, '--capability': None, '--drop-capability': None, '--script': None, '--verbose': None, '--bind': None, '--bind-ro': None, }, + 'enter': { '--name': None, }, + 'key': { '--add': None, '--list': None, '--remove': None, }, + 'limit': { '--name': None, '--blockio-device-weight': None, '--blockio-read-bandwidth': None, '--blockio-weight': None, '--blockio-write-bandwidth': None, '--cpu-quota': None, '--cpu-shares': None, '--memory-limit': None, '--tasks-max': None, }, + 'list': { '--all': None, + '--format': {'cli': None, 'csv': None, 'json': None, 'nwdiag': None, 'shell': None, 'yaml': None, 'xml': None, }, + '--host': None, '--other': None, '--started': None, '--stopped': None, + }, + 'log': { '--date': None, '--name': None, }, + 'move': { '--force': None, '--new': None, '--old': None, }, + 'remove': { '--force': None, '--name': None, }, + 'restart': { '--name': None, }, + 'start': { '--name': None, '--verbose': None, }, + 'status': { '--name': None, }, + 'stop': { '--force': None, '--name': None, }, + 'top': { '--force': None, '--name': None, }, + 'version': None, +}) + +style = Style.from_dict({ + 'completion-menu.completion': 'bg:ansiblack ansiwhite', + 'completion-menu.completion.current': 'bg:ansibrightblack ansiwhite', + 'bottom-toolbar': 'bg:ansiwhite ansibrightblack', + 'scrollbar.background': 'bg:ansibrightblack', + 'scrollbar.button': 'bg:ansiwhite', +}) + +kb = KeyBindings() + +@kb.add('c-space') # Ctrl-Space +def _(event): + b = event.app.current_buffer + + if b.complete_state: + b.complete_next() + else: + b.start_completion(select_first=False) + +def run(commands): + with Popen(commands, stdout=PIPE, bufsize=1, universal_newlines=True) as p: + for line in p.stdout: + print(line, end='') + + if p.returncode != 0: + raise CalledProcessError(p.returncode, p.args) + +def bottom_toolbar(): + return hostname + ' | [Ctrl-Space] for menu | [Ctrl-D] to exit | [Ctrl-R] for history search | help for documentation' + +def main(): + set_title('container-shell: ' + hostname) + + print('container-shell: ' + hostname) + print() + + os.makedirs(home + '/.cache/container', exist_ok=True) + history = FileHistory(home + '/.cache/container/history') + + session = PromptSession( + auto_suggest=AutoSuggestFromHistory(), + bottom_toolbar=bottom_toolbar, + completer=container_completer, + complete_while_typing=True, + history=history, + enable_history_search=True, + key_bindings=kb, + style=style, + ) + + while True: + try: + text = session.prompt('container: ') + except KeyboardInterrupt: + continue # Ctrl-C + except EOFError: + break # Ctrl-D + else: + if not text: + continue + + try: + command = text.split(None, 1)[0] + except: + command = '' + + if command == 'exit' or command == 'logoff' or command == 'logout' or command == 'quit': + break + + if command != 'help' and not os.path.exists('/usr/lib/open-infrastructure/container/' + command): + print('\'' + command + '\': - no such command') + continue + + commands = text.split() + + if command == 'help': + try: + manpage = text.split(None, 1)[1] + manpage = 'container-' + manpage + except: + manpage = 'container' + + commands = [ 'man', manpage ] + else: + commands.insert(0, '/usr/bin/container') + + print() + run(commands) + print() + +if __name__ == '__main__': + main() -- cgit v1.2.3