clonebox 1.1.5__tar.gz → 1.1.6__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.
Files changed (59) hide show
  1. {clonebox-1.1.5/src/clonebox.egg-info → clonebox-1.1.6}/PKG-INFO +2 -1
  2. {clonebox-1.1.5 → clonebox-1.1.6}/pyproject.toml +2 -1
  3. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/cli.py +18 -15
  4. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/models.py +2 -2
  5. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/validator.py +34 -0
  6. {clonebox-1.1.5 → clonebox-1.1.6/src/clonebox.egg-info}/PKG-INFO +2 -1
  7. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox.egg-info/requires.txt +1 -0
  8. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_models.py +3 -3
  9. {clonebox-1.1.5 → clonebox-1.1.6}/LICENSE +0 -0
  10. {clonebox-1.1.5 → clonebox-1.1.6}/README.md +0 -0
  11. {clonebox-1.1.5 → clonebox-1.1.6}/setup.cfg +0 -0
  12. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/__init__.py +0 -0
  13. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/__main__.py +0 -0
  14. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/backends/libvirt_backend.py +0 -0
  15. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/backends/qemu_disk.py +0 -0
  16. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/backends/subprocess_runner.py +0 -0
  17. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/cloner.py +0 -0
  18. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/container.py +0 -0
  19. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/dashboard.py +0 -0
  20. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/detector.py +0 -0
  21. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/di.py +0 -0
  22. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/exporter.py +0 -0
  23. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/health/__init__.py +0 -0
  24. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/health/manager.py +0 -0
  25. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/health/models.py +0 -0
  26. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/health/probes.py +0 -0
  27. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/importer.py +0 -0
  28. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/interfaces/disk.py +0 -0
  29. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/interfaces/hypervisor.py +0 -0
  30. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/interfaces/network.py +0 -0
  31. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/interfaces/process.py +0 -0
  32. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/logging.py +0 -0
  33. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/monitor.py +0 -0
  34. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/p2p.py +0 -0
  35. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/profiles.py +0 -0
  36. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/resource_monitor.py +0 -0
  37. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/resources.py +0 -0
  38. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/rollback.py +0 -0
  39. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/secrets.py +0 -0
  40. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/snapshots/__init__.py +0 -0
  41. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/snapshots/manager.py +0 -0
  42. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/snapshots/models.py +0 -0
  43. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/templates/profiles/ml-dev.yaml +0 -0
  44. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox/templates/profiles/web-stack.yaml +0 -0
  45. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox.egg-info/SOURCES.txt +0 -0
  46. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox.egg-info/dependency_links.txt +0 -0
  47. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox.egg-info/entry_points.txt +0 -0
  48. {clonebox-1.1.5 → clonebox-1.1.6}/src/clonebox.egg-info/top_level.txt +0 -0
  49. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_cli.py +0 -0
  50. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_cloner.py +0 -0
  51. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_cloner_simple.py +0 -0
  52. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_container.py +0 -0
  53. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_coverage_additional.py +0 -0
  54. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_coverage_boost_final.py +0 -0
  55. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_dashboard_coverage.py +0 -0
  56. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_detector.py +0 -0
  57. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_network.py +0 -0
  58. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_profiles.py +0 -0
  59. {clonebox-1.1.5 → clonebox-1.1.6}/tests/test_validator.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 1.1.5
3
+ Version: 1.1.6
4
4
  Summary: Clone your workstation environment to an isolated VM with selective apps, paths and services
5
5
  Author: CloneBox Team
6
6
  License: Apache-2.0
@@ -33,6 +33,7 @@ Requires-Dist: pyyaml>=6.0
33
33
  Requires-Dist: pydantic>=2.0.0
34
34
  Requires-Dist: python-dotenv>=1.0.0
35
35
  Requires-Dist: cryptography>=42.0.0
36
+ Requires-Dist: structlog>=24.0.0
36
37
  Provides-Extra: dev
37
38
  Requires-Dist: pytest>=7.0.0; extra == "dev"
38
39
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "clonebox"
7
- version = "1.1.5"
7
+ version = "1.1.6"
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"}
@@ -39,6 +39,7 @@ dependencies = [
39
39
  "pydantic>=2.0.0",
40
40
  "python-dotenv>=1.0.0",
41
41
  "cryptography>=42.0.0",
42
+ "structlog>=24.0.0",
42
43
  ]
