From 4e7636e4540b0fa7974c8625582a3dbd08ea8df9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=D0=92=D0=B8=D0=BA=D1=82=D0=BE=D1=80?=
<78488229+viktor138irk@users.noreply.github.com>
Date: Thu, 7 May 2026 07:18:53 +0900
Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B0=D1=82=D1=87=20=D0=BF=D0=BE=D1=80?=
=?UTF-8?q?=D1=8F=D0=B4=D0=BA=D0=B0=20=D0=B7=D0=B0=D0=B3=D1=80=D1=83=D0=B7?=
=?UTF-8?q?=D0=BA=D0=B8=20VM=20=D0=B1=D1=8B=D0=BB=20=D0=B4=D0=BE=D0=B1?=
=?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
scripts/patch_vm_boot_order.py | 150 +++++++++++++++++++++++++++++++++
1 file changed, 150 insertions(+)
create mode 100644 scripts/patch_vm_boot_order.py
diff --git a/scripts/patch_vm_boot_order.py b/scripts/patch_vm_boot_order.py
new file mode 100644
index 0000000..9fc88c6
--- /dev/null
+++ b/scripts/patch_vm_boot_order.py
@@ -0,0 +1,150 @@
+#!/usr/bin/env python3
+from pathlib import Path
+import re
+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}')
+
+app_dir = app_path.resolve().parent
+template_path = app_dir / 'templates' / 'vm_create.html'
+changed = []
+
+text = app_path.read_text()
+
+helpers = r'''
+
+def vm_boot_order_options() -> list[dict[str, str]]:
+ return [
+ {"value": "auto", "label": "Auto — по источнику VM"},
+ {"value": "disk", "label": "Сначала диск"},
+ {"value": "cdrom_disk", "label": "Сначала ISO/CD-ROM, потом диск"},
+ {"value": "disk_cdrom", "label": "Сначала диск, потом ISO/CD-ROM"},
+ {"value": "network_disk", "label": "Сначала сеть/PXE, потом диск"},
+ ]
+
+
+def normalize_boot_order(value: str, source_type: str) -> str:
+ value = (value or "auto").strip()
+ if value == "auto":
+ return "cdrom_disk" if source_type == "iso" else "disk"
+ if value in ("disk", "cdrom_disk", "disk_cdrom", "network_disk"):
+ return value
+ return "cdrom_disk" if source_type == "iso" else "disk"
+
+
+def virt_boot_arg(boot_order: str, is_arm: bool) -> str:
+ mapping = {
+ "disk": "hd",
+ "cdrom_disk": "cdrom,hd",
+ "disk_cdrom": "hd,cdrom",
+ "network_disk": "network,hd",
+ }
+ value = mapping.get(boot_order, "hd")
+ if is_arm:
+ return "uefi," + value
+ return value
+'''
+
+if 'def vm_boot_order_options()' not in text:
+ marker = '\n\ndef vm_form_context('
+ if marker not in text:
+ raise SystemExit('vm_form_context marker not found')
+ text = text.replace(marker, helpers + marker, 1)
+ changed.append('boot order helpers added')
+else:
+ changed.append('boot order helpers already present')
+
+if '"boot_options": vm_boot_order_options()' not in text:
+ text = text.replace('"arch_options": vm_arch_options(),', '"arch_options": vm_arch_options(), "boot_options": vm_boot_order_options(),')
+ text = text.replace('"guest_arch": "auto", "network_mode"', '"guest_arch": "auto", "boot_order": "auto", "network_mode"')
+ changed.append('vm form context gets boot options')
+else:
+ changed.append('vm form context already has boot options')
+
+if 'boot_order: str = Form("auto")' not in text:
+ text = text.replace('guest_arch: str = Form("auto"), network_mode:', 'guest_arch: str = Form("auto"), boot_order: str = Form("auto"), network_mode:', 1)
+ changed.append('vm create signature supports boot order')
+else:
+ changed.append('vm create signature already supports boot order')
+
+if '"boot_order": boot_order' not in text:
+ text = text.replace('"guest_arch": guest_arch, "network_mode"', '"guest_arch": guest_arch, "boot_order": boot_order, "network_mode"', 1)
+ changed.append('vm create form state supports boot order')
+else:
+ changed.append('vm create form state already supports boot order')
+
+if 'boot_order not in ("auto", "disk", "cdrom_disk", "disk_cdrom", "network_disk")' not in text:
+ text = text.replace(
+ 'elif guest_arch not in ("auto", "x86_64", "aarch64", "generic"):\n error = "Некорректная архитектура VM."',
+ 'elif guest_arch not in ("auto", "x86_64", "aarch64", "generic"):\n error = "Некорректная архитектура VM."\n elif boot_order not in ("auto", "disk", "cdrom_disk", "disk_cdrom", "network_disk"):\n error = "Некорректный порядок загрузки VM."',
+ 1,
+ )
+ changed.append('vm create validation supports boot order')
+else:
+ changed.append('vm create validation already supports boot order')
+
+if 'selected_boot_order = normalize_boot_order(boot_order, source_type)' not in text:
+ text = text.replace('selected_arch = normalize_guest_arch(guest_arch, profile)\n', 'selected_arch = normalize_guest_arch(guest_arch, profile)\n selected_boot_order = normalize_boot_order(boot_order, source_type)\n', 1)
+ changed.append('selected boot order added')
+else:
+ changed.append('selected boot order already present')
+
+# Replace ARM-specific --boot uefi in architecture block with a later unified --boot argument.
+text = text.replace(', "--boot", "uefi"]', ']')
+
+if 'cmd += ["--boot", virt_boot_arg(selected_boot_order, is_arm)]' not in text:
+ marker = ' if source_type == "disk_image":\n'
+ if marker not in text:
+ raise SystemExit('source_type command marker not found')
+ text = text.replace(marker, ' cmd += ["--boot", virt_boot_arg(selected_boot_order, is_arm)]\n\n' + marker, 1)
+ changed.append('virt-install boot argument added')
+else:
+ changed.append('virt-install boot argument already present')
+
+if '"boot_order": selected_boot_order' not in text:
+ text = text.replace('"guest_arch": selected_arch, "host_profile"', '"guest_arch": selected_arch, "boot_order": selected_boot_order, "host_profile"')
+ changed.append('operation metadata gets boot order')
+else:
+ changed.append('operation metadata already has boot order')
+
+app_path.write_text(text)
+
+if template_path.exists():
+ tpl = template_path.read_text()
+ original = tpl
+
+ if 'name="boot_order"' not in tpl:
+ insert = r'''
+
+'''
+ marker = '''