clonebox 1.1.19__tar.gz → 1.1.20__tar.gz
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.
Potentially problematic release.
This version of clonebox might be problematic. Click here for more details.
- {clonebox-1.1.19/src/clonebox.egg-info → clonebox-1.1.20}/PKG-INFO +1 -1
- {clonebox-1.1.19 → clonebox-1.1.20}/pyproject.toml +1 -1
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/cli.py +13 -1
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/validator.py +23 -3
- {clonebox-1.1.19 → clonebox-1.1.20/src/clonebox.egg-info}/PKG-INFO +1 -1
- {clonebox-1.1.19 → clonebox-1.1.20}/LICENSE +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/README.md +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/setup.cfg +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/__init__.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/__main__.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/audit.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/backends/libvirt_backend.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/backends/qemu_disk.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/backends/subprocess_runner.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/cloner.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/container.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/dashboard.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/detector.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/di.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/exporter.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/health/__init__.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/health/manager.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/health/models.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/health/probes.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/importer.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/interfaces/disk.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/interfaces/hypervisor.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/interfaces/network.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/interfaces/process.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/logging.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/models.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/monitor.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/orchestrator.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/p2p.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/plugins/__init__.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/plugins/base.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/plugins/manager.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/policies/__init__.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/policies/engine.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/policies/models.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/policies/validators.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/profiles.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/remote.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/resource_monitor.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/resources.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/rollback.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/secrets.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/snapshots/__init__.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/snapshots/manager.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/snapshots/models.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/templates/profiles/ml-dev.yaml +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox/templates/profiles/web-stack.yaml +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox.egg-info/SOURCES.txt +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox.egg-info/dependency_links.txt +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox.egg-info/entry_points.txt +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox.egg-info/requires.txt +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/src/clonebox.egg-info/top_level.txt +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_audit.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_cli.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_cli_new_commands.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_cloner.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_cloner_simple.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_container.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_coverage_additional.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_coverage_boost_final.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_dashboard_coverage.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_detector.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_models.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_network.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_orchestrator.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_plugins.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_profiles.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_remote.py +0 -0
- {clonebox-1.1.19 → clonebox-1.1.20}/tests/test_validator.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "clonebox"
|
|
7
|
-
version = "1.1.
|
|
7
|
+
version = "1.1.20"
|
|
8
8
|
description = "Clone your workstation environment to an isolated VM with selective apps, paths and services"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "Apache-2.0"}
|
|
@@ -1884,14 +1884,24 @@ def cmd_test(args):
|
|
|
1884
1884
|
# Give QEMU Guest Agent some time to come up (common during early boot)
|
|
1885
1885
|
qga_ready = _qga_ping(vm_name, conn_uri)
|
|
1886
1886
|
if not qga_ready:
|
|
1887
|
-
for
|
|
1887
|
+
console.print("[yellow]⏳ Waiting for QEMU Guest Agent (up to 60s)...[/]")
|
|
1888
|
+
qga_wait_start = time.time()
|
|
1889
|
+
for attempt in range(12): # ~60s
|
|
1888
1890
|
time.sleep(5)
|
|
1889
1891
|
qga_ready = _qga_ping(vm_name, conn_uri)
|
|
1892
|
+
elapsed = int(time.time() - qga_wait_start)
|
|
1890
1893
|
if qga_ready:
|
|
1894
|
+
console.print(f"[green]✅ QEMU Guest Agent connected after {elapsed}s[/]")
|
|
1891
1895
|
break
|
|
1896
|
+
if attempt % 2 == 1:
|
|
1897
|
+
console.print(f"[dim] ...still waiting ({elapsed}s elapsed)[/]")
|
|
1898
|
+
|
|
1899
|
+
if not qga_ready:
|
|
1900
|
+
console.print("[yellow]⚠️ QEMU Guest Agent still not connected[/]")
|
|
1892
1901
|
|
|
1893
1902
|
# Check cloud-init status immediately if QGA is ready
|
|
1894
1903
|
if qga_ready:
|
|
1904
|
+
console.print("[dim] Checking cloud-init status via QGA...[/]")
|
|
1895
1905
|
status = _qga_exec(
|
|
1896
1906
|
vm_name, conn_uri, "cloud-init status 2>/dev/null || true", timeout=15
|
|
1897
1907
|
)
|
|
@@ -2068,6 +2078,8 @@ def cmd_test(args):
|
|
|
2068
2078
|
|
|
2069
2079
|
# Run full validation if requested
|
|
2070
2080
|
if validate_all and state == "running":
|
|
2081
|
+
console.print("[bold cyan]🔎 Starting deep validation (--validate)[/]")
|
|
2082
|
+
console.print("[dim]This can take a few minutes on first boot (waiting for QGA/cloud-init, checking packages/services).[/]")
|
|
2071
2083
|
validator = VMValidator(
|
|
2072
2084
|
config,
|
|
2073
2085
|
vm_name,
|
|
@@ -266,12 +266,17 @@ class VMValidator:
|
|
|
266
266
|
self.console.print("[dim]No APT packages configured[/]")
|
|
267
267
|
return self.results["packages"]
|
|
268
268
|
|
|
269
|
+
total_pkgs = len(packages)
|
|
270
|
+
self.console.print(f"[dim]Checking {total_pkgs} packages via QGA...[/]")
|
|
271
|
+
|
|
269
272
|
pkg_table = Table(title="Package Validation", border_style="cyan")
|
|
270
273
|
pkg_table.add_column("Package", style="bold")
|
|
271
274
|
pkg_table.add_column("Status", justify="center")
|
|
272
275
|
pkg_table.add_column("Version", style="dim")
|
|
273
276
|
|
|
274
|
-
for package in packages:
|
|
277
|
+
for idx, package in enumerate(packages, 1):
|
|
278
|
+
if idx == 1 or idx % 25 == 0 or idx == total_pkgs:
|
|
279
|
+
self.console.print(f"[dim] ...packages progress: {idx}/{total_pkgs}[/]")
|
|
275
280
|
self.results["packages"]["total"] += 1
|
|
276
281
|
|
|
277
282
|
# Check if installed
|
|
@@ -315,12 +320,17 @@ class VMValidator:
|
|
|
315
320
|
self.console.print("[dim]No snap packages configured[/]")
|
|
316
321
|
return self.results["snap_packages"]
|
|
317
322
|
|
|
323
|
+
total_snaps = len(snap_packages)
|
|
324
|
+
self.console.print(f"[dim]Checking {total_snaps} snap packages via QGA...[/]")
|
|
325
|
+
|
|
318
326
|
snap_table = Table(title="Snap Package Validation", border_style="cyan")
|
|
319
327
|
snap_table.add_column("Package", style="bold")
|
|
320
328
|
snap_table.add_column("Status", justify="center")
|
|
321
329
|
snap_table.add_column("Version", style="dim")
|
|
322
330
|
|
|
323
|
-
for package in snap_packages:
|
|
331
|
+
for idx, package in enumerate(snap_packages, 1):
|
|
332
|
+
if idx == 1 or idx % 25 == 0 or idx == total_snaps:
|
|
333
|
+
self.console.print(f"[dim] ...snap progress: {idx}/{total_snaps}[/]")
|
|
324
334
|
self.results["snap_packages"]["total"] += 1
|
|
325
335
|
|
|
326
336
|
# Check if installed
|
|
@@ -395,6 +405,9 @@ class VMValidator:
|
|
|
395
405
|
self.console.print("[dim]No services configured[/]")
|
|
396
406
|
return self.results["services"]
|
|
397
407
|
|
|
408
|
+
total_svcs = len(services)
|
|
409
|
+
self.console.print(f"[dim]Checking {total_svcs} services via QGA...[/]")
|
|
410
|
+
|
|
398
411
|
if "skipped" not in self.results["services"]:
|
|
399
412
|
self.results["services"]["skipped"] = 0
|
|
400
413
|
|
|
@@ -405,7 +418,9 @@ class VMValidator:
|
|
|
405
418
|
svc_table.add_column("PID", justify="right", style="dim")
|
|
406
419
|
svc_table.add_column("Note", style="dim")
|
|
407
420
|
|
|
408
|
-
for service in services:
|
|
421
|
+
for idx, service in enumerate(services, 1):
|
|
422
|
+
if idx == 1 or idx % 25 == 0 or idx == total_svcs:
|
|
423
|
+
self.console.print(f"[dim] ...services progress: {idx}/{total_svcs}[/]")
|
|
409
424
|
if service in self.VM_EXCLUDED_SERVICES:
|
|
410
425
|
svc_table.add_row(service, "[dim]—[/]", "[dim]—[/]", "[dim]—[/]", "host-only")
|
|
411
426
|
self.results["services"]["skipped"] += 1
|
|
@@ -1144,10 +1159,15 @@ class VMValidator:
|
|
|
1144
1159
|
if not self._check_qga_ready():
|
|
1145
1160
|
wait_deadline = time.time() + 180
|
|
1146
1161
|
self.console.print("[yellow]⏳ Waiting for QEMU Guest Agent (up to 180s)...[/]")
|
|
1162
|
+
last_log = 0
|
|
1147
1163
|
while time.time() < wait_deadline:
|
|
1148
1164
|
time.sleep(5)
|
|
1149
1165
|
if self._check_qga_ready():
|
|
1150
1166
|
break
|
|
1167
|
+
elapsed = int(180 - (wait_deadline - time.time()))
|
|
1168
|
+
if elapsed - last_log >= 15:
|
|
1169
|
+
self.console.print(f"[dim] ...still waiting for QGA ({elapsed}s elapsed)[/]")
|
|
1170
|
+
last_log = elapsed
|
|
1151
1171
|
|
|
1152
1172
|
if not self._check_qga_ready():
|
|
1153
1173
|
self.console.print("[red]❌ QEMU Guest Agent not responding[/]")
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|