43
44
 
44
45
  [project.optional-dependencies]
@@ -1852,18 +1852,21 @@ def cmd_test(args):
1852
1852
  all_paths.update(config.get("app_data_paths", {}))
1853
1853
 
1854
1854
  if all_paths:
1855
- for idx, (host_path, guest_path) in enumerate(all_paths.items()):
1856
- try:
1857
- # Use the same QGA helper as diagnose/status
1858
- is_accessible = _qga_exec(
1859
- vm_name, conn_uri, f"test -d {guest_path} && echo yes || echo no", timeout=5
1860
- )
1861
- if is_accessible == "yes":
1862
- console.print(f"[green]✅ {guest_path}[/]")
1863
- else:
1864
- console.print(f"[red]❌ {guest_path} (not accessible)[/]")
1865
- except Exception:
1866
- console.print(f"[yellow]⚠️ {guest_path} (could not check)[/]")
1855
+ if not _qga_ping(vm_name, conn_uri):
1856
+ console.print("[yellow]⚠️ QEMU guest agent not connected - cannot verify mounts[/]")
1857
+ else:
1858
+ for idx, (host_path, guest_path) in enumerate(all_paths.items()):
1859
+ try:
1860
+ # Use the same QGA helper as diagnose/status
1861
+ is_accessible = _qga_exec(
1862
+ vm_name, conn_uri, f"test -d {guest_path} && echo yes || echo no", timeout=5
1863
+ )
1864
+ if is_accessible == "yes":
1865
+ console.print(f"[green]✅ {guest_path}[/]")
1866
+ else:
1867
+ console.print(f"[red]❌ {guest_path} (not accessible)[/]")
1868
+ except Exception:
1869
+ console.print(f"[yellow]⚠️ {guest_path} (could not check)[/]")
1867
1870
  else:
1868
1871
  console.print("[dim]No mount points configured[/]")
1869
1872
 
