clonebox 0.1.16__py3-none-any.whl → 0.1.17__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 +76 -75
- clonebox/cloner.py +19 -12
- {clonebox-0.1.16.dist-info → clonebox-0.1.17.dist-info}/METADATA +1 -1
- {clonebox-0.1.16.dist-info → clonebox-0.1.17.dist-info}/RECORD +8 -8
- {clonebox-0.1.16.dist-info → clonebox-0.1.17.dist-info}/WHEEL +0 -0
- {clonebox-0.1.16.dist-info → clonebox-0.1.17.dist-info}/entry_points.txt +0 -0
- {clonebox-0.1.16.dist-info → clonebox-0.1.17.dist-info}/licenses/LICENSE +0 -0
- {clonebox-0.1.16.dist-info → clonebox-0.1.17.dist-info}/top_level.txt +0 -0
clonebox/cli.py
CHANGED
|
@@ -80,96 +80,98 @@ def _resolve_vm_name_and_config_file(name: Optional[str]) -> tuple[str, Optional
|
|
|
80
80
|
|
|
81
81
|
|
|
82
82
|
def _qga_ping(vm_name: str, conn_uri: str) -> bool:
|
|
83
|
-
|
|
83
|
+
def _qga_ping() -> bool:
|
|
84
|
+
try:
|
|
85
|
+
result = subprocess.run(
|
|
86
|
+
[
|
|
87
|
+
"virsh",
|
|
88
|
+
"--connect",
|
|
89
|
+
conn_uri,
|
|
90
|
+
"qemu-agent-command",
|
|
91
|
+
vm_name,
|
|
92
|
+
json.dumps({"execute": "guest-ping"}),
|
|
93
|
+
],
|
|
94
|
+
capture_output=True,
|
|
95
|
+
text=True,
|
|
96
|
+
timeout=5,
|
|
97
|
+
)
|
|
98
|
+
return result.returncode == 0
|
|
99
|
+
except Exception:
|
|
100
|
+
return False
|
|
84
101
|
|
|
85
|
-
|
|
86
|
-
result = subprocess.run(
|
|
87
|
-
[
|
|
88
|
-
"virsh",
|
|
89
|
-
"--connect",
|
|
90
|
-
conn_uri,
|
|
91
|
-
"qemu-agent-command",
|
|
92
|
-
vm_name,
|
|
93
|
-
json.dumps({"execute": "guest-ping"}),
|
|
94
|
-
],
|
|
95
|
-
capture_output=True,
|
|
96
|
-
text=True,
|
|
97
|
-
timeout=5,
|
|
98
|
-
)
|
|
99
|
-
return result.returncode == 0
|
|
100
|
-
except Exception:
|
|
101
|
-
return False
|
|
102
|
+
return _qga_ping()
|
|
102
103
|
|
|
103
104
|
|
|
104
105
|
def _qga_exec(vm_name: str, conn_uri: str, command: str, timeout: int = 10) -> Optional[str]:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
exec_result = subprocess.run(
|
|
117
|
-
[
|
|
118
|
-
"virsh",
|
|
119
|
-
"--connect",
|
|
120
|
-
conn_uri,
|
|
121
|
-
"qemu-agent-command",
|
|
122
|
-
vm_name,
|
|
123
|
-
json.dumps(payload),
|
|
124
|
-
],
|
|
125
|
-
capture_output=True,
|
|
126
|
-
text=True,
|
|
127
|
-
timeout=timeout,
|
|
128
|
-
)
|
|
129
|
-
if exec_result.returncode != 0:
|
|
130
|
-
return None
|
|
131
|
-
|
|
132
|
-
resp = json.loads(exec_result.stdout)
|
|
133
|
-
pid = resp.get("return", {}).get("pid")
|
|
134
|
-
if not pid:
|
|
135
|
-
return None
|
|
136
|
-
|
|
137
|
-
import base64
|
|
138
|
-
import time
|
|
139
|
-
|
|
140
|
-
deadline = time.time() + timeout
|
|
141
|
-
while time.time() < deadline:
|
|
142
|
-
status_payload = {"execute": "guest-exec-status", "arguments": {"pid": pid}}
|
|
143
|
-
status_result = subprocess.run(
|
|
106
|
+
def _qga_exec() -> Optional[str]:
|
|
107
|
+
try:
|
|
108
|
+
payload = {
|
|
109
|
+
"execute": "guest-exec",
|
|
110
|
+
"arguments": {
|
|
111
|
+
"path": "/bin/sh",
|
|
112
|
+
"arg": ["-c", command],
|
|
113
|
+
"capture-output": True,
|
|
114
|
+
},
|
|
115
|
+
}
|
|
116
|
+
exec_result = subprocess.run(
|
|
144
117
|
[
|
|
145
118
|
"virsh",
|
|
146
119
|
"--connect",
|
|
147
120
|
conn_uri,
|
|
148
121
|
"qemu-agent-command",
|
|
149
122
|
vm_name,
|
|
150
|
-
json.dumps(
|
|
123
|
+
json.dumps(payload),
|
|
151
124
|
],
|
|
152
125
|
capture_output=True,
|
|
153
126
|
text=True,
|
|
154
|
-
timeout=
|
|
127
|
+
timeout=timeout,
|
|
155
128
|
)
|
|
156
|
-
if
|
|
129
|
+
if exec_result.returncode != 0:
|
|
157
130
|
return None
|
|
158
131
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if not
|
|
162
|
-
|
|
163
|
-
continue
|
|
132
|
+
resp = json.loads(exec_result.stdout)
|
|
133
|
+
pid = resp.get("return", {}).get("pid")
|
|
134
|
+
if not pid:
|
|
135
|
+
return None
|
|
164
136
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
return base64.b64decode(out_data).decode().strip()
|
|
168
|
-
return ""
|
|
137
|
+
import base64
|
|
138
|
+
import time
|
|
169
139
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
140
|
+
deadline = time.time() + timeout
|
|
141
|
+
while time.time() < deadline:
|
|
142
|
+
status_payload = {"execute": "guest-exec-status", "arguments": {"pid": pid}}
|
|
143
|
+
status_result = subprocess.run(
|
|
144
|
+
[
|
|
145
|
+
"virsh",
|
|
146
|
+
"--connect",
|
|
147
|
+
conn_uri,
|
|
148
|
+
"qemu-agent-command",
|
|
149
|
+
vm_name,
|
|
150
|
+
json.dumps(status_payload),
|
|
151
|
+
],
|
|
152
|
+
capture_output=True,
|
|
153
|
+
text=True,
|
|
154
|
+
timeout=5,
|
|
155
|
+
)
|
|
156
|
+
if status_result.returncode != 0:
|
|
157
|
+
return None
|
|
158
|
+
|
|
159
|
+
status_resp = json.loads(status_result.stdout)
|
|
160
|
+
ret = status_resp.get("return", {})
|
|
161
|
+
if not ret.get("exited", False):
|
|
162
|
+
time.sleep(0.3)
|
|
163
|
+
continue
|
|
164
|
+
|
|
165
|
+
out_data = ret.get("out-data")
|
|
166
|
+
if out_data:
|
|
167
|
+
return base64.b64decode(out_data).decode().strip()
|
|
168
|
+
return ""
|
|
169
|
+
|
|
170
|
+
return None
|
|
171
|
+
except Exception:
|
|
172
|
+
return None
|
|
173
|
+
|
|
174
|
+
return _qga_exec()
|
|
173
175
|
|
|
174
176
|
|
|
175
177
|
def run_vm_diagnostics(
|
|
@@ -698,14 +700,13 @@ def interactive_mode():
|
|
|
698
700
|
if questionary.confirm("Start VM now?", default=True, style=custom_style).ask():
|
|
699
701
|
cloner.start_vm(vm_name, open_viewer=enable_gui, console=console)
|
|
700
702
|
console.print("\n[bold green]🎉 VM is running![/]")
|
|
703
|
+
console.print(f"\n[dim]UUID: {vm_uuid}[/]")
|
|
701
704
|
|
|
702
705
|
if paths_mapping:
|
|
703
706
|
console.print("\n[bold]Inside the VM, mount shared folders with:[/]")
|
|
704
707
|
for idx, (host, guest) in enumerate(paths_mapping.items()):
|
|
705
708
|
console.print(f" [cyan]sudo mount -t 9p -o trans=virtio mount{idx} {guest}[/]")
|
|
706
709
|
|
|
707
|
-
console.print(f"\n[dim]VM UUID: {vm_uuid}[/]")
|
|
708
|
-
|
|
709
710
|
except Exception as e:
|
|
710
711
|
console.print(f"\n[red]❌ Error: {e}[/]")
|
|
711
712
|
raise
|
|
@@ -942,7 +943,7 @@ def cmd_container_up(args):
|
|
|
942
943
|
"Container features require extra dependencies (e.g. pydantic). Install them to use 'clonebox container'."
|
|
943
944
|
) from e
|
|
944
945
|
|
|
945
|
-
mounts
|
|
946
|
+
mounts = {}
|
|
946
947
|
for m in getattr(args, "mount", []) or []:
|
|
947
948
|
if ":" not in m:
|
|
948
949
|
raise ValueError(f"Invalid mount: {m} (expected HOST:CONTAINER)")
|
clonebox/cloner.py
CHANGED
|
@@ -679,19 +679,27 @@ fi
|
|
|
679
679
|
|
|
680
680
|
# User-data
|
|
681
681
|
# Add desktop environment if GUI is enabled
|
|
682
|
-
runcmd_lines = []
|
|
683
682
|
base_packages = ["qemu-guest-agent"]
|
|
684
683
|
if config.gui:
|
|
685
|
-
base_packages.extend(
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
]
|
|
690
|
-
)
|
|
684
|
+
base_packages.extend([
|
|
685
|
+
"ubuntu-desktop-minimal",
|
|
686
|
+
"firefox",
|
|
687
|
+
])
|
|
691
688
|
|
|
692
689
|
all_packages = base_packages + list(config.packages)
|
|
693
|
-
packages_yaml =
|
|
690
|
+
packages_yaml = (
|
|
691
|
+
"\n".join(f" - {pkg}" for pkg in all_packages) if all_packages else ""
|
|
692
|
+
)
|
|
693
|
+
|
|
694
|
+
# Build runcmd - services, mounts, snaps, post_commands
|
|
695
|
+
runcmd_lines = []
|
|
694
696
|
|
|
697
|
+
runcmd_lines.append(" - systemctl enable --now qemu-guest-agent || true")
|
|
698
|
+
|
|
699
|
+
# Add service enablement
|
|
700
|
+
for svc in config.services:
|
|
701
|
+
runcmd_lines.append(f" - systemctl enable --now {svc} || true")
|
|
702
|
+
|
|
695
703
|
# Add fstab entries for persistent mounts after reboot
|
|
696
704
|
if fstab_entries:
|
|
697
705
|
runcmd_lines.append(" - grep -q '^# CloneBox 9p mounts' /etc/fstab || echo '# CloneBox 9p mounts' >> /etc/fstab")
|
|
@@ -699,10 +707,9 @@ fi
|
|
|
699
707
|
runcmd_lines.append(f" - grep -qF \"{entry}\" /etc/fstab || echo '{entry}' >> /etc/fstab")
|
|
700
708
|
runcmd_lines.append(" - mount -a || true")
|
|
701
709
|
|
|
702
|
-
#
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
runcmd_lines.append(f" - apt-get install -y {pkg} || true")
|
|
710
|
+
# Add mounts (immediate, before reboot)
|
|
711
|
+
for cmd in mount_commands:
|
|
712
|
+
runcmd_lines.append(cmd)
|
|
706
713
|
|
|
707
714
|
# Install snap packages
|
|
708
715
|
if config.snap_packages:
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
clonebox/__init__.py,sha256=C1J7Uwrp8H9Zopo5JgrQYzXg-PWls1JdqmE_0Qp1Tro,408
|
|
2
2
|
clonebox/__main__.py,sha256=Fcoyzwwyz5-eC_sBlQk5a5RbKx8uodQz5sKJ190U0NU,135
|
|
3
|
-
clonebox/cli.py,sha256=
|
|
4
|
-
clonebox/cloner.py,sha256=
|
|
3
|
+
clonebox/cli.py,sha256=mB2Xoz9llkHW9yIR-xRuZ94F_PmV833gdiKVKKcJQrc,98462
|
|
4
|
+
clonebox/cloner.py,sha256=tgN51yeNGesolO1wfuVh-CAGkAZew7oMoCYYz_bXgBk,32456
|
|
5
5
|
clonebox/container.py,sha256=tiYK1ZB-DhdD6A2FuMA0h_sRNkUI7KfYcJ0tFOcdyeM,6105
|
|
6
6
|
clonebox/detector.py,sha256=4fu04Ty6KC82WkcJZ5UL5TqXpWYE7Kb7R0uJ-9dtbCk,21635
|
|
7
7
|
clonebox/models.py,sha256=Uxz9eHov2epJpNYbl0ejaOX91iMSjqdHskGdC8-smVk,7789
|
|
8
8
|
clonebox/validator.py,sha256=8HV3ahfiLkFDOH4UOmZr7-fGfhKep1Jlw1joJeWSaQE,15858
|
|
9
|
-
clonebox-0.1.
|
|
10
|
-
clonebox-0.1.
|
|
11
|
-
clonebox-0.1.
|
|
12
|
-
clonebox-0.1.
|
|
13
|
-
clonebox-0.1.
|
|
14
|
-
clonebox-0.1.
|
|
9
|
+
clonebox-0.1.17.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
10
|
+
clonebox-0.1.17.dist-info/METADATA,sha256=YHJ3k1qzqhkB7hsuZVesQv5ANF6bbABLGTYSCl29p34,35220
|
|
11
|
+
clonebox-0.1.17.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
|
|
12
|
+
clonebox-0.1.17.dist-info/entry_points.txt,sha256=FES95Vi3btfViLEEoHdb8nikNxTqzaooi9ehZw9ZfWI,47
|
|
13
|
+
clonebox-0.1.17.dist-info/top_level.txt,sha256=LdMo2cvCrEcRGH2M8JgQNVsCoszLV0xug6kx1JnaRjo,9
|
|
14
|
+
clonebox-0.1.17.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|