2019-09-20 10:10:25 +02:00
|
|
|
#!/usr/bin/python3
|
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
import argparse
|
2019-09-23 15:30:06 +02:00
|
|
|
import time
|
|
|
|
import sys
|
|
|
|
|
|
|
|
from concurrent.futures import ThreadPoolExecutor
|
2019-09-20 10:10:25 +02:00
|
|
|
|
|
|
|
from lxcmgr import lxcmgr
|
2019-09-23 15:30:06 +02:00
|
|
|
from lxcmgr.pkgmgr import App, Stage, PkgMgr
|
2019-09-20 10:10:25 +02:00
|
|
|
|
|
|
|
parser = argparse.ArgumentParser(description='LXC container and package manager')
|
|
|
|
subparsers = parser.add_subparsers()
|
|
|
|
|
|
|
|
parser_list = subparsers.add_parser('list')
|
|
|
|
subparsers_list = parser_list.add_subparsers()
|
|
|
|
parser_list_installed = subparsers_list.add_parser('installed')
|
|
|
|
parser_list_installed.set_defaults(action='list-installed')
|
|
|
|
parser_list_online = subparsers_list.add_parser('online')
|
|
|
|
parser_list_online.set_defaults(action='list-online')
|
|
|
|
parser_list_updates = subparsers_list.add_parser('updates')
|
|
|
|
parser_list_updates.set_defaults(action='list-updates')
|
|
|
|
|
|
|
|
parser_install = subparsers.add_parser('install')
|
|
|
|
parser_install.set_defaults(action='install')
|
|
|
|
parser_install.add_argument('app', help='Application to install')
|
|
|
|
|
|
|
|
parser_update = subparsers.add_parser('update')
|
|
|
|
parser_update.set_defaults(action='update')
|
|
|
|
parser_update.add_argument('app', help='Application to update')
|
|
|
|
|
|
|
|
parser_uninstall = subparsers.add_parser('uninstall')
|
|
|
|
parser_uninstall.set_defaults(action='uninstall')
|
|
|
|
parser_uninstall.add_argument('app', help='Application to uninstall')
|
|
|
|
|
|
|
|
parser_container = subparsers.add_parser('container')
|
|
|
|
subparsers_container = parser_container.add_subparsers()
|
|
|
|
|
|
|
|
parser_container_prepare = subparsers_container.add_parser('prepare')
|
|
|
|
parser_container_prepare.set_defaults(action='container-prepare')
|
|
|
|
parser_container_prepare.add_argument('layers', help='OverlayFS LXC rootfs layers')
|
|
|
|
parser_container_prepare.add_argument('container', help='Container name')
|
|
|
|
parser_container_prepare.add_argument('lxc', nargs=argparse.REMAINDER)
|
|
|
|
|
|
|
|
parser_container_cleanup = subparsers_container.add_parser('cleanup')
|
|
|
|
parser_container_cleanup.set_defaults(action='container-cleanup')
|
|
|
|
parser_container_cleanup.add_argument('container', help='Container name')
|
|
|
|
parser_container_cleanup.add_argument('lxc', nargs=argparse.REMAINDER)
|
|
|
|
|
|
|
|
def print_apps(packages):
|
|
|
|
for app, meta in packages.items():
|
2019-10-05 22:42:13 +02:00
|
|
|
print(app, meta['version'])
|
2019-09-20 10:10:25 +02:00
|
|
|
for key, value in meta['meta'].items():
|
|
|
|
print(' {}: {}'.format(key, value))
|
|
|
|
|
|
|
|
def list_online():
|
|
|
|
pm = PkgMgr()
|
2019-09-23 15:30:06 +02:00
|
|
|
apps = pm.online_packages['apps']
|
|
|
|
if apps:
|
|
|
|
print_apps(apps)
|
|
|
|
else:
|
|
|
|
print('Repository lists no applications packages.')
|
2019-09-20 10:10:25 +02:00
|
|
|
|
|
|
|
def list_installed():
|
|
|
|
pm = PkgMgr()
|
2019-09-23 15:30:06 +02:00
|
|
|
apps = pm.installed_packages['apps']
|
|
|
|
if apps:
|
|
|
|
print_apps(apps)
|
|
|
|
else:
|
|
|
|
print('No applications packages installed.')
|
2019-09-20 10:10:25 +02:00
|
|
|
|
|
|
|
def list_updates():
|
|
|
|
pm = PkgMgr()
|
2019-09-23 15:30:06 +02:00
|
|
|
apps = pm.installed_packages['apps']
|
|
|
|
if apps:
|
|
|
|
updateable_apps = [app for app in apps if pm.has_update(app)]
|
|
|
|
if updateable_apps:
|
|
|
|
updates = {name: meta for (name, meta) in pm.online_packages['apps'].items() if name in updateable_apps}
|
|
|
|
print_apps(updates)
|
|
|
|
else:
|
|
|
|
print('All installed application packages are up-to-date.')
|
|
|
|
else:
|
|
|
|
print('No applications packages installed.')
|
2019-09-20 10:10:25 +02:00
|
|
|
|
2019-09-24 10:52:33 +02:00
|
|
|
def run_install_action(action, app):
|
2019-09-23 15:30:06 +02:00
|
|
|
with ThreadPoolExecutor() as executor:
|
2019-09-24 10:52:33 +02:00
|
|
|
future = executor.submit(action, app)
|
2019-09-23 15:30:06 +02:00
|
|
|
while not future.done():
|
2019-09-24 10:52:33 +02:00
|
|
|
time.sleep(0.25)
|
2019-09-23 15:30:06 +02:00
|
|
|
print_install_status(app)
|
|
|
|
# Get the result of the future and let it raise exception, if there was any
|
|
|
|
data = future.result()
|
|
|
|
print_install_status(app)
|
|
|
|
|
|
|
|
def print_install_status(app):
|
2019-09-24 09:59:45 +02:00
|
|
|
# Prints current status of the installation. Uses ANSI "erase line" and "carriage return" to rewrite the status on single line.
|
2019-09-23 15:30:06 +02:00
|
|
|
if app.stage == Stage.QUEUED:
|
|
|
|
print('\x1b[KQueued...', end='\r')
|
|
|
|
elif app.stage == Stage.DOWNLOAD:
|
|
|
|
print('\x1b[KDownloading... {} % ({} / {} bytes)'.format(app.percent_processed, app.bytes_processed, app.bytes_total), end='\r')
|
|
|
|
elif app.stage == Stage.UNPACK:
|
|
|
|
print('\x1b[KUnpacking...', end='\r')
|
|
|
|
elif app.stage == Stage.INSTALL:
|
|
|
|
print('\x1b[KInstalling...', end='\r')
|
|
|
|
elif app.stage == Stage.UNINSTALL:
|
|
|
|
print('\x1b[KUninstalling...', end='\r')
|
2019-09-24 10:52:33 +02:00
|
|
|
elif app.stage == Stage.UPDATE:
|
|
|
|
print('\x1b[KUpdating...', end='\r')
|
2019-09-23 15:30:06 +02:00
|
|
|
elif app.stage == Stage.DONE:
|
|
|
|
print('\x1b[KDone.')
|
2019-09-20 10:10:25 +02:00
|
|
|
|
2019-09-24 10:52:33 +02:00
|
|
|
def install_app(app):
|
|
|
|
pm = PkgMgr()
|
|
|
|
app = App(app)
|
|
|
|
run_install_action(pm.install_app, app)
|
|
|
|
|
2019-09-20 10:10:25 +02:00
|
|
|
def update_app(app):
|
|
|
|
pm = PkgMgr()
|
2019-09-24 19:45:25 +02:00
|
|
|
app = App(app)
|
2019-09-24 10:52:33 +02:00
|
|
|
run_install_action(pm.update_app, app)
|
2019-09-20 10:10:25 +02:00
|
|
|
|
|
|
|
def uninstall_app(app):
|
|
|
|
pm = PkgMgr()
|
2019-09-24 19:45:25 +02:00
|
|
|
app = App(app)
|
2019-09-24 10:52:33 +02:00
|
|
|
run_install_action(pm.uninstall_app, app)
|
2019-09-20 10:10:25 +02:00
|
|
|
|
|
|
|
args = parser.parse_args()
|
2019-09-23 15:30:06 +02:00
|
|
|
if not hasattr(args, 'action'):
|
|
|
|
parser.print_usage()
|
|
|
|
sys.exit(1)
|
|
|
|
|
2019-09-20 10:10:25 +02:00
|
|
|
if args.action == 'list-installed':
|
|
|
|
list_installed()
|
|
|
|
elif args.action == 'list-online':
|
|
|
|
list_online()
|
|
|
|
elif args.action == 'list-updates':
|
|
|
|
list_updates()
|
|
|
|
elif args.action == 'install':
|
|
|
|
install_app(args.app)
|
|
|
|
elif args.action == 'update':
|
|
|
|
update_app(args.app)
|
|
|
|
elif args.action == 'uninstall':
|
|
|
|
uninstall_app(args.app)
|
|
|
|
elif args.action == 'container-prepare':
|
|
|
|
# Used with LXC hooks on container startup
|
|
|
|
lxcmgr.prepare_container(args.container, args.layers)
|
|
|
|
elif args.action == 'container-cleanup':
|
|
|
|
# Used with LXC hooks on container stop
|
|
|
|
lxcmgr.cleanup_container(args.container)
|