portacode 1.4.16.dev11__py3-none-any.whl → 1.4.17.dev0__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/proxmox_infra.py +69 -16
- {portacode-1.4.16.dev11.dist-info → portacode-1.4.17.dev0.dist-info}/METADATA +1 -1
- {portacode-1.4.16.dev11.dist-info → portacode-1.4.17.dev0.dist-info}/RECORD +8 -8
- {portacode-1.4.16.dev11.dist-info → portacode-1.4.17.dev0.dist-info}/WHEEL +0 -0
- {portacode-1.4.16.dev11.dist-info → portacode-1.4.17.dev0.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.16.dev11.dist-info → portacode-1.4.17.dev0.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.16.dev11.dist-info → portacode-1.4.17.dev0.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.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4,
|
|
31
|
+
__version__ = version = '1.4.17.dev0'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 17, 'dev0')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -777,6 +777,57 @@ def _build_full_container_summary(records: List[Dict[str, Any]], config: Dict[st
|
|
|
777
777
|
return summary
|
|
778
778
|
|
|
779
779
|
|
|
780
|
+
def _compute_free_resources(summary: Dict[str, Any]) -> Dict[str, float]:
|
|
781
|
+
"""Return free resources using the same math as the dashboard."""
|
|
782
|
+
free_ram = None
|
|
783
|
+
free_disk = None
|
|
784
|
+
free_cpu = None
|
|
785
|
+
host_ram = summary.get("host_total_ram_mib")
|
|
786
|
+
alloc_ram = summary.get("allocated_ram_mib")
|
|
787
|
+
if host_ram is not None and alloc_ram is not None:
|
|
788
|
+
free_ram = max(float(host_ram) - float(alloc_ram), 0.0)
|
|
789
|
+
|
|
790
|
+
host_disk = summary.get("host_total_disk_gib")
|
|
791
|
+
alloc_disk = summary.get("allocated_disk_gib")
|
|
792
|
+
if host_disk is not None and alloc_disk is not None:
|
|
793
|
+
free_disk = max(float(host_disk) - float(alloc_disk), 0.0)
|
|
794
|
+
|
|
795
|
+
host_cpu = summary.get("host_total_cpu_cores")
|
|
796
|
+
alloc_cpu = summary.get("allocated_cpu_share")
|
|
797
|
+
if host_cpu is not None and alloc_cpu is not None:
|
|
798
|
+
free_cpu = max(float(host_cpu) - float(alloc_cpu), 0.0)
|
|
799
|
+
|
|
800
|
+
return {
|
|
801
|
+
"ram_mib": free_ram,
|
|
802
|
+
"disk_gib": free_disk,
|
|
803
|
+
"cpu_share": free_cpu,
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
|
|
807
|
+
def _assert_capacity_for_payload(payload: Dict[str, Any], summary: Dict[str, Any]) -> None:
|
|
808
|
+
"""Validate requested container resources against current free capacity."""
|
|
809
|
+
free = _compute_free_resources(summary)
|
|
810
|
+
shortages: List[str] = []
|
|
811
|
+
|
|
812
|
+
req_ram = float(payload.get("ram_mib", 0))
|
|
813
|
+
free_ram = free.get("ram_mib")
|
|
814
|
+
if free_ram is not None and req_ram > free_ram:
|
|
815
|
+
shortages.append(f"RAM (need {int(req_ram)} MiB, free {int(free_ram)} MiB)")
|
|
816
|
+
|
|
817
|
+
req_disk = float(payload.get("disk_gib", 0))
|
|
818
|
+
free_disk = free.get("disk_gib")
|
|
819
|
+
if free_disk is not None and req_disk > free_disk:
|
|
820
|
+
shortages.append(f"Disk (need {req_disk:.1f} GiB, free {free_disk:.1f} GiB)")
|
|
821
|
+
|
|
822
|
+
req_cpu = float(payload.get("cpus", 0))
|
|
823
|
+
free_cpu = free.get("cpu_share")
|
|
824
|
+
if free_cpu is not None and req_cpu > free_cpu:
|
|
825
|
+
shortages.append(f"CPU (need {req_cpu:.2f} vCPU, free {free_cpu:.2f} vCPU)")
|
|
826
|
+
|
|
827
|
+
if shortages:
|
|
828
|
+
raise RuntimeError(f"Insufficient resources: {', '.join(shortages)}")
|
|
829
|
+
|
|
830
|
+
|
|
780
831
|
def _get_managed_containers_summary(force: bool = False) -> Dict[str, Any]:
|
|
781
832
|
def _refresh_container_statuses(records: List[Dict[str, Any]], config: Dict[str, Any] | None) -> None:
|
|
782
833
|
if not records or not config:
|
|
@@ -1102,22 +1153,18 @@ def _start_container(proxmox: Any, node: str, vmid: int) -> Tuple[Dict[str, Any]
|
|
|
1102
1153
|
logger.info("Container %s already running (%ss)", vmid, uptime)
|
|
1103
1154
|
return status, 0.0
|
|
1104
1155
|
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
if mem_total_mb and used_mem_mb + target_mem_mb > mem_total_mb:
|
|
1118
|
-
raise RuntimeError("Not enough RAM to start this container safely.")
|
|
1119
|
-
if cores_total and used_cores + target_cores > cores_total:
|
|
1120
|
-
raise RuntimeError("Not enough CPU cores to start this container safely.")
|
|
1156
|
+
# Validate capacity using the same math as the dashboard.
|
|
1157
|
+
summary = _get_managed_containers_summary(force=True)
|
|
1158
|
+
cfg = proxmox.nodes(node).lxc(vmid).config.get()
|
|
1159
|
+
payload = {
|
|
1160
|
+
"ram_mib": _to_mib(cfg.get("memory")),
|
|
1161
|
+
"disk_gib": _pick_container_disk_gib("lxc", cfg, {"rootfs": cfg.get("rootfs")}),
|
|
1162
|
+
"cpus": _pick_container_cpu_share("lxc", cfg, {}),
|
|
1163
|
+
}
|
|
1164
|
+
try:
|
|
1165
|
+
_assert_capacity_for_payload(payload, summary)
|
|
1166
|
+
except RuntimeError as exc:
|
|
1167
|
+
raise RuntimeError(f"Not enough resources to start container {vmid}: {exc}") from exc
|
|
1121
1168
|
|
|
1122
1169
|
upid = proxmox.nodes(node).lxc(vmid).status.start.post()
|
|
1123
1170
|
return _wait_for_task(proxmox, node, upid)
|
|
@@ -1855,6 +1902,12 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1855
1902
|
payload["cores"] = int(max(math.ceil(payload["cpus"]), 1))
|
|
1856
1903
|
payload["memory"] = int(payload["ram_mib"])
|
|
1857
1904
|
payload["node"] = node
|
|
1905
|
+
# Validate against current free resources (same math as dashboard charts).
|
|
1906
|
+
summary = _get_managed_containers_summary(force=True)
|
|
1907
|
+
try:
|
|
1908
|
+
_assert_capacity_for_payload(payload, summary)
|
|
1909
|
+
except RuntimeError as exc:
|
|
1910
|
+
raise RuntimeError(f"Not enough resources to create the container safely: {exc}") from exc
|
|
1858
1911
|
logger.debug(
|
|
1859
1912
|
"Provisioning container node=%s template=%s ram=%s cpu=%s storage=%s",
|
|
1860
1913
|
node,
|
|
@@ -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=bQ-FAL2ble5i7ZHjLXfiacqJWiDcpjxBVD73svWzbjg,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
|
|
@@ -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=GjGB0BICYR5eI-tUYwg7p2qnBWvLu_UcMshP5XTTLr0,87397
|
|
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.
|
|
68
|
+
portacode-1.4.17.dev0.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.
|
|
95
|
-
portacode-1.4.
|
|
96
|
-
portacode-1.4.
|
|
97
|
-
portacode-1.4.
|
|
98
|
-
portacode-1.4.
|
|
94
|
+
portacode-1.4.17.dev0.dist-info/METADATA,sha256=F-YQ9c2OK7Ck8xlpNpfdypteq_TEfZMKpB-HGLVhllg,13051
|
|
95
|
+
portacode-1.4.17.dev0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
portacode-1.4.17.dev0.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.17.dev0.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.17.dev0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|