Add safe ISO download and explicit run/open commands
This commit is contained in:
+61
-4
@@ -13,13 +13,30 @@ from .session import launch_session, session_helper, view
|
||||
|
||||
|
||||
def parser() -> argparse.ArgumentParser:
|
||||
cli = argparse.ArgumentParser(prog='hydra', description='Independent HydraOS VM manager')
|
||||
cli = argparse.ArgumentParser(prog='hydra', description='Hydra OS independent VM manager')
|
||||
cli.add_argument('--config', type=Path, default=Path(os.environ.get('XDG_CONFIG_HOME', Path.home() / '.config')) / 'hydra')
|
||||
cli.add_argument('--state', type=Path, default=Path(os.environ.get('XDG_STATE_HOME', Path.home() / '.local/state')) / 'hydra')
|
||||
cli.add_argument('--runtime', type=Path, default=Path(os.environ.get('XDG_RUNTIME_DIR', f'/tmp/hydra-{os.getuid()}')) / 'hydra')
|
||||
cli.add_argument('--defaults', type=Path, default=Path('/etc/hydra/defaults.toml'),
|
||||
help='Distribution preferences for newly created profiles')
|
||||
commands = cli.add_subparsers(dest='command', required=True)
|
||||
commands = cli.add_subparsers(dest='command')
|
||||
commands.add_parser('menu', help='Open the terminal VM menu')
|
||||
download = commands.add_parser('download', help='Download and validate a direct ISO URL')
|
||||
download.add_argument('url')
|
||||
download.add_argument('--sha256')
|
||||
run = commands.add_parser('run', help='Create, start and display a VM from a local ISO or HTTP(S) URL')
|
||||
run.add_argument('source')
|
||||
run.add_argument('--name', required=True)
|
||||
run.add_argument('--memory', '--ram', type=int, default=4096, help='VM RAM in MiB')
|
||||
run.add_argument('--cpus', type=int, default=2)
|
||||
run.add_argument('--disk-size', default='40G')
|
||||
run.add_argument('--sha256')
|
||||
run.add_argument('--no-view', action='store_true')
|
||||
run.add_argument('--windowed', action='store_true')
|
||||
opener = commands.add_parser('open', help='Start a stopped VM or reconnect a running VM')
|
||||
opener.add_argument('name')
|
||||
opener.add_argument('--no-view', action='store_true')
|
||||
opener.add_argument('--windowed', action='store_true')
|
||||
add = commands.add_parser('add', help='Create a VM profile without starting it')
|
||||
add.add_argument('name')
|
||||
add.add_argument('--iso', type=Path)
|
||||
@@ -48,7 +65,7 @@ def parser() -> argparse.ArgumentParser:
|
||||
viewer = commands.add_parser('view', help='Connect a viewer without changing VM power')
|
||||
viewer.add_argument('name')
|
||||
viewer.add_argument('--windowed', action='store_true')
|
||||
session = commands.add_parser('session', help='Start the minimal HydraOS labwc session')
|
||||
session = commands.add_parser('session', help='Start the minimal Hydra OS labwc session')
|
||||
session.add_argument('--nested', action='store_true')
|
||||
helper = commands.add_parser('_session-helper', help=argparse.SUPPRESS)
|
||||
helper.add_argument('operation')
|
||||
@@ -56,10 +73,47 @@ def parser() -> argparse.ArgumentParser:
|
||||
return cli
|
||||
|
||||
|
||||
def _download_progress():
|
||||
last = None
|
||||
interactive = sys.stderr.isatty()
|
||||
def report(received, total):
|
||||
nonlocal last
|
||||
step = received * 100 // total if total else received // (64 * 1024 * 1024)
|
||||
if last == step:
|
||||
return
|
||||
last = step
|
||||
if not interactive and received and (total is None or received != total):
|
||||
return
|
||||
suffix = f' / {total // (1024 * 1024)} MiB ({step}%)' if total else ' MiB'
|
||||
print(f'Download: {received // (1024 * 1024)}{suffix}', file=sys.stderr)
|
||||
return report
|
||||
|
||||
|
||||
def main(argv: list[str] | None = None) -> int:
|
||||
args = parser().parse_args(argv)
|
||||
cli = parser()
|
||||
args = cli.parse_args(argv)
|
||||
runtime = Runtime(args.config, args.state, args.runtime)
|
||||
try:
|
||||
if args.command is None and not sys.stdin.isatty():
|
||||
cli.print_help()
|
||||
return 0
|
||||
if args.command in (None, 'menu'):
|
||||
from .launch import session_manifest
|
||||
from .menu import run_menu
|
||||
return run_menu(runtime, manifest=session_manifest(runtime))
|
||||
if args.command == 'download':
|
||||
from .media import download_iso
|
||||
print(download_iso(args.url, sha256=args.sha256, progress=_download_progress()))
|
||||
return 0
|
||||
if args.command == 'run':
|
||||
from .launch import run_source
|
||||
return run_source(runtime, args.source, name=args.name, memory=args.memory,
|
||||
cpus=args.cpus, disk_size=args.disk_size, sha256=args.sha256,
|
||||
no_view=args.no_view, windowed=args.windowed, defaults=args.defaults,
|
||||
progress=_download_progress())
|
||||
if args.command == 'open':
|
||||
from .launch import open_vm
|
||||
return open_vm(runtime, args.name, no_view=args.no_view, windowed=args.windowed)
|
||||
if args.command == 'view':
|
||||
return view(runtime, args.name, windowed=args.windowed)
|
||||
elif args.command == 'session':
|
||||
@@ -87,6 +141,9 @@ def main(argv: list[str] | None = None) -> int:
|
||||
result = getattr(runtime, args.command)(args.name)
|
||||
print(json.dumps(result, indent=2))
|
||||
return 0
|
||||
except KeyboardInterrupt:
|
||||
print('hydra: Cancelled', file=sys.stderr)
|
||||
return 130
|
||||
except (RuntimeError, OSError, ValueError, subprocess.TimeoutExpired) as exc:
|
||||
print(f'hydra: {exc}', file=sys.stderr)
|
||||
return 1
|
||||
|
||||
Reference in New Issue
Block a user