#!/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') 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(): print(f'VM detail resource layout patch skipped: vm_detail.html not found: {template_path}') raise SystemExit(0) 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 before ISO messages') else: # Safe fallback: place messages after header/topbar area if exact ISO marker is not present. marker = ' ', '
', '
', '
', ] inserted = False for marker in candidate_markers: pos = tpl.find(marker) if pos == -1: continue if 'vm-boot-iso-grid' in marker: tpl = tpl.replace(marker, '
', 1) grid_pos = tpl.find('vm-resource-boot-iso-grid') insert_at = tpl.find('\n
', grid_pos) if insert_at != -1: tpl = tpl[:insert_at] + '\n' + resource_card + tpl[insert_at:] else: close_at = tpl.find('
', grid_pos) if close_at != -1: tpl = tpl[:close_at] + resource_card + tpl[close_at:] changed.append('resource card inserted into existing settings grid') inserted = True break elif marker == '
': settings = '\n\n
\n' + resource_card + '
\n' tpl = tpl.replace('\n\n' + marker, settings + '\n\n' + marker, 1) changed.append('resource grid inserted before first two-column grid') inserted = True break else: settings = '\n\n
\n' + resource_card + '
\n' tpl = tpl[:pos] + settings + tpl[pos:] changed.append('resource grid inserted before first card fallback') inserted = True break if not inserted: changed.append('resource card skipped: no safe layout marker') else: changed.append('resource card already present') # Upgrade older two-column settings grid when present. tpl = tpl.replace('class="grid two vm-boot-iso-grid"', 'class="grid three vm-resource-boot-iso-grid"') if tpl != original: template_path.write_text(tpl) if css_path.exists(): css = css_path.read_text() original_css = css if '.three {' not in css: if '.two {' 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; }') if '.three {' not in css: css += '\n.three { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; }\n' else: css += '\n.three { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; margin-bottom: 12px; }\n' 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) else: changed.append('CSS skipped: app.css not found') print('VM detail resource layout patch applied:') for item in changed or ['already applied']: print(f'- {item}')