#!/usr/bin/env python3 from pathlib import Path import sys app_path = Path(sys.argv[1]) if len(sys.argv) > 1 else Path('/opt/virtuality/web/app.py') if not app_path.exists(): raise SystemExit(f'app.py not found: {app_path}') text = app_path.read_text() changed = [] helpers = r''' def is_x86_host(profile: dict[str, Any]) -> bool: return str(profile.get("arch", "")) in ("x86_64", "amd64") def is_arm64_guest_on_x86(guest_arch: str, profile: dict[str, Any]) -> bool: return guest_arch == "aarch64" and is_x86_host(profile) def xml_escape(value: str) -> str: return str(value).replace("&", "&").replace("<", "<").replace(">", ">").replace('"', """) def arm64_emulation_xml(name: str, memory: int, vcpus: int, disk_path: Path, network_mode: str, bridge: str) -> str: if network_mode == "nat": interface_xml = f""" """ else: interface_xml = f""" """ return f""" {xml_escape(name)} {int(memory)} {int(memory)} {int(vcpus)} hvm cortex-a57 destroy restart restart /usr/bin/qemu-system-aarch64 {interface_xml} """ def make_arm64_emulation_script(name: str, memory: int, vcpus: int, disk_path: Path, network_mode: str, bridge: str) -> str: xml_path = Path('/tmp') / f"virtuality-{name}-arm64.xml" xml_path.write_text(arm64_emulation_xml(name, memory, vcpus, disk_path, network_mode, bridge)) return f"virsh define {xml_path} && virsh start {name}" ''' if 'def arm64_emulation_xml(' not in text: marker = '\n\ndef valid_vm_name(name: str) -> bool:' if marker not in text: raise SystemExit('valid_vm_name marker not found') text = text.replace(marker, helpers + marker, 1) changed.append('ARM64 emulation XML helpers added') else: changed.append('ARM64 emulation XML helpers already present') old_disk_cmd = ''' if source_type == "disk_image": source_disk = Path(disk_image_path).resolve() source_format = disk_image_format(source_disk) convert_cmd = f"qemu-img convert -p -f {source_format} -O qcow2 {source_disk} {disk_path}" virt_cmd = " ".join(cmd + ["--import", "--disk", f"path={disk_path},format=qcow2,bus=virtio", "--os-variant", "generic", "--network", network_arg, "--graphics", "vnc,listen=0.0.0.0", "--noautoconsole"]) cmd = ["bash", "-lc", f"set -euo pipefail; {convert_cmd}; {virt_cmd}"] else: cmd += ["--disk", f"path={disk_path},size={disk_size},format=qcow2,bus=virtio", "--cdrom", iso_path, "--os-variant", "generic", "--network", network_arg, "--graphics", "vnc,listen=0.0.0.0", "--noautoconsole"] ''' new_disk_cmd = ''' if source_type == "disk_image": source_disk = Path(disk_image_path).resolve() source_format = disk_image_format(source_disk) convert_cmd = f"qemu-img convert -p -f {source_format} -O qcow2 {source_disk} {disk_path}" if is_arm64_guest_on_x86(resolved_guest_arch, profile): create_cmd = make_arm64_emulation_script(name, memory, vcpus, disk_path, network_mode, bridge) cmd = ["bash", "-lc", f"set -euo pipefail; {convert_cmd}; {create_cmd}"] else: virt_cmd = " ".join(cmd + ["--import", "--disk", f"path={disk_path},format=qcow2,bus=virtio", "--os-variant", "generic", "--network", network_arg, "--graphics", "vnc,listen=0.0.0.0", "--noautoconsole"]) cmd = ["bash", "-lc", f"set -euo pipefail; {convert_cmd}; {virt_cmd}"] else: if is_arm64_guest_on_x86(resolved_guest_arch, profile): return vm_form_context(request, error="ARM64 ISO на x86-хосте пока не поддерживается. Загрузи готовый ARM64 .img/.qcow2 в разделе «Диски» и создай VM из готового диска.", form=form, status_code=400) cmd += ["--disk", f"path={disk_path},size={disk_size},format=qcow2,bus=virtio", "--cdrom", iso_path, "--os-variant", "generic", "--network", network_arg, "--graphics", "vnc,listen=0.0.0.0", "--noautoconsole"] ''' if old_disk_cmd in text: text = text.replace(old_disk_cmd, new_disk_cmd, 1) changed.append('ARM64 disk image on x86 uses direct libvirt XML') elif 'make_arm64_emulation_script' in text: changed.append('ARM64 disk image XML path already present') else: raise SystemExit('disk image create command marker not found') app_path.write_text(text) print('arm64 emulation xml patch applied:') for item in changed: print(f'- {item}')