@@ -2100,7 +2103,7 @@ def generate_clonebox_yaml(
2100
2103
  vm_name = f"clone-{sys_info['hostname']}"
2101
2104
 
2102
2105
  # Calculate recommended resources
2103
- ram_mb = min(4096, int(sys_info["memory_available_gb"] * 1024 * 0.5))
2106
+ ram_mb = min(8192, int(sys_info["memory_available_gb"] * 1024 * 0.5))
2104
2107
  vcpus = max(2, sys_info["cpu_count"] // 2)
2105
2108
 
2106
2109
  if disk_size_gb is None:
@@ -2333,8 +2336,8 @@ def create_vm_from_config(
2333
2336
 
2334
2337
  vm_config = VMConfig(
2335
2338
  name=config["vm"]["name"],
2336
- ram_mb=config["vm"].get("ram_mb", 4096),
2337
- vcpus=config["vm"].get("vcpus", 4),
2339
+ ram_mb=config["vm"].get("ram_mb", 8192),
2340
+ vcpus=config["vm"].get("vcpus", 8),
2338
2341
  disk_size_gb=config["vm"].get("disk_size_gb", 10),
2339
2342
  gui=config["vm"].get("gui", True),
2340
2343
  base_image=config["vm"].get("base_image"),
@@ -14,8 +14,8 @@ class VMSettings(BaseModel):
14
14
  """VM-specific settings."""
15
15
 
16
16
  name: str = Field(default="clonebox-vm", description="VM name")
17
- ram_mb: int = Field(default=4096, ge=512, le=131072, description="RAM in MB")
18
- vcpus: int = Field(default=4, ge=1, le=128, description="Number of vCPUs")
17
+ ram_mb: int = Field(default=8192, ge=512, le=131072, description="RAM in MB")
18
+ vcpus: int = Field(default=8, ge=1, le=128, description="Number of vCPUs")
19
19
  disk_size_gb: int = Field(default=20, ge=1, le=2048, description="Disk size in GB")
20
20
  gui: bool = Field(default=True, description="Enable SPICE graphics")
21
21
  base_image: Optional[str] = Field(default=None, description="Path to base qcow2 image")
@@ -826,6 +826,26 @@ class VMValidator:
826
826
  self.console.print(table)
827
827
  return self.results["smoke"]
828
828
 
829
+ def _check_qga_ready(self) -> bool:
830
+ """Check if QEMU guest agent is responding."""
831
+ try:
832
+ result = subprocess.run(
833
+ [
834
+ "virsh",
835
+ "--connect",
836
+ self.conn_uri,
837
+ "qemu-agent-command",
838
+ self.vm_name,
839
+ '{"execute":"guest-ping"}',
840
+ ],
841
+ capture_output=True,
842
+ text=True,
843
+ timeout=5,
844
+ )
845
+ return result.returncode == 0
846
+ except Exception:
847
+ return False
848
+
829
849
  def validate_all(self) -> Dict:
830
850
  """Run all validations and return comprehensive results."""
831
851
  self.console.print("[bold cyan]🔍 Running Full Validation...[/]")
@@ -850,6 +870,20 @@ class VMValidator:
850
870
  self.results["overall"] = "error"
851
871
  return self.results
852
872
 
873
+ # Check QEMU Guest Agent
874
+ if not self._check_qga_ready():
875
+ self.console.print("[red]❌ QEMU Guest Agent not responding[/]")
876
+ self.console.print("\n[bold]🔧 Troubleshooting QGA:[/]")
877
+ self.console.print(" 1. The VM might still be booting. Wait 30-60 seconds.")
878
+ self.console.print(" 2. Ensure the agent is installed and running inside the VM:")
879
+ self.console.print(" [dim]virsh console " + self.vm_name + "[/]")
880
+ self.console.print(" [dim]sudo systemctl status qemu-guest-agent[/]")
881
+ self.console.print(" 3. If newly created, cloud-init might still be running.")
882
+ self.console.print(" 4. Check VM logs: [dim]clonebox logs " + self.vm_name + "[/]")
883
+ self.console.print(f"\n[yellow]⚠️ Skipping deep validation as it requires a working Guest Agent.[/]")
884
+ self.results["overall"] = "qga_not_ready"
885
+ return self.results
886
+
853
887
  # Run all validations
854
888
  self.validate_mounts()
855
889
  self.validate_packages()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: clonebox
3
- Version: 1.1.5
3
+ Version: 1.1.6
4
4
  Summary: Clone your workstation environment to an isolated VM with selective apps, paths and services
5
5
  Author: CloneBox Team
6
6
  License: Apache-2.0
@@ -33,6 +33,7 @@ Requires-Dist: pyyaml>=6.0
33
33
  Requires-Dist: pydantic>=2.0.0
34
34
  Requires-Dist: python-dotenv>=1.0.0
35
35
  Requires-Dist: cryptography>=42.0.0
36
+ Requires-Dist: structlog>=24.0.0
36
37
  Provides-Extra: dev
37
38
  Requires-Dist: pytest>=7.0.0; extra == "dev"
38
39
  Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
@@ -6,6 +6,7 @@ pyyaml>=6.0
6
6
  pydantic>=2.0.0
7
7
  python-dotenv>=1.0.0
8
8
  cryptography>=42.0.0
9
+ structlog>=24.0.0
9
10
 
10
11
  [dashboard]
11
12
  fastapi>=0.100.0
@@ -15,8 +15,8 @@ class TestVMSettings:
15
15
  def test_default_values(self):
16
16
  settings = VMSettings()
17
17
  assert settings.name == "clonebox-vm"
18
- assert settings.ram_mb == 4096
19
- assert settings.vcpus == 4
18
+ assert settings.ram_mb == 8192
19
+ assert settings.vcpus == 8
20
20
  assert settings.gui is True
21
21
 
22
22
  @pytest.mark.parametrize(
@@ -173,7 +173,7 @@ class TestCloneBoxConfigYAML:
173
173
  def test_yaml_roundtrip(self, tmp_path):
174
174
  original = CloneBoxConfig(
175
175
  version="1",
176
- vm=VMSettings(name="yaml-vm", ram_mb=4096, network_mode="user"),
176
+ vm=VMSettings(name="yaml-vm", ram_mb=8192, network_mode="user"),
177
177
  paths={"/home/user/code": "/mnt/code"},
178
178
  app_data_paths={"/home/user/.config/app": "/home/ubuntu/.config/app"},
179
179
  packages=["python3", "nodejs"],
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes