clonebox 1.1.5__py3-none-any.whl → 1.1.7__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
@@ -92,6 +92,7 @@ def _resolve_vm_name_and_config_file(name: Optional[str]) -> Tuple[str, Optional
92
92
 
93
93
  def _qga_ping(vm_name: str, conn_uri: str) -> bool:
94
94
  import subprocess
95
+ import json
95
96
 
96
97
  try:
97
98
  result = subprocess.run(
@@ -116,6 +117,7 @@ def _qga_exec(vm_name: str, conn_uri: str, command: str, timeout: int = 10) -> O
116
117
  import subprocess
117
118
  import base64
118
119
  import time
120
+ import json
119
121
 
120
122
  try:
121
123
  payload = {
@@ -1852,18 +1854,21 @@ def cmd_test(args):
1852
1854
  all_paths.update(config.get("app_data_paths", {}))
1853
1855
 
1854
1856
  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)[/]")
1857
+ if not _qga_ping(vm_name, conn_uri):
1858
+ console.print("[yellow]⚠️ QEMU guest agent not connected - cannot verify mounts[/]")
1859
+ else:
1860
+ for idx, (host_path, guest_path) in enumerate(all_paths.items()):
1861
+ try:
1862
+ # Use the same QGA helper as diagnose/status
1863
+ is_accessible = _qga_exec(
1864
+ vm_name, conn_uri, f"test -d {guest_path} && echo yes || echo no", timeout=5
1865
+ )
1866
+ if is_accessible == "yes":
1867
+ console.print(f"[green]✅ {guest_path}[/]")
1868
+ else:
1869
+ console.print(f"[red]❌ {guest_path} (not accessible)[/]")
1870
+ except Exception:
1871
+ console.print(f"[yellow]⚠️ {guest_path} (could not check)[/]")
1867
1872
  else:
1868
1873
  console.print("[dim]No mount points configured[/]")
1869
1874
 
@@ -2100,7 +2105,7 @@ def generate_clonebox_yaml(
2100
2105
  vm_name = f"clone-{sys_info['hostname']}"
2101
2106
 
2102
2107
  # Calculate recommended resources
2103
- ram_mb = min(4096, int(sys_info["memory_available_gb"] * 1024 * 0.5))
2108
+ ram_mb = min(8192, int(sys_info["memory_available_gb"] * 1024 * 0.5))
2104
2109
  vcpus = max(2, sys_info["cpu_count"] // 2)
2105
2110
 
2106
2111
  if disk_size_gb is None:
@@ -2333,8 +2338,8 @@ def create_vm_from_config(
2333
2338
 
2334
2339
  vm_config = VMConfig(
2335
2340
  name=config["vm"]["name"],
2336
- ram_mb=config["vm"].get("ram_mb", 4096),
2337
- vcpus=config["vm"].get("vcpus", 4),
2341
+ ram_mb=config["vm"].get("ram_mb", 8192),
2342
+ vcpus=config["vm"].get("vcpus", 8),
2338
2343
  disk_size_gb=config["vm"].get("disk_size_gb", 10),
2339
2344
  gui=config["vm"].get("gui", True),
2340
2345
  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
@@ -728,7 +728,20 @@ class VMValidator:
728
728
  return None if out is None else out.strip() == "yes"
729
729
 
730
730
  def _run_test(app: str) -> Optional[bool]:
731
- user_env = f"sudo -u {vm_user} env HOME=/home/{vm_user} XDG_RUNTIME_DIR=/run/user/1000"
731
+ uid_out = self._exec_in_vm(f"id -u {vm_user} 2>/dev/null || true", timeout=10)
732
+ vm_uid = (uid_out or "").strip()
733
+ if not vm_uid.isdigit():
734
+ vm_uid = "1000"
735
+
736
+ runtime_dir = f"/run/user/{vm_uid}"
737
+ self._exec_in_vm(
738
+ f"mkdir -p {runtime_dir} && chown {vm_uid}:{vm_uid} {runtime_dir} && chmod 700 {runtime_dir}",
739
+ timeout=10,
740
+ )
741
+
742
+ user_env = (
743
+ f"sudo -u {vm_user} env HOME=/home/{vm_user} USER={vm_user} LOGNAME={vm_user} XDG_RUNTIME_DIR={runtime_dir}"
744
+ )
732
745
 
733
746
  if app == "pycharm-community":
734
747
  out = self._exec_in_vm(
@@ -826,6 +839,26 @@ class VMValidator:
826
839
  self.console.print(table)
827
840
  return self.results["smoke"]
828
841
 
842
+ def _check_qga_ready(self) -> bool:
843
+ """Check if QEMU guest agent is responding."""
844
+ try:
845
+ result = subprocess.run(
846
+ [
847
+ "virsh",
848
+ "--connect",
849
+ self.conn_uri,
850
+ "qemu-agent-command",
851
+ self.vm_name,
852
+ '{"execute":"guest-ping"}',
853
+ ],
854
+ capture_output=True,
855
+ text=True,
856
+ timeout=5,
857
+ )
858
+ return result.returncode == 0
859
+ except Exception:
860
+ return False
861
+
829
862
  def validate_all(self) -> Dict:
830
863
  """Run all validations and return comprehensive results."""
831
864
  self.console.print("[bold cyan]🔍 Running Full Validation...[/]")
@@ -850,6 +883,20 @@ class VMValidator:
850
883
  self.results["overall"] = "error"
851
884
  return self.results
852
885
 
886
+ # Check QEMU Guest Agent
887
+ if not self._check_qga_ready():
888
+ self.console.print("[red]❌ QEMU Guest Agent not responding[/]")
889
+ self.console.print("\n[bold]🔧 Troubleshooting QGA:[/]")
890
+ self.console.print(" 1. The VM might still be booting. Wait 30-60 seconds.")
891
+ self.console.print(" 2. Ensure the agent is installed and running inside the VM:")
892
+ self.console.print(" [dim]virsh console " + self.vm_name + "[/]")
893
+ self.console.print(" [dim]sudo systemctl status qemu-guest-agent[/]")
894
+ self.console.print(" 3. If newly created, cloud-init might still be running.")
895
+ self.console.print(" 4. Check VM logs: [dim]clonebox logs " + self.vm_name + "[/]")
896
+ self.console.print(f"\n[yellow]⚠️ Skipping deep validation as it requires a working Guest Agent.[/]")
897
+ self.results["overall"] = "qga_not_ready"
898
+ return self.results
899
+
853
900
  # Run all validations
854
901
  self.validate_mounts()
855
902
  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.7
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=KR3naqYB1ZRFDGGoQAfqRk4Ci4NVF3WiY3nOzRKmcyg,136362
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=zX_E0X3IOZ8Dnmn8i9IVXViYXw-9Nyj0L_YyCKYRj9I,38722
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.7.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
38
+ clonebox-1.1.7.dist-info/METADATA,sha256=ojWCleuifB5DP3QDwjgT-X45ge_SslR30e7CyHl8iwY,48915
39
+ clonebox-1.1.7.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
40
+ clonebox-1.1.7.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
41
+ clonebox-1.1.7.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
42
+ clonebox-1.1.7.dist-info/RECORD,,