portacode 1.4.16.dev7__py3-none-any.whl → 1.4.16.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 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.dev7'
32
- __version_tuple__ = version_tuple = (1, 4, 16, 'dev7')
31
+ __version__ = version = '1.4.16.dev9'
32
+ __version_tuple__ = version_tuple = (1, 4, 16, 'dev9')
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -259,6 +259,153 @@ def _bytes_to_gib(value: Any) -> int:
259
259
  return int(round(_normalize_bytes(value) / (1024**3)))
260
260
 
261
261
 
262
+ def _extract_storage(value: Any) -> str:
263
+ if not value:
264
+ return "unknown"
265
+ text = str(value).strip()
266
+ if not text:
267
+ return "unknown"
268
+ primary = text.split(",", 1)[0]
269
+ storage = primary.split(":", 1)[0].strip()
270
+ return storage or "unknown"
271
+
272
+
273
+ def _size_token_to_gib(token: str) -> float:
274
+ match = re.match(r"^\s*([0-9]+(?:\.[0-9]+)?)\s*([KMGTP])?([iI]?[bB])?\s*$", token)
275
+ if not match:
276
+ return 0.0
277
+ number = float(match.group(1))
278
+ unit = (match.group(2) or "").upper()
279
+ scale = {
280
+ "": 1,
281
+ "K": 1024**1,
282
+ "M": 1024**2,
283
+ "G": 1024**3,
284
+ "T": 1024**4,
285
+ "P": 1024**5,
286
+ }.get(unit, 1)
287
+ return (number * scale) / 1024**3
288
+
289
+
290
+ def _extract_size_gib(value: Any) -> float:
291
+ if not value:
292
+ return 0.0
293
+ text = str(value)
294
+ for part in text.split(","):
295
+ if "size=" in part:
296
+ token = part.split("=", 1)[1]
297
+ return _size_token_to_gib(token)
298
+ return _size_token_to_gib(text)
299
+
300
+
301
+ def _storage_from_lxc(cfg: Dict[str, Any], entry: Dict[str, Any]) -> str:
302
+ rootfs = cfg.get("rootfs") or entry.get("rootfs")
303
+ storage = _extract_storage(rootfs)
304
+ if storage != "unknown":
305
+ return storage
306
+ for idx in range(0, 10):
307
+ mp_value = cfg.get(f"mp{idx}")
308
+ storage = _extract_storage(mp_value)
309
+ if storage != "unknown":
310
+ return storage
311
+ return "unknown"
312
+
313
+
314
+ def _storage_from_qemu(cfg: Dict[str, Any]) -> str:
315
+ preferred_keys = []
316
+ for prefix in ("scsi", "virtio", "sata", "ide"):
317
+ preferred_keys.extend(f"{prefix}{idx}" for idx in range(0, 6))
318
+ seen = set()
319
+ for key in preferred_keys:
320
+ value = cfg.get(key)
321
+ if value is None:
322
+ continue
323
+ seen.add(key)
324
+ text = str(value)
325
+ if "media=cdrom" in text or "cloudinit" in text:
326
+ continue
327
+ storage = _extract_storage(text)
328
+ if storage != "unknown":
329
+ return storage
330
+ for key in sorted(cfg.keys()):
331
+ if key in seen:
332
+ continue
333
+ if not any(key.startswith(prefix) for prefix in ("scsi", "virtio", "sata", "ide")):
334
+ continue
335
+ value = cfg.get(key)
336
+ if value is None:
337
+ continue
338
+ text = str(value)
339
+ if "media=cdrom" in text or "cloudinit" in text:
340
+ continue
341
+ storage = _extract_storage(text)
342
+ if storage != "unknown":
343
+ return storage
344
+ for key in ("efidisk0", "tpmstate0"):
345
+ storage = _extract_storage(cfg.get(key))
346
+ if storage != "unknown":
347
+ return storage
348
+ return "unknown"
349
+
350
+
351
+ def _primary_lxc_disk(cfg: Dict[str, Any], entry: Dict[str, Any]) -> str:
352
+ return str(cfg.get("rootfs") or entry.get("rootfs") or "")
353
+
354
+
355
+ def _primary_qemu_disk(cfg: Dict[str, Any]) -> str:
356
+ preferred_keys = []
357
+ for prefix in ("scsi", "virtio", "sata", "ide"):
358
+ preferred_keys.extend(f"{prefix}{idx}" for idx in range(0, 6))
359
+ seen = set()
360
+ for key in preferred_keys:
361
+ value = cfg.get(key)
362
+ if value is None:
363
+ continue
364
+ seen.add(key)
365
+ text = str(value)
366
+ if "media=cdrom" in text or "cloudinit" in text:
367
+ continue
368
+ return text
369
+ for key in sorted(cfg.keys()):
370
+ if key in seen:
371
+ continue
372
+ if not any(key.startswith(prefix) for prefix in ("scsi", "virtio", "sata", "ide")):
373
+ continue
374
+ value = cfg.get(key)
375
+ if value is None:
376
+ continue
377
+ text = str(value)
378
+ if "media=cdrom" in text or "cloudinit" in text:
379
+ continue
380
+ return text
381
+ return ""
382
+
383
+
384
+ def _pick_storage(kind: str, cfg: Dict[str, Any], entry: Dict[str, Any]) -> str:
385
+ storage = _extract_storage(cfg.get("storage") or entry.get("storage"))
386
+ if storage != "unknown":
387
+ return storage
388
+ if kind == "lxc":
389
+ return _storage_from_lxc(cfg, entry)
390
+ return _storage_from_qemu(cfg)
391
+
392
+
393
+ def _pick_disk_gib(kind: str, cfg: Dict[str, Any], entry: Dict[str, Any]) -> float:
394
+ if kind == "lxc":
395
+ size = _extract_size_gib(_primary_lxc_disk(cfg, entry))
396
+ if size:
397
+ return size
398
+ else:
399
+ size = _extract_size_gib(_primary_qemu_disk(cfg))
400
+ if size:
401
+ return size
402
+ for candidate in (entry.get("maxdisk"), entry.get("disk"), cfg.get("disk")):
403
+ if candidate in (None, 0):
404
+ continue
405
+ return _bytes_to_gib(candidate)
406
+ return 0.0
407
+
408
+
262
409
  def _normalize_storage_name(name: Any) -> str:
