diff --git a/scripts/patch_vm_detail_resource_layout.py b/scripts/patch_vm_detail_resource_layout.py new file mode 100644 index 0000000..5bb5da4 --- /dev/null +++ b/scripts/patch_vm_detail_resource_layout.py @@ -0,0 +1,121 @@ +#!/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') +app_dir = app_path.resolve().parent if app_path.exists() else Path('/opt/virtuality/web') +template_path = app_dir / 'templates' / 'vm_detail.html' +css_path = app_dir / 'static' / 'app.css' +changed = [] + +if not template_path.exists(): + raise SystemExit(f'vm_detail.html not found: {template_path}') + +tpl = template_path.read_text() +original = tpl + +resource_messages = r''' + {% if resource_message %} +
{{ resource_message }}
+ {% endif %} + {% if resource_error %} +
{{ resource_error }}
+ {% endif %} +''' + +resource_card = r''' +
+
+

CPU / RAM / Архитектура

+ resources +
+
+
CPU{{ resource_settings.vcpus }}
+
RAM{{ resource_settings.memory_mb }} MB
+
Архитектура{{ resource_settings.arch }}
+
+
+ + + + +
+ {% if not resource_settings.is_shutoff %} +
VM сейчас не выключена. CPU, RAM и архитектуру можно менять только после Shutdown/Power off.
+ {% endif %} +

CPU/RAM меняются в XML VM. Смена архитектуры требует совместимый диск и загрузчик; x86-диск не станет ARM-диском по щелчку, увы, физика всё ещё душнит.

+
+''' + +if 'resource_message' not in tpl: + marker = ' {% if iso_message %}' + if marker in tpl: + tpl = tpl.replace(marker, resource_messages + '\n' + marker, 1) + changed.append('resource messages added') + +if 'CPU / RAM / Архитектура' not in tpl: + marker = '
' + if marker in tpl: + tpl = tpl.replace(marker, '
', 1) + insert_at = tpl.find('\n
', tpl.find('vm-resource-boot-iso-grid')) + if insert_at != -1: + tpl = tpl[:insert_at] + '\n' + resource_card + tpl[insert_at:] + changed.append('resource card inserted before ISO card') + else: + # fallback: insert before first two-column grid in VM detail + marker = '\n\n
' + if marker not in tpl: + raise SystemExit('vm detail grid marker not found') + settings = '\n\n
\n' + resource_card + '
\n' + tpl = tpl.replace(marker, settings + marker, 1) + changed.append('resource grid inserted fallback') +else: + changed.append('resource card already present') + +# If grid is already present from older patch, upgrade two columns to three. +tpl = tpl.replace('class="grid two vm-boot-iso-grid"', 'class="grid three vm-resource-boot-iso-grid"') + +template_path.write_text(tpl) + +if css_path.exists(): + css = css_path.read_text() + original_css = css + if '.three {' not in css: + css = css.replace('.two { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: 12px; }', '.two { grid-template-columns: repeat(2, minmax(0, 1fr)); margin-bottom: 12px; }\n.three { grid-template-columns: repeat(3, minmax(0, 1fr)); margin-bottom: 12px; }') + changed.append('three-column grid CSS added') + if '.resource-mini-grid' not in css: + css += ''' + +.resource-mini-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 7px; margin-bottom: 10px; } +.resource-mini-grid div { display: grid; gap: 3px; padding: 8px; border: 1px solid var(--line); border-radius: 7px; background: var(--panel-2); } +.resource-mini-grid span { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .04em; } +.resource-mini-grid b { font-size: 14px; overflow-wrap: anywhere; } +.boot-order-list { display: grid; gap: 8px; } +.boot-order-item { display: flex; align-items: center; gap: 10px; padding: 9px; border: 1px solid var(--line); border-radius: 7px; background: var(--panel-2); cursor: grab; } +.boot-order-item:active { cursor: grabbing; } +.boot-order-item.dragging { opacity: .55; border-color: var(--accent); } +.boot-order-item small { display: block; color: var(--muted); font-size: 12px; margin-top: 2px; } +.drag-handle { color: var(--muted); font-weight: 900; } +@media (max-width: 1180px) { .three { grid-template-columns: 1fr; } .resource-mini-grid { grid-template-columns: 1fr; } } +''' + changed.append('resource and boot-order CSS added') + if css != original_css: + css_path.write_text(css) + +print('VM detail resource layout patch applied:') +for item in changed or ['already applied']: + print(f'- {item}')