feat: Add storage management for VM creation and resizing
- Introduced storage parameter in VmCreateRequest and VmResizeRequest models. - Implemented storage validation to ensure minimum and maximum limits. - Enhanced CLI to support storage options during VM creation and resizing. - Updated lifecycle management to handle pending resize operations for VM resources. - Added methods for resizing VM disks and validating storage capacity. - Updated API endpoints to accommodate new storage functionality. - Refactored related validation and configuration logic to support storage management.
This commit is contained in:
+40
-2
@@ -22,6 +22,7 @@ from .validation import (
|
||||
parse_password,
|
||||
parse_ports,
|
||||
parse_ram,
|
||||
parse_storage,
|
||||
parse_timeout,
|
||||
parse_username,
|
||||
)
|
||||
@@ -97,6 +98,10 @@ def build_parser(application: Application) -> argparse.ArgumentParser:
|
||||
default=str(application.settings.default_ram_mib),
|
||||
help="RAM, e.g. 512, 1G, 512M",
|
||||
)
|
||||
create.add_argument(
|
||||
"--storage", default=f"{application.settings.default_storage_mib}M",
|
||||
help="private disk capacity, e.g. 30G, 50G, 1T; minimum 30 GiB (bare numbers use GiB)",
|
||||
)
|
||||
create.add_argument(
|
||||
"--guest-ip",
|
||||
"--host-ip",
|
||||
@@ -130,6 +135,14 @@ def build_parser(application: Application) -> argparse.ArgumentParser:
|
||||
)
|
||||
create.add_argument("--json", action="store_true")
|
||||
|
||||
resize = subcommands.add_parser("resize", help="stop, resize CPU/RAM/storage, and boot the retained VM")
|
||||
resize.add_argument("vm", help="VM ID, name, or guest IP")
|
||||
resize.add_argument("--cpu", help="new CPU capacity; omitted values are retained")
|
||||
resize.add_argument("--ram", help="new RAM, e.g. 512M or 4G")
|
||||
resize.add_argument("--storage", help="new private disk capacity, e.g. 30G or 50G; minimum 30 GiB")
|
||||
resize.add_argument("--timeout", help="seconds to wait after SIGTERM before SIGKILL (normally 5s)")
|
||||
resize.add_argument("--json", action="store_true")
|
||||
|
||||
listing = subcommands.add_parser("list", help="list VMs")
|
||||
listing.add_argument("--status", action="append", default=[], help="observed status (repeatable)")
|
||||
output = listing.add_mutually_exclusive_group()
|
||||
@@ -293,6 +306,7 @@ def _run_command(application: Application, args: argparse.Namespace) -> int:
|
||||
VmSpec(
|
||||
cpu=parse_cpu(args.cpu),
|
||||
ram_mib=parse_ram(args.ram),
|
||||
storage_mib=parse_storage(args.storage),
|
||||
guest_ip=guest_ip,
|
||||
username=parse_username(args.username),
|
||||
password=parse_password(password),
|
||||
@@ -321,6 +335,7 @@ def _run_command(application: Application, args: argparse.Namespace) -> int:
|
||||
print(f" Name: {vm.name}")
|
||||
print(f" IP: {vm.guest_ip}")
|
||||
print(f" RAM: {vm.ram_mib} MiB")
|
||||
print(f" Storage: {vm.storage_mib} MiB")
|
||||
print(f" CPU: {vm.cpu}")
|
||||
print(f" TAP: {vm.tap}")
|
||||
print(f" Username: {vm.username}")
|
||||
@@ -329,6 +344,27 @@ def _run_command(application: Application, args: argparse.Namespace) -> int:
|
||||
print(f" SSH: ssh {vm.username}@{vm.guest_ip}")
|
||||
return 0
|
||||
|
||||
if args.command == "resize":
|
||||
if args.cpu is None and args.ram is None and args.storage is None:
|
||||
raise ValidationError("resize requires at least one of --cpu, --ram, or --storage")
|
||||
vm = application.lifecycle.resize(
|
||||
args.vm,
|
||||
cpu=parse_cpu(args.cpu) if args.cpu is not None else None,
|
||||
ram_mib=parse_ram(args.ram) if args.ram is not None else None,
|
||||
storage_mib=parse_storage(args.storage) if args.storage is not None else None,
|
||||
timeout=parse_timeout(args.timeout) if args.timeout is not None else None,
|
||||
)
|
||||
if args.json:
|
||||
_print_json(_vm_output(vm, policy_status="enforced"))
|
||||
else:
|
||||
print(f"VM resized and started: {vm.id}")
|
||||
print(f" CPU: {vm.cpu}")
|
||||
print(f" RAM: {vm.ram_mib} MiB")
|
||||
print(f" Storage: {vm.storage_mib} MiB")
|
||||
print(f" IP: {vm.guest_ip}")
|
||||
print(f" Username: {vm.username}")
|
||||
return 0
|
||||
|
||||
if args.command == "list":
|
||||
vms = [
|
||||
listed for listed in application.lifecycle.list_vms()
|
||||
@@ -349,14 +385,14 @@ def _run_command(application: Application, args: argparse.Namespace) -> int:
|
||||
return 0
|
||||
print(
|
||||
f"{'ID':<37} {'NAME':<20} {'IP':<16} {'USER':<16} {'CPU':<7} "
|
||||
f"{'RAM':<8} {'STATUS':<12} {'POLICY':<12} PID"
|
||||
f"{'RAM':<8} {'DISK(MiB)':<12} {'STATUS':<12} {'POLICY':<12} PID"
|
||||
)
|
||||
for listed in vms:
|
||||
vm = listed.vm
|
||||
pid = vm.pid if vm.pid is not None else ""
|
||||
print(
|
||||
f"{vm.id:<37} {vm.name or '-':<20} {vm.guest_ip:<16} {vm.username:<16} {vm.cpu:<7} "
|
||||
f"{vm.ram_mib:<8} {listed.observed_status:<12} {listed.policy_status:<12} {pid}"
|
||||
f"{vm.ram_mib:<8} {vm.storage_mib or '-':<12} {listed.observed_status:<12} {listed.policy_status:<12} {pid}"
|
||||
)
|
||||
return 0
|
||||
|
||||
@@ -543,6 +579,8 @@ def _vm_output(
|
||||
"name": vm.name,
|
||||
"cpu": vm.cpu,
|
||||
"ram_mib": vm.ram_mib,
|
||||
"storage_mib": vm.storage_mib,
|
||||
"pending_resize": vm.pending_resize.to_dict() if vm.pending_resize is not None else None,
|
||||
"guest_ip": vm.guest_ip,
|
||||
"gateway": vm.gateway,
|
||||
"tap": vm.tap,
|
||||
|
||||
Reference in New Issue
Block a user