portacode 1.4.17.dev1__py3-none-any.whl → 1.4.17.dev2__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 +58 -28
- {portacode-1.4.17.dev1.dist-info → portacode-1.4.17.dev2.dist-info}/METADATA +1 -1
- {portacode-1.4.17.dev1.dist-info → portacode-1.4.17.dev2.dist-info}/RECORD +8 -8
- {portacode-1.4.17.dev1.dist-info → portacode-1.4.17.dev2.dist-info}/WHEEL +0 -0
- {portacode-1.4.17.dev1.dist-info → portacode-1.4.17.dev2.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.17.dev1.dist-info → portacode-1.4.17.dev2.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.17.dev1.dist-info → portacode-1.4.17.dev2.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.17.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4, 17, '
|
|
31
|
+
__version__ = version = '1.4.17.dev2'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 17, 'dev2')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -49,6 +49,7 @@ _MANAGED_CONTAINERS_CACHE_TTL_S = 30.0
|
|
|
49
49
|
_MANAGED_CONTAINERS_CACHE: Dict[str, Any] = {"timestamp": 0.0, "summary": None}
|
|
50
50
|
_MANAGED_CONTAINERS_CACHE_LOCK = threading.Lock()
|
|
51
51
|
_CAPACITY_LOCK = threading.Lock()
|
|
52
|
+
_PENDING_ALLOCATIONS = {"ram_mib": 0.0, "disk_gib": 0.0, "cpu_share": 0.0}
|
|
52
53
|
TEMPLATES_REFRESH_INTERVAL_S = 300
|
|
53
54
|
|
|
54
55
|
ProgressCallback = Callable[[int, int, Dict[str, Any], str, Optional[Dict[str, Any]]], None]
|
|
@@ -721,6 +722,13 @@ def _build_full_container_summary(records: List[Dict[str, Any]], config: Dict[st
|
|
|
721
722
|
"template": record.get("template") if record else None,
|
|
722
723
|
"created_at": record.get("created_at") if record else None,
|
|
723
724
|
}
|
|
725
|
+
# Fallback to recorded specs if live probing returned zeros (e.g., just-created CT).
|
|
726
|
+
if not merged["ram_mib"] and record:
|
|
727
|
+
merged["ram_mib"] = record.get("ram_mib") or merged["ram_mib"]
|
|
728
|
+
if not merged["disk_gib"] and record:
|
|
729
|
+
merged["disk_gib"] = record.get("disk_gib") or merged["disk_gib"]
|
|
730
|
+
if not merged["cpu_share"] and record:
|
|
731
|
+
merged["cpu_share"] = record.get("cpus") or merged["cpu_share"]
|
|
724
732
|
managed_entries.append(merged)
|
|
725
733
|
else:
|
|
726
734
|
unmanaged_entries.append(base_entry)
|
|
@@ -783,20 +791,24 @@ def _compute_free_resources(summary: Dict[str, Any]) -> Dict[str, float]:
|
|
|
783
791
|
free_ram = None
|
|
784
792
|
free_disk = None
|
|
785
793
|
free_cpu = None
|
|
794
|
+
with _CAPACITY_LOCK:
|
|
795
|
+
pending_ram = float(_PENDING_ALLOCATIONS["ram_mib"])
|
|
796
|
+
pending_disk = float(_PENDING_ALLOCATIONS["disk_gib"])
|
|
797
|
+
pending_cpu = float(_PENDING_ALLOCATIONS["cpu_share"])
|
|
786
798
|
host_ram = summary.get("host_total_ram_mib")
|
|
787
799
|
alloc_ram = summary.get("allocated_ram_mib")
|
|
788
800
|
if host_ram is not None and alloc_ram is not None:
|
|
789
|
-
free_ram = max(float(host_ram) - float(alloc_ram), 0.0)
|
|
801
|
+
free_ram = max(float(host_ram) - float(alloc_ram) - pending_ram, 0.0)
|
|
790
802
|
|
|
791
803
|
host_disk = summary.get("host_total_disk_gib")
|
|
792
804
|
alloc_disk = summary.get("allocated_disk_gib")
|
|
793
805
|
if host_disk is not None and alloc_disk is not None:
|
|
794
|
-
free_disk = max(float(host_disk) - float(alloc_disk), 0.0)
|
|
806
|
+
free_disk = max(float(host_disk) - float(alloc_disk) - pending_disk, 0.0)
|
|
795
807
|
|
|
796
808
|
host_cpu = summary.get("host_total_cpu_cores")
|
|
797
809
|
alloc_cpu = summary.get("allocated_cpu_share")
|
|
798
810
|
if host_cpu is not None and alloc_cpu is not None:
|
|
799
|
-
free_cpu = max(float(host_cpu) - float(alloc_cpu), 0.0)
|
|
811
|
+
free_cpu = max(float(host_cpu) - float(alloc_cpu) - pending_cpu, 0.0)
|
|
800
812
|
|
|
801
813
|
return {
|
|
802
814
|
"ram_mib": free_ram,
|
|
@@ -1155,21 +1167,30 @@ def _start_container(proxmox: Any, node: str, vmid: int) -> Tuple[Dict[str, Any]
|
|
|
1155
1167
|
return status, 0.0
|
|
1156
1168
|
|
|
1157
1169
|
# Validate capacity using the same math as the dashboard and serialize allocation.
|
|
1170
|
+
summary = _get_managed_containers_summary(force=True)
|
|
1171
|
+
cfg = proxmox.nodes(node).lxc(vmid).config.get()
|
|
1172
|
+
payload = {
|
|
1173
|
+
"ram_mib": _to_mib(cfg.get("memory")),
|
|
1174
|
+
"disk_gib": _pick_container_disk_gib("lxc", cfg, {"rootfs": cfg.get("rootfs")}),
|
|
1175
|
+
"cpus": _pick_container_cpu_share("lxc", cfg, {}),
|
|
1176
|
+
}
|
|
1158
1177
|
with _CAPACITY_LOCK:
|
|
1159
|
-
summary = _get_managed_containers_summary(force=True)
|
|
1160
|
-
cfg = proxmox.nodes(node).lxc(vmid).config.get()
|
|
1161
|
-
payload = {
|
|
1162
|
-
"ram_mib": _to_mib(cfg.get("memory")),
|
|
1163
|
-
"disk_gib": _pick_container_disk_gib("lxc", cfg, {"rootfs": cfg.get("rootfs")}),
|
|
1164
|
-
"cpus": _pick_container_cpu_share("lxc", cfg, {}),
|
|
1165
|
-
}
|
|
1166
1178
|
try:
|
|
1167
1179
|
_assert_capacity_for_payload(payload, summary)
|
|
1168
1180
|
except RuntimeError as exc:
|
|
1169
1181
|
raise RuntimeError(f"Not enough resources to start container {vmid}: {exc}") from exc
|
|
1182
|
+
_PENDING_ALLOCATIONS["ram_mib"] += float(payload["ram_mib"] or 0)
|
|
1183
|
+
_PENDING_ALLOCATIONS["disk_gib"] += float(payload["disk_gib"] or 0)
|
|
1184
|
+
_PENDING_ALLOCATIONS["cpu_share"] += float(payload["cpus"] or 0)
|
|
1170
1185
|
|
|
1171
|
-
|
|
1172
|
-
|
|
1186
|
+
try:
|
|
1187
|
+
upid = proxmox.nodes(node).lxc(vmid).status.start.post()
|
|
1188
|
+
return _wait_for_task(proxmox, node, upid)
|
|
1189
|
+
finally:
|
|
1190
|
+
with _CAPACITY_LOCK:
|
|
1191
|
+
_PENDING_ALLOCATIONS["ram_mib"] -= float(payload["ram_mib"] or 0)
|
|
1192
|
+
_PENDING_ALLOCATIONS["disk_gib"] -= float(payload["disk_gib"] or 0)
|
|
1193
|
+
_PENDING_ALLOCATIONS["cpu_share"] -= float(payload["cpus"] or 0)
|
|
1173
1194
|
|
|
1174
1195
|
|
|
1175
1196
|
def _stop_container(proxmox: Any, node: str, vmid: int) -> Tuple[Dict[str, Any], float]:
|
|
@@ -1896,22 +1917,31 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1896
1917
|
_validate_environment,
|
|
1897
1918
|
)
|
|
1898
1919
|
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1920
|
+
def _create_container():
|
|
1921
|
+
proxmox = _connect_proxmox(config)
|
|
1922
|
+
node = config.get("node") or DEFAULT_NODE_NAME
|
|
1923
|
+
payload = _build_container_payload(message, config)
|
|
1924
|
+
payload["cpulimit"] = float(payload["cpus"])
|
|
1925
|
+
payload["cores"] = int(max(math.ceil(payload["cpus"]), 1))
|
|
1926
|
+
payload["memory"] = int(payload["ram_mib"])
|
|
1927
|
+
payload["node"] = node
|
|
1928
|
+
# Validate against current free resources (same math as dashboard charts) and place a short-lived reservation.
|
|
1929
|
+
summary = _get_managed_containers_summary(force=True)
|
|
1930
|
+
with _CAPACITY_LOCK:
|
|
1931
|
+
try:
|
|
1932
|
+
_assert_capacity_for_payload(payload, summary)
|
|
1933
|
+
except RuntimeError as exc:
|
|
1934
|
+
raise RuntimeError(f"Not enough resources to create the container safely: {exc}") from exc
|
|
1935
|
+
_PENDING_ALLOCATIONS["ram_mib"] += float(payload["ram_mib"] or 0)
|
|
1936
|
+
_PENDING_ALLOCATIONS["disk_gib"] += float(payload["disk_gib"] or 0)
|
|
1937
|
+
_PENDING_ALLOCATIONS["cpu_share"] += float(payload["cpus"] or 0)
|
|
1938
|
+
try:
|
|
1939
|
+
vmid, _ = _instantiate_container(proxmox, node, payload)
|
|
1940
|
+
finally:
|
|
1941
|
+
with _CAPACITY_LOCK:
|
|
1942
|
+
_PENDING_ALLOCATIONS["ram_mib"] -= float(payload["ram_mib"] or 0)
|
|
1943
|
+
_PENDING_ALLOCATIONS["disk_gib"] -= float(payload["disk_gib"] or 0)
|
|
1944
|
+
_PENDING_ALLOCATIONS["cpu_share"] -= float(payload["cpus"] or 0)
|
|
1915
1945
|
logger.debug(
|
|
1916
1946
|
"Provisioning container node=%s template=%s ram=%s cpu=%s storage=%s",
|
|
1917
1947
|
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=tY1iaGi0436VvMeNiABna_erno3lFewWo4mlsbvOmsg,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=_v-qj-7zupk6egmed1AN_6iEhpL2k5hVa0q6-iZyK2I,89330
|
|
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.17.
|
|
68
|
+
portacode-1.4.17.dev2.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.17.
|
|
95
|
-
portacode-1.4.17.
|
|
96
|
-
portacode-1.4.17.
|
|
97
|
-
portacode-1.4.17.
|
|
98
|
-
portacode-1.4.17.
|
|
94
|
+
portacode-1.4.17.dev2.dist-info/METADATA,sha256=dX2o0Oj7K-LojM-kwShhU7XEWqV-Lw30xCImcbfc-wU,13051
|
|
95
|
+
portacode-1.4.17.dev2.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
portacode-1.4.17.dev2.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.17.dev2.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.17.dev2.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|