portacode 1.4.15.dev12__py3-none-any.whl → 1.4.15.dev14__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 +75 -36
- {portacode-1.4.15.dev12.dist-info → portacode-1.4.15.dev14.dist-info}/METADATA +1 -1
- {portacode-1.4.15.dev12.dist-info → portacode-1.4.15.dev14.dist-info}/RECORD +8 -8
- {portacode-1.4.15.dev12.dist-info → portacode-1.4.15.dev14.dist-info}/WHEEL +0 -0
- {portacode-1.4.15.dev12.dist-info → portacode-1.4.15.dev14.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.15.dev12.dist-info → portacode-1.4.15.dev14.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.15.dev12.dist-info → portacode-1.4.15.dev14.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.15.
|
|
32
|
-
__version_tuple__ = version_tuple = (1, 4, 15, '
|
|
31
|
+
__version__ = version = '1.4.15.dev14'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 15, 'dev14')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -16,7 +16,7 @@ import sys
|
|
|
16
16
|
import tempfile
|
|
17
17
|
import time
|
|
18
18
|
import threading
|
|
19
|
-
from datetime import datetime
|
|
19
|
+
from datetime import datetime, timezone
|
|
20
20
|
from pathlib import Path
|
|
21
21
|
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple
|
|
22
22
|
|
|
@@ -47,7 +47,7 @@ UNIT_DIR = Path("/etc/systemd/system")
|
|
|
47
47
|
_MANAGED_CONTAINERS_CACHE_TTL_S = 30.0
|
|
48
48
|
_MANAGED_CONTAINERS_CACHE: Dict[str, Any] = {"timestamp": 0.0, "summary": None}
|
|
49
49
|
_MANAGED_CONTAINERS_CACHE_LOCK = threading.Lock()
|
|
50
|
-
|
|
50
|
+
TEMPLATES_REFRESH_INTERVAL_S = 300
|
|
51
51
|
|
|
52
52
|
ProgressCallback = Callable[[int, int, Dict[str, Any], str, Optional[Dict[str, Any]]], None]
|
|
53
53
|
|
|
@@ -197,10 +197,33 @@ def _build_proxmox_client_from_config(config: Dict[str, Any]):
|
|
|
197
197
|
)
|
|
198
198
|
|
|
199
199
|
|
|
200
|
+
def _current_time_iso() -> str:
|
|
201
|
+
return datetime.now(timezone.utc).isoformat()
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def _parse_iso_timestamp(value: str) -> Optional[datetime]:
|
|
205
|
+
if not value:
|
|
206
|
+
return None
|
|
207
|
+
text = value
|
|
208
|
+
if text.endswith("Z"):
|
|
209
|
+
text = text[:-1] + "+00:00"
|
|
210
|
+
try:
|
|
211
|
+
return datetime.fromisoformat(text)
|
|
212
|
+
except ValueError:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _templates_need_refresh(config: Dict[str, Any]) -> bool:
|
|
217
|
+
if not config or not config.get("token_value"):
|
|
218
|
+
return False
|
|
219
|
+
last = _parse_iso_timestamp(config.get("templates_last_refreshed") or "")
|
|
220
|
+
if not last:
|
|
221
|
+
return True
|
|
222
|
+
return (datetime.now(timezone.utc) - last).total_seconds() >= TEMPLATES_REFRESH_INTERVAL_S
|
|
223
|
+
|
|
224
|
+
|
|
200
225
|
def _ensure_templates_refreshed_on_startup(config: Dict[str, Any]) -> None:
|
|
201
|
-
|
|
202
|
-
if _STARTUP_TEMPLATES_REFRESHED or not config or not config.get("token_value"):
|
|
203
|
-
_STARTUP_TEMPLATES_REFRESHED = True
|
|
226
|
+
if not _templates_need_refresh(config):
|
|
204
227
|
return
|
|
205
228
|
try:
|
|
206
229
|
client = _build_proxmox_client_from_config(config)
|
|
@@ -209,10 +232,10 @@ def _ensure_templates_refreshed_on_startup(config: Dict[str, Any]) -> None:
|
|
|
209
232
|
templates = _list_templates(client, node, storages)
|
|
210
233
|
if templates:
|
|
211
234
|
config["templates"] = templates
|
|
235
|
+
config["templates_last_refreshed"] = _current_time_iso()
|
|
236
|
+
_save_config(config)
|
|
212
237
|
except Exception as exc:
|
|
213
238
|
logger.warning("Unable to refresh Proxmox templates on startup: %s", exc)
|
|
214
|
-
finally:
|
|
215
|
-
_STARTUP_TEMPLATES_REFRESHED = True
|
|
216
239
|
|
|
217
240
|
|
|
218
241
|
def _pick_storage(storages: Iterable[Dict[str, Any]]) -> str:
|
|
@@ -1123,35 +1146,50 @@ def _bootstrap_portacode(
|
|
|
1123
1146
|
ssh_key,
|
|
1124
1147
|
package_manager=detected_manager,
|
|
1125
1148
|
)
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1149
|
+
results, ok = _run_setup_steps(
|
|
1150
|
+
vmid,
|
|
1151
|
+
actual_steps,
|
|
1152
|
+
user,
|
|
1153
|
+
progress_callback=progress_callback,
|
|
1154
|
+
start_index=start_index,
|
|
1155
|
+
total_steps=total_steps,
|
|
1156
|
+
)
|
|
1157
|
+
if not ok:
|
|
1158
|
+
details = results[-1] if results else {}
|
|
1159
|
+
summary = details.get("error_summary") or details.get("stderr") or details.get("stdout") or details.get("name")
|
|
1160
|
+
history = details.get("history")
|
|
1161
|
+
history_snippet = ""
|
|
1162
|
+
if isinstance(history, list) and history:
|
|
1163
|
+
history_snippet = f" history={history[-3:]}"
|
|
1164
|
+
command = details.get("cmd")
|
|
1165
|
+
command_text = ""
|
|
1166
|
+
if command:
|
|
1167
|
+
if isinstance(command, (list, tuple)):
|
|
1168
|
+
command_text = shlex.join(str(entry) for entry in command)
|
|
1169
|
+
else:
|
|
1170
|
+
command_text = str(command)
|
|
1171
|
+
command_suffix = f" command={command_text}" if command_text else ""
|
|
1172
|
+
stdout = details.get("stdout")
|
|
1173
|
+
stderr = details.get("stderr")
|
|
1174
|
+
if stdout or stderr:
|
|
1175
|
+
logger.debug(
|
|
1176
|
+
"Bootstrap command output%s%s%s",
|
|
1177
|
+
f" stdout={stdout!r}" if stdout else "",
|
|
1178
|
+
" " if stdout and stderr else "",
|
|
1179
|
+
f"stderr={stderr!r}" if stderr else "",
|
|
1180
|
+
)
|
|
1181
|
+
if summary:
|
|
1182
|
+
logger.warning(
|
|
1183
|
+
"Portacode bootstrap failure summary=%s%s%s",
|
|
1184
|
+
summary,
|
|
1185
|
+
f" history_len={len(history)}" if history else "",
|
|
1186
|
+
f" command={command_text}" if command_text else "",
|
|
1187
|
+
)
|
|
1188
|
+
logger.error(
|
|
1189
|
+
"Portacode bootstrap command failed%s%s%s",
|
|
1154
1190
|
f" command={command_text}" if command_text else "",
|
|
1191
|
+
f" stdout={stdout!r}" if stdout else "",
|
|
1192
|
+
f" stderr={stderr!r}" if stderr else "",
|
|
1155
1193
|
)
|
|
1156
1194
|
raise RuntimeError(
|
|
1157
1195
|
f"Portacode bootstrap steps failed: {summary}{history_snippet}{command_suffix}"
|
|
@@ -1223,8 +1261,9 @@ def configure_infrastructure(token_identifier: str, token_value: str, verify_ssl
|
|
|
1223
1261
|
"token_value": token_value,
|
|
1224
1262
|
"verify_ssl": verify_ssl,
|
|
1225
1263
|
"default_storage": default_storage,
|
|
1226
|
-
"templates": templates,
|
|
1227
1264
|
"last_verified": datetime.utcnow().isoformat() + "Z",
|
|
1265
|
+
"templates": templates,
|
|
1266
|
+
"templates_last_refreshed": _current_time_iso(),
|
|
1228
1267
|
"network": network,
|
|
1229
1268
|
"node_status": status,
|
|
1230
1269
|
}
|
|
@@ -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=7sZ3dV4aYpvI1I9brTQWYWiK506aqN6dWnk5AodwRfY,721
|
|
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=AMaNY1X3-QydOipj2o6N4io-ITwKnogFeTXABoZ6E4E,69551
|
|
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.15.
|
|
68
|
+
portacode-1.4.15.dev14.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.15.
|
|
95
|
-
portacode-1.4.15.
|
|
96
|
-
portacode-1.4.15.
|
|
97
|
-
portacode-1.4.15.
|
|
98
|
-
portacode-1.4.15.
|
|
94
|
+
portacode-1.4.15.dev14.dist-info/METADATA,sha256=JluPzf6YcZruBqEn8aKqfD8u9eY5GqaAn2ZuAU8pLRQ,13052
|
|
95
|
+
portacode-1.4.15.dev14.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
portacode-1.4.15.dev14.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.15.dev14.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.15.dev14.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|