portacode 1.4.16.dev6__py3-none-any.whl → 1.4.16.dev7__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- portacode/_version.py +2 -2
- portacode/connection/handlers/WEBSOCKET_PROTOCOL.md +11 -10
- portacode/connection/handlers/proxmox_infra.py +88 -6
- {portacode-1.4.16.dev6.dist-info → portacode-1.4.16.dev7.dist-info}/METADATA +1 -1
- {portacode-1.4.16.dev6.dist-info → portacode-1.4.16.dev7.dist-info}/RECORD +9 -9
- {portacode-1.4.16.dev6.dist-info → portacode-1.4.16.dev7.dist-info}/WHEEL +0 -0
- {portacode-1.4.16.dev6.dist-info → portacode-1.4.16.dev7.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.16.dev6.dist-info → portacode-1.4.16.dev7.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.16.dev6.dist-info → portacode-1.4.16.dev7.dist-info}/top_level.txt +0 -0
portacode/_version.py
CHANGED
|
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
|
|
|
28
28
|
commit_id: COMMIT_ID
|
|
29
29
|
__commit_id__: COMMIT_ID
|
|
30
30
|
|
|
31
|
-
__version__ = version = '1.4.16.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4, 16, '
|
|
31
|
+
__version__ = version = '1.4.16.dev7'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 16, 'dev7')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -1146,16 +1146,17 @@ Provides system information in response to a `system_info` action. Handled by [`
|
|
|
1146
1146
|
* `vmid` (string|null): Numeric CT ID.
|
|
1147
1147
|
* `hostname` (string|null): Hostname configured in the CT.
|
|
1148
1148
|
* `template` (string|null): Template identifier used.
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
* `
|
|
1149
|
+
* `storage` (string|null): Storage pool backing the rootfs.
|
|
1150
|
+
* `disk_gib` (integer): Rootfs size in GiB.
|
|
1151
|
+
* `ram_mib` (integer): Memory size in MiB.
|
|
1152
|
+
* `cpu_share` (number): vCPU-equivalent share requested at creation.
|
|
1153
|
+
* `status` (string): Lowercase lifecycle status (e.g., `running`, `stopped`, `deleted`).
|
|
1154
|
+
* `created_at` (string|null): ISO timestamp recorded when the CT was provisioned.
|
|
1155
|
+
* `managed` (boolean): `true` for Portacode-managed entries.
|
|
1156
|
+
* `matches_default_storage` (boolean): `true` when this container is backed by the default storage pool used for new Portacode containers.
|
|
1157
|
+
* `type` (string): Either `lxc` or `qemu`, indicating whether we enumerated the container from the LXC or QEMU APIs.
|
|
1158
|
+
* `unmanaged_containers` (array[object]): Facts about containers Portacode did not provision; fields mirror the managed list but are marked `managed=false`.
|
|
1159
|
+
* `reserve_on_boot` (boolean): `true` when the CT is configured to start at boot; this flag is used to decide if its RAM and CPU allocations count toward the available totals.
|
|
1159
1160
|
* `unmanaged_count` (integer): Number of unmanaged containers detected on the node.
|
|
1160
1161
|
* `allocated_ram_mib` (integer): Total RAM reserved by both managed and unmanaged containers.
|
|
1161
1162
|
* `allocated_disk_gib` (integer): Total disk reserved by both managed and unmanaged containers.
|
|
@@ -464,11 +464,43 @@ def _extract_host_totals(node_status: Dict[str, Any] | None) -> Tuple[Optional[i
|
|
|
464
464
|
return host_ram, host_disk, host_cpu
|
|
465
465
|
|
|
466
466
|
|
|
467
|
+
def _parse_disk_reference(value: Any) -> Tuple[str | None, int]:
|
|
468
|
+
if not value:
|
|
469
|
+
return None, 0
|
|
470
|
+
text = str(value)
|
|
471
|
+
primary = text.split(",", 1)[0]
|
|
472
|
+
if ":" not in primary:
|
|
473
|
+
return primary.strip() or None, 0
|
|
474
|
+
storage_name, size_part = primary.split(":", 1)
|
|
475
|
+
storage_name = storage_name.strip() or None
|
|
476
|
+
size_text = size_part.strip()
|
|
477
|
+
if not size_text:
|
|
478
|
+
return storage_name, 0
|
|
479
|
+
unit = size_text[-1].upper()
|
|
480
|
+
number = size_text[:-1] if unit in {"G", "M"} else size_text
|
|
481
|
+
try:
|
|
482
|
+
value_num = float(number)
|
|
483
|
+
except ValueError:
|
|
484
|
+
return storage_name, 0
|
|
485
|
+
if unit == "M":
|
|
486
|
+
gib = value_num / 1024.0
|
|
487
|
+
else:
|
|
488
|
+
gib = value_num
|
|
489
|
+
return storage_name, int(round(gib))
|
|
490
|
+
|
|
491
|
+
|
|
467
492
|
def _build_unmanaged_container_entry(
|
|
468
|
-
ct: Dict[str, Any],
|
|
493
|
+
ct: Dict[str, Any],
|
|
494
|
+
cfg: Dict[str, Any],
|
|
495
|
+
vmid: str,
|
|
496
|
+
default_storage: str | None,
|
|
497
|
+
*,
|
|
498
|
+
disk_gib_override: int | None = None,
|
|
499
|
+
storage_override: str | None = None,
|
|
500
|
+
entry_type: str = "lxc",
|
|
469
501
|
) -> Dict[str, Any]:
|
|
470
502
|
ram_mib = _to_int(cfg.get("memory")) or _bytes_to_mib(ct.get("maxmem"))
|
|
471
|
-
disk_gib = _bytes_to_gib(ct.get("maxdisk"))
|
|
503
|
+
disk_gib = disk_gib_override if disk_gib_override is not None else _bytes_to_gib(ct.get("maxdisk"))
|
|
472
504
|
cpu_share = _to_float(
|
|
473
505
|
cfg.get("cpulimit")
|
|
474
506
|
or cfg.get("cpus")
|
|
@@ -477,7 +509,7 @@ def _build_unmanaged_container_entry(
|
|
|
477
509
|
or ct.get("cpu")
|
|
478
510
|
)
|
|
479
511
|
hostname = ct.get("name") or cfg.get("hostname") or f"ct{vmid}"
|
|
480
|
-
storage = cfg.get("storage") or ct.get("storage")
|
|
512
|
+
storage = storage_override or cfg.get("storage") or ct.get("storage")
|
|
481
513
|
status = (ct.get("status") or "unknown").lower()
|
|
482
514
|
reserved = _parse_bool_flag(cfg.get("onboot"))
|
|
483
515
|
storage_matches_default = _storage_matches_default(storage, default_storage)
|
|
@@ -491,6 +523,7 @@ def _build_unmanaged_container_entry(
|
|
|
491
523
|
"cpu_share": cpu_share,
|
|
492
524
|
"reserve_on_boot": reserved,
|
|
493
525
|
"matches_default_storage": storage_matches_default,
|
|
526
|
+
"type": entry_type,
|
|
494
527
|
"status": status,
|
|
495
528
|
"managed": False,
|
|
496
529
|
}
|
|
@@ -515,6 +548,26 @@ def _get_storage_snapshot(proxmox: Any, node: str, storage_name: str | None) ->
|
|
|
515
548
|
}
|
|
516
549
|
|
|
517
550
|
|
|
551
|
+
def _extract_qemu_disk_info(cfg: Dict[str, Any]) -> Tuple[str | None, int]:
|
|
552
|
+
disk_keys = sorted(
|
|
553
|
+
key
|
|
554
|
+
for key in cfg.keys()
|
|
555
|
+
if (
|
|
556
|
+
key.startswith("ide")
|
|
557
|
+
or key.startswith("sata")
|
|
558
|
+
or key.startswith("scsi")
|
|
559
|
+
or key.startswith("virtio")
|
|
560
|
+
or key.startswith("efidisk")
|
|
561
|
+
or key.startswith("raid")
|
|
562
|
+
)
|
|
563
|
+
and cfg.get(key)
|
|
564
|
+
)
|
|
565
|
+
for key in disk_keys:
|
|
566
|
+
storage_name, size_gib = _parse_disk_reference(cfg.get(key))
|
|
567
|
+
if storage_name or size_gib:
|
|
568
|
+
return storage_name, size_gib
|
|
569
|
+
return None, 0
|
|
570
|
+
|
|
518
571
|
|
|
519
572
|
def _storage_matches_default(storage_name: Any, default_storage: str | None) -> bool:
|
|
520
573
|
if not default_storage:
|
|
@@ -567,8 +620,7 @@ def _build_managed_containers_summary(
|
|
|
567
620
|
unmanaged_total_disk = sum(
|
|
568
621
|
_to_int(entry.get("disk_gib"))
|
|
569
622
|
for entry in unmanaged_records
|
|
570
|
-
if entry.get("
|
|
571
|
-
and _storage_matches_default(entry.get("storage"), default_storage)
|
|
623
|
+
if entry.get("matches_default_storage")
|
|
572
624
|
)
|
|
573
625
|
unmanaged_total_cpu = sum(
|
|
574
626
|
_to_float(entry.get("cpu_share"))
|
|
@@ -644,7 +696,37 @@ def _get_managed_containers_summary(force: bool = False) -> Dict[str, Any]:
|
|
|
644
696
|
description = (cfg.get("description") or "")
|
|
645
697
|
if MANAGED_MARKER in description:
|
|
646
698
|
continue
|
|
647
|
-
unmanaged.append(
|
|
699
|
+
unmanaged.append(
|
|
700
|
+
_build_unmanaged_container_entry(ct, cfg, vmid_key, default_storage, entry_type="lxc")
|
|
701
|
+
)
|
|
702
|
+
for vm in proxmox.nodes(node).qemu.get():
|
|
703
|
+
vmid_val = vm.get("vmid")
|
|
704
|
+
if vmid_val is None:
|
|
705
|
+
continue
|
|
706
|
+
vmid_key = str(_to_int(vmid_val))
|
|
707
|
+
statuses[vmid_key] = (vm.get("status") or "unknown").lower()
|
|
708
|
+
if vmid_key in managed_vmids:
|
|
709
|
+
continue
|
|
710
|
+
cfg: Dict[str, Any] = {}
|
|
711
|
+
try:
|
|
712
|
+
cfg = proxmox.nodes(node).qemu(vmid_key).config.get() or {}
|
|
713
|
+
except Exception as exc:
|
|
714
|
+
logger.debug("Failed to read config for VM %s: %s", vmid_key, exc)
|
|
715
|
+
description = (cfg.get("description") or "")
|
|
716
|
+
if MANAGED_MARKER in description:
|
|
717
|
+
continue
|
|
718
|
+
storage_name, disk_gib_override = _extract_qemu_disk_info(cfg)
|
|
719
|
+
unmanaged.append(
|
|
720
|
+
_build_unmanaged_container_entry(
|
|
721
|
+
vm,
|
|
722
|
+
cfg,
|
|
723
|
+
vmid_key,
|
|
724
|
+
default_storage,
|
|
725
|
+
disk_gib_override=disk_gib_override,
|
|
726
|
+
storage_override=storage_name,
|
|
727
|
+
entry_type="qemu",
|
|
728
|
+
)
|
|
729
|
+
)
|
|
648
730
|
except Exception as exc: # pragma: no cover - best effort
|
|
649
731
|
logger.debug("Failed to refresh container statuses: %s", exc)
|
|
650
732
|
storage_snapshot = (
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
portacode/README.md,sha256=4dKtpvR8LNgZPVz37GmkQCMWIr_u25Ao63iW56s7Ke4,775
|
|
2
2
|
portacode/__init__.py,sha256=oB3sV1wXr-um-RXio73UG8E5Xx6cF2ZVJveqjNmC-vQ,1086
|
|
3
3
|
portacode/__main__.py,sha256=jmHTGC1hzmo9iKJLv-SSYe9BSIbPPZ2IOpecI03PlTs,296
|
|
4
|
-
portacode/_version.py,sha256=
|
|
4
|
+
portacode/_version.py,sha256=PjQCzQ14vbQAHflnIvm-t7BX8uT-4bUn2o-KdxjdxKU,719
|
|
5
5
|
portacode/cli.py,sha256=mGLKoZ-T2FBF7IA9wUq0zyG0X9__-A1ao7gajjcVRH8,21828
|
|
6
6
|
portacode/data.py,sha256=5-s291bv8J354myaHm1Y7CQZTZyRzMU3TGe5U4hb-FA,1591
|
|
7
7
|
portacode/keypair.py,sha256=0OO4vHDcF1XMxCDqce61xFTlFwlTcmqe5HyGsXFEt7s,5838
|
|
@@ -14,7 +14,7 @@ portacode/connection/client.py,sha256=jtLb9_YufqPkzi9t8VQH3iz_JEMisbtY6a8L9U5wei
|
|
|
14
14
|
portacode/connection/multiplex.py,sha256=L-TxqJ_ZEbfNEfu1cwxgJ5vUdyRzZjsMy2Kx1diiZys,5237
|
|
15
15
|
portacode/connection/terminal.py,sha256=n1Uu92JacV5K6d1Qwx94Tw9OB2Tpke5HqsW2NDn76Ls,49032
|
|
16
16
|
portacode/connection/handlers/README.md,sha256=HsLZG1QK1JNm67HsgL6WoDg9nxzKXxwkc5fJPFJdX5g,12169
|
|
17
|
-
portacode/connection/handlers/WEBSOCKET_PROTOCOL.md,sha256=
|
|
17
|
+
portacode/connection/handlers/WEBSOCKET_PROTOCOL.md,sha256=R9trHDWDuADIY2Xs2LrpIw1himtxE-NDb_6CzNVNVcM,103625
|
|
18
18
|
portacode/connection/handlers/__init__.py,sha256=WSeBmi65GWFQPYt9M3E10rn0uZ_EPCJzNJOzSf2HZyw,2921
|
|
19
19
|
portacode/connection/handlers/base.py,sha256=oENFb-Fcfzwk99Qx8gJQriEMiwSxwygwjOiuCH36hM4,10231
|
|
20
20
|
portacode/connection/handlers/chunked_content.py,sha256=h6hXRmxSeOgnIxoU8CkmvEf2Odv-ajPrpHIe_W3GKcA,9251
|
|
@@ -22,7 +22,7 @@ portacode/connection/handlers/diff_handlers.py,sha256=iYTIRCcpEQ03vIPKZCsMTE5aZb
|
|
|
22
22
|
portacode/connection/handlers/file_handlers.py,sha256=nAJH8nXnX07xxD28ngLpgIUzcTuRwZBNpEGEKdRqohw,39507
|
|
23
23
|
portacode/connection/handlers/project_aware_file_handlers.py,sha256=AqgMnDqX2893T2NsrvUSCwjN5VKj4Pb2TN0S_SuboOE,9803
|
|
24
24
|
portacode/connection/handlers/project_state_handlers.py,sha256=v6ZefGW9i7n1aZLq2jOGumJIjYb6aHlPI4m1jkYewm8,1686
|
|
25
|
-
portacode/connection/handlers/proxmox_infra.py,sha256=
|
|
25
|
+
portacode/connection/handlers/proxmox_infra.py,sha256=tWwoJeHjZaEw3_lF0VUCibN9ZfUjZM9cIQcOFA13108,83840
|
|
26
26
|
portacode/connection/handlers/registry.py,sha256=qXGE60sYEWg6ZtVQzFcZ5YI2XWR6lMgw4hAL9x5qR1I,6181
|
|
27
27
|
portacode/connection/handlers/session.py,sha256=uNGfiO_1B9-_yjJKkpvmbiJhIl6b-UXlT86UTfd6WYE,42219
|
|
28
28
|
portacode/connection/handlers/system_handlers.py,sha256=fr12QpOr_Z8KYGUU-AYrTQwRPAcrLK85hvj3SEq1Kw8,14757
|
|
@@ -65,7 +65,7 @@ portacode/utils/__init__.py,sha256=NgBlWTuNJESfIYJzP_3adI1yJQJR0XJLRpSdVNaBAN0,3
|
|
|
65
65
|
portacode/utils/diff_apply.py,sha256=4Oi7ft3VUCKmiUE4VM-OeqO7Gk6H7PF3WnN4WHXtjxI,15157
|
|
66
66
|
portacode/utils/diff_renderer.py,sha256=S76StnQ2DLfsz4Gg0m07UwPfRp8270PuzbNaQq-rmYk,13850
|
|
67
67
|
portacode/utils/ntp_clock.py,sha256=VqCnWCTehCufE43W23oB-WUdAZGeCcLxkmIOPwInYHc,2499
|
|
68
|
-
portacode-1.4.16.
|
|
68
|
+
portacode-1.4.16.dev7.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
|
|
69
69
|
test_modules/README.md,sha256=Do_agkm9WhSzueXjRAkV_xEj6Emy5zB3N3VKY5Roce8,9274
|
|
70
70
|
test_modules/__init__.py,sha256=1LcbHodIHsB0g-g4NGjSn6AMuCoGbymvXPYLOb6Z7F0,53
|
|
71
71
|
test_modules/test_device_online.py,sha256=QtYq0Dq9vME8Gp2O4fGSheqVf8LUtpsSKosXXk56gGM,1654
|
|
@@ -91,8 +91,8 @@ testing_framework/core/playwright_manager.py,sha256=Tw46qwxIhOFkS48C2IWIQHHNpEe-
|
|
|
91
91
|
testing_framework/core/runner.py,sha256=j2QwNJmAxVBmJvcbVS7DgPJUKPNzqfLmt_4NNdaKmZU,19297
|
|
92
92
|
testing_framework/core/shared_cli_manager.py,sha256=BESSNtyQb7BOlaOvZmm04T8Uezjms4KCBs2MzTxvzYQ,8790
|
|
93
93
|
testing_framework/core/test_discovery.py,sha256=2FZ9fJ8Dp5dloA-fkgXoJ_gCMC_nYPBnA3Hs2xlagzM,4928
|
|
94
|
-
portacode-1.4.16.
|
|
95
|
-
portacode-1.4.16.
|
|
96
|
-
portacode-1.4.16.
|
|
97
|
-
portacode-1.4.16.
|
|
98
|
-
portacode-1.4.16.
|
|
94
|
+
portacode-1.4.16.dev7.dist-info/METADATA,sha256=IlkUQF4Bly5OO3F51SZ5jaNilKIGAxoV-wX9vIvRkKI,13051
|
|
95
|
+
portacode-1.4.16.dev7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
portacode-1.4.16.dev7.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.16.dev7.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.16.dev7.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|