clonebox 1.1.5__py3-none-any.whl → 1.1.6__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.
clonebox/cli.py CHANGED
@@ -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"),
clonebox/models.py CHANGED
@@ -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")
clonebox/validator.py CHANGED
@@ -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"
@@ -1,6 +1,6 @@
1
1
  clonebox/__init__.py,sha256=CyfHVVq6KqBr4CNERBpXk_O6Q5B35q03YpdQbokVvvI,408
2
2
  clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
3
- clonebox/cli.py,sha256=m2WBz1SDTgpeDvX-dc93bDp4CKp7AcEI17vXWx3D4os,136109
3
+ clonebox/cli.py,sha256=uVACDtbGh0bNcZY3p2s5W90aU6NQHzuTnTLelWCOlss,136330
4
4
  clonebox/cloner.py,sha256=aH7BwmLgPITHzyHn_n_AKqtEADDm_aUXiRG-nB2rxMQ,90063
5
5
  clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
6
6
  clonebox/dashboard.py,sha256=dMY6odvPq3j6FronhRRsX7aY3qdCwznB-aCWKEmHDNw,5768
@@ -9,7 +9,7 @@ clonebox/di.py,sha256=feFMXP5ff-7gwrIqgnoCpk1ivaiZA_lv2wcpkCSKiew,5648
9
9
  clonebox/exporter.py,sha256=WIzVvmA0z_jjrpyXxvnXoLp9oaW6fKS7k0PGwzx_PIM,5629
10
10
  clonebox/importer.py,sha256=Q9Uk1IOA41mgGhU4ynW2k-h9GEoGxRKI3c9wWE4uxcA,7097
11
11
  clonebox/logging.py,sha256=jD_WgkHmt_h99EmX82QWK7D0OKWyxqcLwgT4UDQFp2g,3675
12
- clonebox/models.py,sha256=zwejkNtEEO_aPy_Q5UzXG5tszU-c7lkqh9LQus9eWMo,8307
12
+ clonebox/models.py,sha256=l9e5U4ZEEcgCTt2c47GpqFwppwVeifoCoNFi57lFGFo,8307
13
13
  clonebox/monitor.py,sha256=zlIarNf8w_i34XI8hZGxxrg5PVZK_Yxm6FQnkhLavRI,9181
14
14
  clonebox/p2p.py,sha256=6o0JnscKqF9-BftQhW5fF1W6YY1wXshY9LEklNcHGJc,5913
15
15
  clonebox/profiles.py,sha256=UP37fX_rhrG_O9ehNFJBUcULPmUtN1A8KsJ6cM44oK0,1986
@@ -17,7 +17,7 @@ clonebox/resource_monitor.py,sha256=lDR9KyPbVtImeeOkOBPPVP-5yCgoL5hsVFPZ_UqsY0w,
17
17
  clonebox/resources.py,sha256=IkuM4OdSDV4qhyc0eIynwbAHBTv0aVSxxW-gghsnCAs,6815
18
18
  clonebox/rollback.py,sha256=hpwO-8Ehe1pW0wHuZvJkC_qxZ6yEo9otCJRhGIUArCo,5711
19
19
  clonebox/secrets.py,sha256=QtGMIjhTKq14qQRGy-pX0TU3vKLiZt7CMloAlF2GmzQ,10515
20
- clonebox/validator.py,sha256=CF4hMlY69-AGRH5HdG8HAA9_LNCwDKD4xPlYQPWJ9Rw,36647
20
+ clonebox/validator.py,sha256=2yQXDaL5bzHmxKkEP0fGJ85HV7Ax3iQl7KzSlBVLZkc,38220
21
21
  clonebox/backends/libvirt_backend.py,sha256=sIHFIvFO1hIOXEFR_foSkOGBgIzaJVQs-njOU8GdafA,7170
22
22
  clonebox/backends/qemu_disk.py,sha256=YsGjYX5sbEf35Y4yjTpNkZat73a4RGBxY-KTVzJhqIs,1687
23
23
  clonebox/backends/subprocess_runner.py,sha256=c-IyaMxM1cmUu64h654oAvulm83K5Mu-VQxXJ_0BOds,1506
@@ -34,9 +34,9 @@ clonebox/snapshots/manager.py,sha256=hGzM8V6ZJPXjTqj47c4Kr8idlE-c1Q3gPUvuw1HvS1A
34
34
  clonebox/snapshots/models.py,sha256=sRnn3OZE8JG9FZJlRuA3ihO-JXoPCQ3nD3SQytflAao,6206
35
35
  clonebox/templates/profiles/ml-dev.yaml,sha256=w07MToGh31xtxpjbeXTBk9BkpAN8A3gv8HeA3ESKG9M,461
36
36
  clonebox/templates/profiles/web-stack.yaml,sha256=EBnnGMzML5vAjXmIUbCpbTCwmRaNJiuWd3EcL43DOK8,485
37
- clonebox-1.1.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
38
- clonebox-1.1.5.dist-info/METADATA,sha256=0sp9wPpm969pIM4iwq3m22B3uxh0vC694mJSIRjrUoc,48882
39
- clonebox-1.1.5.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
40
- clonebox-1.1.5.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
41
- clonebox-1.1.5.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
42
- clonebox-1.1.5.dist-info/RECORD,,
37
+ clonebox-1.1.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
38
+ clonebox-1.1.6.dist-info/METADATA,sha256=36_PJreaPh-6Ot5Irm6NSdFjzwGkAGZGHBs4TYcB9lQ,48915
39
+ clonebox-1.1.6.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
40
+ clonebox-1.1.6.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
41
+ clonebox-1.1.6.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
42
+ clonebox-1.1.6.dist-info/RECORD,,