portacode 1.4.16.dev5__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 +5 -2
- portacode/connection/handlers/proxmox_infra.py +93 -7
- {portacode-1.4.16.dev5.dist-info → portacode-1.4.16.dev7.dist-info}/METADATA +1 -1
- {portacode-1.4.16.dev5.dist-info → portacode-1.4.16.dev7.dist-info}/RECORD +9 -9
- {portacode-1.4.16.dev5.dist-info → portacode-1.4.16.dev7.dist-info}/WHEEL +0 -0
- {portacode-1.4.16.dev5.dist-info → portacode-1.4.16.dev7.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.16.dev5.dist-info → portacode-1.4.16.dev7.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.16.dev5.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
|
|
@@ -1152,8 +1152,11 @@ Provides system information in response to a `system_info` action. Handled by [`
|
|
|
1152
1152
|
* `cpu_share` (number): vCPU-equivalent share requested at creation.
|
|
1153
1153
|
* `status` (string): Lowercase lifecycle status (e.g., `running`, `stopped`, `deleted`).
|
|
1154
1154
|
* `created_at` (string|null): ISO timestamp recorded when the CT was provisioned.
|
|
1155
|
-
|
|
1156
|
-
|
|
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.
|
|
1157
1160
|
* `unmanaged_count` (integer): Number of unmanaged containers detected on the node.
|
|
1158
1161
|
* `allocated_ram_mib` (integer): Total RAM reserved by both managed and unmanaged containers.
|
|
1159
1162
|
* `allocated_disk_gib` (integer): Total disk reserved by both managed and unmanaged containers.
|
|
@@ -464,9 +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
|
|
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
|
+
|
|
492
|
+
def _build_unmanaged_container_entry(
|
|
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",
|
|
501
|
+
) -> Dict[str, Any]:
|
|
468
502
|
ram_mib = _to_int(cfg.get("memory")) or _bytes_to_mib(ct.get("maxmem"))
|
|
469
|
-
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"))
|
|
470
504
|
cpu_share = _to_float(
|
|
471
505
|
cfg.get("cpulimit")
|
|
472
506
|
or cfg.get("cpus")
|
|
@@ -475,9 +509,10 @@ def _build_unmanaged_container_entry(ct: Dict[str, Any], cfg: Dict[str, Any], vm
|
|
|
475
509
|
or ct.get("cpu")
|
|
476
510
|
)
|
|
477
511
|
hostname = ct.get("name") or cfg.get("hostname") or f"ct{vmid}"
|
|
478
|
-
storage = cfg.get("storage") or ct.get("storage")
|
|
512
|
+
storage = storage_override or cfg.get("storage") or ct.get("storage")
|
|
479
513
|
status = (ct.get("status") or "unknown").lower()
|
|
480
514
|
reserved = _parse_bool_flag(cfg.get("onboot"))
|
|
515
|
+
storage_matches_default = _storage_matches_default(storage, default_storage)
|
|
481
516
|
return {
|
|
482
517
|
"vmid": vmid,
|
|
483
518
|
"hostname": hostname,
|
|
@@ -487,6 +522,8 @@ def _build_unmanaged_container_entry(ct: Dict[str, Any], cfg: Dict[str, Any], vm
|
|
|
487
522
|
"ram_mib": ram_mib,
|
|
488
523
|
"cpu_share": cpu_share,
|
|
489
524
|
"reserve_on_boot": reserved,
|
|
525
|
+
"matches_default_storage": storage_matches_default,
|
|
526
|
+
"type": entry_type,
|
|
490
527
|
"status": status,
|
|
491
528
|
"managed": False,
|
|
492
529
|
}
|
|
@@ -511,6 +548,26 @@ def _get_storage_snapshot(proxmox: Any, node: str, storage_name: str | None) ->
|
|
|
511
548
|
}
|
|
512
549
|
|
|
513
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
|
+
|
|
514
571
|
|
|
515
572
|
def _storage_matches_default(storage_name: Any, default_storage: str | None) -> bool:
|
|
516
573
|
if not default_storage:
|
|
@@ -551,7 +608,7 @@ def _build_managed_containers_summary(
|
|
|
551
608
|
"created_at": record.get("created_at"),
|
|
552
609
|
"status": status,
|
|
553
610
|
"managed": True,
|
|
554
|
-
"
|
|
611
|
+
"matches_default_storage": _storage_matches_default(record.get("storage"), default_storage),
|
|
555
612
|
}
|
|
556
613
|
)
|
|
557
614
|
|
|
@@ -563,8 +620,7 @@ def _build_managed_containers_summary(
|
|
|
563
620
|
unmanaged_total_disk = sum(
|
|
564
621
|
_to_int(entry.get("disk_gib"))
|
|
565
622
|
for entry in unmanaged_records
|
|
566
|
-
if entry.get("
|
|
567
|
-
and _storage_matches_default(entry.get("storage"), default_storage)
|
|
623
|
+
if entry.get("matches_default_storage")
|
|
568
624
|
)
|
|
569
625
|
unmanaged_total_cpu = sum(
|
|
570
626
|
_to_float(entry.get("cpu_share"))
|
|
@@ -640,7 +696,37 @@ def _get_managed_containers_summary(force: bool = False) -> Dict[str, Any]:
|
|
|
640
696
|
description = (cfg.get("description") or "")
|
|
641
697
|
if MANAGED_MARKER in description:
|
|
642
698
|
continue
|
|
643
|
-
|
|
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
|
+
)
|
|
644
730
|
except Exception as exc: # pragma: no cover - best effort
|
|
645
731
|
logger.debug("Failed to refresh container statuses: %s", exc)
|
|
646
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
|