portacode 1.4.15.dev7__py3-none-any.whl → 1.4.15.dev9__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 +96 -28
- {portacode-1.4.15.dev7.dist-info → portacode-1.4.15.dev9.dist-info}/METADATA +1 -1
- {portacode-1.4.15.dev7.dist-info → portacode-1.4.15.dev9.dist-info}/RECORD +8 -8
- {portacode-1.4.15.dev7.dist-info → portacode-1.4.15.dev9.dist-info}/WHEEL +0 -0
- {portacode-1.4.15.dev7.dist-info → portacode-1.4.15.dev9.dist-info}/entry_points.txt +0 -0
- {portacode-1.4.15.dev7.dist-info → portacode-1.4.15.dev9.dist-info}/licenses/LICENSE +0 -0
- {portacode-1.4.15.dev7.dist-info → portacode-1.4.15.dev9.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.dev9'
|
|
32
|
+
__version_tuple__ = version_tuple = (1, 4, 15, 'dev9')
|
|
33
33
|
|
|
34
34
|
__commit_id__ = commit_id = None
|
|
@@ -47,6 +47,9 @@ 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
|
+
_STARTUP_TEMPLATES: List[str] | None = None
|
|
51
|
+
_STARTUP_TEMPLATE_KEY: tuple[Any, ...] | None = None
|
|
52
|
+
_STARTUP_TEMPLATES_LOCK = threading.Lock()
|
|
50
53
|
|
|
51
54
|
ProgressCallback = Callable[[int, int, Dict[str, Any], str, Optional[Dict[str, Any]]], None]
|
|
52
55
|
|
|
@@ -179,6 +182,70 @@ def _list_templates(client: Any, node: str, storages: Iterable[Dict[str, Any]])
|
|
|
179
182
|
return templates
|
|
180
183
|
|
|
181
184
|
|
|
185
|
+
def _template_key(config: Dict[str, Any]) -> tuple[Any, ...]:
|
|
186
|
+
return (
|
|
187
|
+
config.get("host"),
|
|
188
|
+
config.get("node"),
|
|
189
|
+
config.get("user"),
|
|
190
|
+
config.get("token_name"),
|
|
191
|
+
config.get("verify_ssl"),
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _build_proxmox_client_from_config(config: Dict[str, Any]):
|
|
196
|
+
user = config.get("user")
|
|
197
|
+
token_name = config.get("token_name")
|
|
198
|
+
token_value = config.get("token_value")
|
|
199
|
+
if not user or not token_name or not token_value:
|
|
200
|
+
raise RuntimeError("Proxmox API credentials are missing")
|
|
201
|
+
ProxmoxAPI = _ensure_proxmoxer()
|
|
202
|
+
return ProxmoxAPI(
|
|
203
|
+
config.get("host", DEFAULT_HOST),
|
|
204
|
+
user=user,
|
|
205
|
+
token_name=token_name,
|
|
206
|
+
token_value=token_value,
|
|
207
|
+
verify_ssl=config.get("verify_ssl", False),
|
|
208
|
+
timeout=30,
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def _ensure_startup_templates(config: Dict[str, Any]) -> None:
|
|
213
|
+
global _STARTUP_TEMPLATES, _STARTUP_TEMPLATE_KEY
|
|
214
|
+
if not config:
|
|
215
|
+
with _STARTUP_TEMPLATES_LOCK:
|
|
216
|
+
_STARTUP_TEMPLATES = []
|
|
217
|
+
_STARTUP_TEMPLATE_KEY = None
|
|
218
|
+
return
|
|
219
|
+
with _STARTUP_TEMPLATES_LOCK:
|
|
220
|
+
if _STARTUP_TEMPLATE_KEY == _template_key(config) and _STARTUP_TEMPLATES is not None:
|
|
221
|
+
return
|
|
222
|
+
key = _template_key(config)
|
|
223
|
+
templates: List[str] = []
|
|
224
|
+
try:
|
|
225
|
+
client = _build_proxmox_client_from_config(config)
|
|
226
|
+
node = config.get("node") or _pick_node(client)
|
|
227
|
+
storages = client.nodes(node).storage.get()
|
|
228
|
+
templates = _list_templates(client, node, storages)
|
|
229
|
+
except Exception as exc:
|
|
230
|
+
logger.warning("Unable to refresh Proxmox templates: %s", exc)
|
|
231
|
+
with _STARTUP_TEMPLATES_LOCK:
|
|
232
|
+
_STARTUP_TEMPLATES = list(templates)
|
|
233
|
+
_STARTUP_TEMPLATE_KEY = key
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def _get_startup_templates(config: Dict[str, Any]) -> List[str]:
|
|
237
|
+
_ensure_startup_templates(config)
|
|
238
|
+
with _STARTUP_TEMPLATES_LOCK:
|
|
239
|
+
return list(_STARTUP_TEMPLATES or [])
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def _clear_startup_templates() -> None:
|
|
243
|
+
global _STARTUP_TEMPLATES, _STARTUP_TEMPLATE_KEY
|
|
244
|
+
with _STARTUP_TEMPLATES_LOCK:
|
|
245
|
+
_STARTUP_TEMPLATES = None
|
|
246
|
+
_STARTUP_TEMPLATE_KEY = None
|
|
247
|
+
|
|
248
|
+
|
|
182
249
|
def _pick_storage(storages: Iterable[Dict[str, Any]]) -> str:
|
|
183
250
|
candidates = [s for s in storages if "rootdir" in s.get("content", "") and s.get("avail", 0) > 0]
|
|
184
251
|
if not candidates:
|
|
@@ -561,21 +628,24 @@ def _build_bootstrap_steps(
|
|
|
561
628
|
return steps
|
|
562
629
|
|
|
563
630
|
|
|
564
|
-
def
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
631
|
+
def _guess_package_manager_from_template(template: str) -> str:
|
|
632
|
+
normalized = (template or "").lower()
|
|
633
|
+
if "alpine" in normalized:
|
|
634
|
+
return "apk"
|
|
635
|
+
if "archlinux" in normalized:
|
|
636
|
+
return "pacman"
|
|
637
|
+
if "centos-7" in normalized:
|
|
638
|
+
return "yum"
|
|
639
|
+
if any(keyword in normalized for keyword in ("centos-8", "centos-9", "centos-9-stream", "centos-8-stream")):
|
|
640
|
+
return "dnf"
|
|
641
|
+
if any(keyword in normalized for keyword in ("rockylinux", "almalinux", "fedora")):
|
|
642
|
+
return "dnf"
|
|
643
|
+
if "opensuse" in normalized or "suse" in normalized:
|
|
644
|
+
return "zypper"
|
|
645
|
+
if any(keyword in normalized for keyword in ("debian", "ubuntu", "devuan", "turnkeylinux")):
|
|
646
|
+
return "apt"
|
|
647
|
+
if normalized.startswith("system/") and "linux" in normalized:
|
|
648
|
+
return "apt"
|
|
579
649
|
return "apt"
|
|
580
650
|
|
|
581
651
|
|
|
@@ -686,7 +756,7 @@ def _remove_container_record(vmid: int) -> None:
|
|
|
686
756
|
|
|
687
757
|
|
|
688
758
|
def _build_container_payload(message: Dict[str, Any], config: Dict[str, Any]) -> Dict[str, Any]:
|
|
689
|
-
templates = config
|
|
759
|
+
templates = _get_startup_templates(config)
|
|
690
760
|
default_template = templates[0] if templates else ""
|
|
691
761
|
template = message.get("template") or default_template
|
|
692
762
|
if not template:
|
|
@@ -1056,16 +1126,7 @@ def _bootstrap_portacode(
|
|
|
1056
1126
|
total_steps: Optional[int] = None,
|
|
1057
1127
|
default_public_key: Optional[str] = None,
|
|
1058
1128
|
) -> Tuple[str, List[Dict[str, Any]]]:
|
|
1059
|
-
if steps is not None
|
|
1060
|
-
actual_steps = steps
|
|
1061
|
-
else:
|
|
1062
|
-
package_manager = _detect_package_manager(vmid)
|
|
1063
|
-
actual_steps = _build_bootstrap_steps(
|
|
1064
|
-
user,
|
|
1065
|
-
password,
|
|
1066
|
-
ssh_key,
|
|
1067
|
-
package_manager=package_manager,
|
|
1068
|
-
)
|
|
1129
|
+
actual_steps = steps if steps is not None else _build_bootstrap_steps(user, password, ssh_key)
|
|
1069
1130
|
results, ok = _run_setup_steps(
|
|
1070
1131
|
vmid,
|
|
1071
1132
|
actual_steps,
|
|
@@ -1123,13 +1184,14 @@ def build_snapshot(config: Dict[str, Any]) -> Dict[str, Any]:
|
|
|
1123
1184
|
"user": config.get("user"),
|
|
1124
1185
|
"token_name": config.get("token_name"),
|
|
1125
1186
|
"default_storage": config.get("default_storage"),
|
|
1126
|
-
"templates": config
|
|
1187
|
+
"templates": _get_startup_templates(config),
|
|
1127
1188
|
"last_verified": config.get("last_verified"),
|
|
1128
1189
|
"network": base_network,
|
|
1129
1190
|
}
|
|
1130
1191
|
|
|
1131
1192
|
|
|
1132
1193
|
def configure_infrastructure(token_identifier: str, token_value: str, verify_ssl: bool = False) -> Dict[str, Any]:
|
|
1194
|
+
_clear_startup_templates()
|
|
1133
1195
|
ProxmoxAPI = _ensure_proxmoxer()
|
|
1134
1196
|
user, token_name = _parse_token(token_identifier)
|
|
1135
1197
|
client = ProxmoxAPI(
|
|
@@ -1165,12 +1227,12 @@ def configure_infrastructure(token_identifier: str, token_value: str, verify_ssl
|
|
|
1165
1227
|
"token_value": token_value,
|
|
1166
1228
|
"verify_ssl": verify_ssl,
|
|
1167
1229
|
"default_storage": default_storage,
|
|
1168
|
-
"templates": templates,
|
|
1169
1230
|
"last_verified": datetime.utcnow().isoformat() + "Z",
|
|
1170
1231
|
"network": network,
|
|
1171
1232
|
"node_status": status,
|
|
1172
1233
|
}
|
|
1173
1234
|
_save_config(config)
|
|
1235
|
+
_ensure_startup_templates(config)
|
|
1174
1236
|
snapshot = build_snapshot(config)
|
|
1175
1237
|
snapshot["node_status"] = status
|
|
1176
1238
|
snapshot["managed_containers"] = _get_managed_containers_summary(force=True)
|
|
@@ -1190,6 +1252,7 @@ def revert_infrastructure() -> Dict[str, Any]:
|
|
|
1190
1252
|
_revert_bridge()
|
|
1191
1253
|
if CONFIG_PATH.exists():
|
|
1192
1254
|
CONFIG_PATH.unlink()
|
|
1255
|
+
_clear_startup_templates()
|
|
1193
1256
|
snapshot = build_snapshot({})
|
|
1194
1257
|
snapshot["network"] = snapshot.get("network", {})
|
|
1195
1258
|
snapshot["network"]["applied"] = False
|
|
@@ -1250,12 +1313,17 @@ class CreateProxmoxContainerHandler(SyncHandler):
|
|
|
1250
1313
|
device_public_key = (message.get("device_public_key") or "").strip()
|
|
1251
1314
|
device_private_key = (message.get("device_private_key") or "").strip()
|
|
1252
1315
|
has_device_keypair = bool(device_public_key and device_private_key)
|
|
1316
|
+
config_guess = _load_config()
|
|
1317
|
+
template_candidates = _get_startup_templates(config_guess)
|
|
1318
|
+
template_hint = (message.get("template") or (template_candidates[0] if template_candidates else "")).strip()
|
|
1319
|
+
package_manager = _guess_package_manager_from_template(template_hint)
|
|
1253
1320
|
bootstrap_user, bootstrap_password, bootstrap_ssh_key = _get_provisioning_user_info(message)
|
|
1254
1321
|
bootstrap_steps = _build_bootstrap_steps(
|
|
1255
1322
|
bootstrap_user,
|
|
1256
1323
|
bootstrap_password,
|
|
1257
1324
|
bootstrap_ssh_key,
|
|
1258
1325
|
include_portacode_connect=not has_device_keypair,
|
|
1326
|
+
package_manager=package_manager,
|
|
1259
1327
|
)
|
|
1260
1328
|
total_steps = 3 + len(bootstrap_steps) + 2
|
|
1261
1329
|
current_step_index = 1
|
|
@@ -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=YbH9Im7cDkYWkWFiH-JGJ5BzJGjRVMg1TyCC8DAh1fE,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=g3AXQ_SQ5-WDH0ixDMtowqPtod6ruTrPd7_6FkMp1ts,68206
|
|
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.dev9.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.dev9.dist-info/METADATA,sha256=6lnCia50lWblHLbiGKsxzaOVohX8yzayZgrAB8Hz-CY,13051
|
|
95
|
+
portacode-1.4.15.dev9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
96
|
+
portacode-1.4.15.dev9.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
|
|
97
|
+
portacode-1.4.15.dev9.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
|
|
98
|
+
portacode-1.4.15.dev9.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|