263
410
  if not name:
264
411
  return ""
@@ -500,7 +647,7 @@ def _build_unmanaged_container_entry(
500
647
  entry_type: str = "lxc",
501
648
  ) -> Dict[str, Any]:
502
649
  ram_mib = _to_int(cfg.get("memory")) or _bytes_to_mib(ct.get("maxmem"))
503
- disk_gib = disk_gib_override if disk_gib_override is not None else _bytes_to_gib(ct.get("maxdisk"))
650
+ disk_gib = disk_gib_override if disk_gib_override is not None else _pick_disk_gib(entry_type, cfg, ct)
504
651
  cpu_share = _to_float(
505
652
  cfg.get("cpulimit")
506
653
  or cfg.get("cpus")
@@ -509,7 +656,7 @@ def _build_unmanaged_container_entry(
509
656
  or ct.get("cpu")
510
657
  )
511
658
  hostname = ct.get("name") or cfg.get("hostname") or f"ct{vmid}"
512
- storage = storage_override or cfg.get("storage") or ct.get("storage")
659
+ storage = storage_override or _pick_storage(entry_type, cfg, ct)
513
660
  status = (ct.get("status") or "unknown").lower()
514
661
  reserved = _parse_bool_flag(cfg.get("onboot"))
515
662
  storage_matches_default = _storage_matches_default(storage, default_storage)
@@ -571,8 +718,10 @@ def _extract_qemu_disk_info(cfg: Dict[str, Any]) -> Tuple[str | None, int]:
571
718
 
572
719
  def _storage_matches_default(storage_name: Any, default_storage: str | None) -> bool:
573
720
  if not default_storage:
574
- return True
575
- return _normalize_storage_name(storage_name) == _normalize_storage_name(default_storage)
721
+ return False
722
+ storage = _extract_storage(storage_name)
723
+ default = str(default_storage).strip()
724
+ return storage.lower() == default.lower()
576
725
 
577
726
 
578
727
  def _build_managed_containers_summary(
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: portacode
3
- Version: 1.4.16.dev7
3
+ Version: 1.4.16.dev9
4
4
  Summary: Portacode CLI client and SDK
5
5
  Home-page: https://github.com/portacode/portacode
6
6
  Author: Meena Erian
@@ -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=PjQCzQ14vbQAHflnIvm-t7BX8uT-4bUn2o-KdxjdxKU,719
4
+ portacode/_version.py,sha256=MiVkLaI5JqUCo1IS8Z-ZuJxS2iDsMOn2RV8qQYxQ5kE,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=tWwoJeHjZaEw3_lF0VUCibN9ZfUjZM9cIQcOFA13108,83840
25
+ portacode/connection/handlers/proxmox_infra.py,sha256=CcPTFTM5TMJ9uX4-Ndrfao6Xx_8zdMMPlvze08SHWgU,88344
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.dev7.dist-info/licenses/LICENSE,sha256=2FGbCnUDgRYuQTkB1O1dUUpu5CVAjK1j4_p6ack9Z54,1066
68
+ portacode-1.4.16.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.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,,
94
+ portacode-1.4.16.dev9.dist-info/METADATA,sha256=IwtVePxyAtqCsSSOTisxusWv8gBIvL1DSCoT96cNDj0,13051
95
+ portacode-1.4.16.dev9.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
96
+ portacode-1.4.16.dev9.dist-info/entry_points.txt,sha256=lLUUL-BM6_wwe44Xv0__5NQ1BnAz6jWjSMFvZdWW3zU,48
97
+ portacode-1.4.16.dev9.dist-info/top_level.txt,sha256=TGhTYUxfW8SyVZc_zGgzjzc24gGT7nSw8Qf73liVRKM,41
98
+ portacode-1.4.16.dev9.dist-info/RECORD,,