virtui-manager 1.3.0__py3-none-any.whl → 1.4.0__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.
- {virtui_manager-1.3.0.dist-info → virtui_manager-1.4.0.dist-info}/METADATA +1 -1
- {virtui_manager-1.3.0.dist-info → virtui_manager-1.4.0.dist-info}/RECORD +25 -22
- vmanager/constants.py +8 -6
- vmanager/locales/de/LC_MESSAGES/virtui-manager.mo +0 -0
- vmanager/locales/de/LC_MESSAGES/virtui-manager.po +1186 -1040
- vmanager/locales/fr/LC_MESSAGES/virtui-manager.mo +0 -0
- vmanager/locales/fr/LC_MESSAGES/virtui-manager.po +532 -501
- vmanager/locales/it/LC_MESSAGES/virtui-manager.mo +0 -0
- vmanager/locales/it/LC_MESSAGES/virtui-manager.po +1171 -1051
- vmanager/locales/virtui-manager.pot +520 -499
- vmanager/modals/host_stats.py +199 -0
- vmanager/modals/migration_modals.py +1 -1
- vmanager/modals/utils_modals.py +1 -1
- vmanager/utils.py +15 -1
- vmanager/vm_actions.py +20 -0
- vmanager/vm_migration.py +4 -1
- vmanager/vmanager.css +9 -1
- vmanager/vmanager.py +70 -9
- vmanager/vmcard.css +3 -1
- vmanager/vmcard.py +113 -50
- vmanager/webconsole_manager.py +1 -1
- {virtui_manager-1.3.0.dist-info → virtui_manager-1.4.0.dist-info}/WHEEL +0 -0
- {virtui_manager-1.3.0.dist-info → virtui_manager-1.4.0.dist-info}/entry_points.txt +0 -0
- {virtui_manager-1.3.0.dist-info → virtui_manager-1.4.0.dist-info}/licenses/LICENSE +0 -0
- {virtui_manager-1.3.0.dist-info → virtui_manager-1.4.0.dist-info}/top_level.txt +0 -0
vmanager/vmcard.py
CHANGED
|
@@ -28,7 +28,7 @@ from .vm_actions import (
|
|
|
28
28
|
clone_vm, rename_vm, create_vm_snapshot,
|
|
29
29
|
restore_vm_snapshot, delete_vm_snapshot,
|
|
30
30
|
create_external_overlay, commit_disk_changes,
|
|
31
|
-
discard_overlay, delete_vm
|
|
31
|
+
discard_overlay, delete_vm, hibernate_vm
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
from .vm_queries import (
|
|
@@ -51,6 +51,7 @@ from .utils import (
|
|
|
51
51
|
extract_server_name_from_uri,
|
|
52
52
|
generate_tooltip_markdown,
|
|
53
53
|
remote_viewer_cmd,
|
|
54
|
+
check_tmux,
|
|
54
55
|
)
|
|
55
56
|
from .constants import (
|
|
56
57
|
ButtonLabels, TabTitles, StatusText,
|
|
@@ -66,12 +67,14 @@ class VMCardActions(Static):
|
|
|
66
67
|
def compose(self):
|
|
67
68
|
self.card.ui["start"] = Button(ButtonLabels.START, id="start", variant="success")
|
|
68
69
|
self.card.ui["shutdown"] = Button(ButtonLabels.SHUTDOWN, id="shutdown", variant="primary")
|
|
70
|
+
self.card.ui["hibernate"] = Button(ButtonLabels.HIBERNATE_VM, id="hibernate", variant="primary")
|
|
69
71
|
self.card.ui["stop"] = Button(ButtonLabels.FORCE_OFF, id="stop", variant="error")
|
|
70
72
|
self.card.ui["pause"] = Button(ButtonLabels.PAUSE, id="pause", variant="primary")
|
|
71
73
|
self.card.ui["resume"] = Button(ButtonLabels.RESUME, id="resume", variant="success")
|
|
72
74
|
self.card.ui["configure-button"] = Button(ButtonLabels.CONFIGURE, id="configure-button", variant="primary")
|
|
73
75
|
self.card.ui["web_console"] = Button(ButtonLabels.WEB_CONSOLE, id="web_console", variant="default")
|
|
74
76
|
self.card.ui["connect"] = Button(ButtonLabels.CONNECT, id="connect", variant="default")
|
|
77
|
+
self.card.ui["tmux_console"] = Button(ButtonLabels.TEXT_CONSOLE, id="tmux_console", variant="default")
|
|
75
78
|
|
|
76
79
|
self.card.ui["snapshot_take"] = Button(ButtonLabels.SNAPSHOT, id="snapshot_take", variant="primary")
|
|
77
80
|
self.card.ui["snapshot_restore"] = Button(ButtonLabels.RESTORE_SNAPSHOT, id="snapshot_restore", variant="primary")
|
|
@@ -100,16 +103,18 @@ class VMCardActions(Static):
|
|
|
100
103
|
yield self.card.ui["pause"]
|
|
101
104
|
yield self.card.ui["resume"]
|
|
102
105
|
with Vertical():
|
|
103
|
-
yield self.card.ui["configure-button"]
|
|
104
106
|
yield self.card.ui["web_console"]
|
|
105
107
|
yield self.card.ui["connect"]
|
|
106
|
-
|
|
108
|
+
if os.environ.get("TMUX") and check_tmux():
|
|
109
|
+
yield self.card.ui["tmux_console"]
|
|
110
|
+
with TabPane(TabTitles.STATE_MANAGEMENT, id="snapshot-tab"):
|
|
107
111
|
with Horizontal():
|
|
108
112
|
with Vertical():
|
|
109
113
|
yield self.card.ui["snapshot_take"]
|
|
110
114
|
yield self.card.ui["snapshot_restore"]
|
|
111
115
|
yield self.card.ui["snapshot_delete"]
|
|
112
116
|
with Vertical():
|
|
117
|
+
yield self.card.ui["hibernate"]
|
|
113
118
|
yield self.card.ui["create_overlay"]
|
|
114
119
|
yield self.card.ui["commit_disk"]
|
|
115
120
|
yield self.card.ui["discard_overlay"]
|
|
@@ -122,6 +127,7 @@ class VMCardActions(Static):
|
|
|
122
127
|
yield self.card.ui["clone"]
|
|
123
128
|
yield self.card.ui["migration"]
|
|
124
129
|
with Vertical():
|
|
130
|
+
yield self.card.ui["configure-button"]
|
|
125
131
|
yield self.card.ui["xml"]
|
|
126
132
|
yield Static(classes="button-separator")
|
|
127
133
|
yield self.card.ui["rename-button"]
|
|
@@ -184,21 +190,19 @@ class VMCard(Static):
|
|
|
184
190
|
def _get_snapshot_tab_title(self, num_snapshots: int = -1) -> str:
|
|
185
191
|
"""Get snapshot tab title. Pass num_snapshots to avoid blocking libvirt call."""
|
|
186
192
|
if num_snapshots == -1:
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
193
|
+
# If no count provided, don't fetch it here to avoid blocking.
|
|
194
|
+
# For now, return default if we can't get it cheaply.
|
|
195
|
+
return TabTitles.SNAP_OVER_UPDATE
|
|
190
196
|
|
|
191
197
|
if self.vm:
|
|
192
198
|
try:
|
|
193
|
-
if num_snapshots
|
|
194
|
-
return TabTitles.
|
|
195
|
-
|
|
196
|
-
return TabTitles.
|
|
197
|
-
elif num_snapshots >= 2:
|
|
198
|
-
return TabTitles.SNAPSHOTS + "(" + str(num_snapshots) + ")" "/" + TabTitles.OVERLAY
|
|
199
|
+
if num_snapshots <= 0:
|
|
200
|
+
return TabTitles.STATE_MANAGEMENT
|
|
201
|
+
else:
|
|
202
|
+
return f"{TabTitles.STATE_MANAGEMENT}({num_snapshots})"
|
|
199
203
|
except libvirt.libvirtError:
|
|
200
204
|
pass # Domain might be transient or invalid
|
|
201
|
-
return TabTitles.
|
|
205
|
+
return TabTitles.STATE_MANAGEMENT
|
|
202
206
|
|
|
203
207
|
def update_snapshot_tab_title(self, num_snapshots: int = -1) -> None:
|
|
204
208
|
"""Updates the snapshot tab title."""
|
|
@@ -342,7 +346,7 @@ class VMCard(Static):
|
|
|
342
346
|
"""Updates the tooltip for the VM name using Markdown."""
|
|
343
347
|
if not self.display or not self.ui or "vmname" not in self.ui:
|
|
344
348
|
return
|
|
345
|
-
|
|
349
|
+
|
|
346
350
|
uuid = self.internal_id
|
|
347
351
|
if not uuid:
|
|
348
352
|
return
|
|
@@ -388,7 +392,7 @@ class VMCard(Static):
|
|
|
388
392
|
if ips:
|
|
389
393
|
ip_display = ", ".join(ips)
|
|
390
394
|
|
|
391
|
-
cpu_model_display = f"
|
|
395
|
+
cpu_model_display = f" {self.cpu_model}" if self.cpu_model else ""
|
|
392
396
|
|
|
393
397
|
tooltip_md = generate_tooltip_markdown(
|
|
394
398
|
uuid=uuid_display,
|
|
@@ -397,7 +401,7 @@ class VMCard(Static):
|
|
|
397
401
|
ip=ip_display,
|
|
398
402
|
boot=self.boot_device or "N/A",
|
|
399
403
|
cpu=self.cpu,
|
|
400
|
-
cpu_model=
|
|
404
|
+
cpu_model=cpu_model_display or "",
|
|
401
405
|
memory=self.memory
|
|
402
406
|
)
|
|
403
407
|
|
|
@@ -576,8 +580,7 @@ class VMCard(Static):
|
|
|
576
580
|
logging.warning(f"Could not find #info-container on VMCard {self.name} when switching to detailed view.")
|
|
577
581
|
except Exception as e:
|
|
578
582
|
# Catch-all for potential mounting errors (e.g. already mounted elsewhere?)
|
|
579
|
-
|
|
580
|
-
|
|
583
|
+
logging.warning(f"Error restoring collapsible in detailed view: {e}")
|
|
581
584
|
|
|
582
585
|
# Ensure sparklines visibility is correct
|
|
583
586
|
self.watch_stats_view_mode(self.stats_view_mode, self.stats_view_mode)
|
|
@@ -890,6 +893,7 @@ class VMCard(Static):
|
|
|
890
893
|
|
|
891
894
|
self.ui["start"].display = is_stopped
|
|
892
895
|
self.ui["shutdown"].display = is_running or is_blocked
|
|
896
|
+
self.ui["hibernate"].display = is_running or is_blocked
|
|
893
897
|
self.ui["stop"].display = is_running or is_paused or is_pmsuspended or is_blocked
|
|
894
898
|
self.ui["delete"].display = is_running or is_paused or is_stopped or is_pmsuspended or is_blocked
|
|
895
899
|
self.ui["clone"].display = is_stopped
|
|
@@ -993,7 +997,7 @@ class VMCard(Static):
|
|
|
993
997
|
|
|
994
998
|
def update_ui():
|
|
995
999
|
self._update_slow_buttons(snapshot_summary, has_overlay)
|
|
996
|
-
|
|
1000
|
+
|
|
997
1001
|
try:
|
|
998
1002
|
self.app.call_from_thread(update_ui)
|
|
999
1003
|
except RuntimeError:
|
|
@@ -1065,12 +1069,14 @@ class VMCard(Static):
|
|
|
1065
1069
|
|
|
1066
1070
|
button_handlers = {
|
|
1067
1071
|
"shutdown": self._handle_shutdown_button,
|
|
1072
|
+
"hibernate": self._handle_hibernate_button,
|
|
1068
1073
|
"stop": self._handle_stop_button,
|
|
1069
1074
|
"pause": self._handle_pause_button,
|
|
1070
1075
|
"resume": self._handle_resume_button,
|
|
1071
1076
|
"xml": self._handle_xml_button,
|
|
1072
1077
|
"connect": self._handle_connect_button,
|
|
1073
1078
|
"web_console": self._handle_web_console_button,
|
|
1079
|
+
"tmux_console": self._handle_tmux_console_button,
|
|
1074
1080
|
"snapshot_take": self._handle_snapshot_take_button,
|
|
1075
1081
|
"snapshot_restore": self._handle_snapshot_restore_button,
|
|
1076
1082
|
"snapshot_delete": self._handle_snapshot_delete_button,
|
|
@@ -1243,6 +1249,27 @@ class VMCard(Static):
|
|
|
1243
1249
|
if self.status in (StatusText.RUNNING, StatusText.PAUSED):
|
|
1244
1250
|
self.post_message(VmActionRequest(self.internal_id, VmAction.STOP))
|
|
1245
1251
|
|
|
1252
|
+
def _handle_hibernate_button(self, event: Button.Pressed) -> None:
|
|
1253
|
+
"""Handles the save button press."""
|
|
1254
|
+
logging.info(f"Attempting to save (hibernate) VM: {self.name}")
|
|
1255
|
+
|
|
1256
|
+
def do_save():
|
|
1257
|
+
self.stop_background_activities()
|
|
1258
|
+
self.app.vm_service.suppress_vm_events(self.internal_id)
|
|
1259
|
+
try:
|
|
1260
|
+
hibernate_vm(self.vm)
|
|
1261
|
+
self.app.call_from_thread(self.app.show_success_message, SuccessMessages.VM_SAVED_TEMPLATE.format(vm_name=self.name))
|
|
1262
|
+
self.app.vm_service.invalidate_vm_state_cache(self.internal_id)
|
|
1263
|
+
self.app.call_from_thread(setattr, self, 'status', StatusText.STOPPED)
|
|
1264
|
+
self.app.call_from_thread(self.update_button_layout)
|
|
1265
|
+
except Exception as e:
|
|
1266
|
+
self.app.call_from_thread(self.app.show_error_message, ErrorMessages.ERROR_ON_VM_DURING_ACTION.format(vm_name=self.name, action='save', error=e))
|
|
1267
|
+
finally:
|
|
1268
|
+
self.app.vm_service.unsuppress_vm_events(self.internal_id)
|
|
1269
|
+
|
|
1270
|
+
if self.status in (StatusText.RUNNING, StatusText.PAUSED):
|
|
1271
|
+
self.app.worker_manager.run(do_save, name=f"save_{self.internal_id}")
|
|
1272
|
+
|
|
1246
1273
|
def stop_background_activities(self):
|
|
1247
1274
|
""" Stop background activities before action """
|
|
1248
1275
|
with self._timer_lock:
|
|
@@ -1324,7 +1351,7 @@ class VMCard(Static):
|
|
|
1324
1351
|
self.app.refresh_vm_list()
|
|
1325
1352
|
except libvirt.libvirtError as e:
|
|
1326
1353
|
self.app.show_error_message(ErrorMessages.INVALID_XML_TEMPLATE.format(vm_name=self.name, error=e))
|
|
1327
|
-
logging.error(
|
|
1354
|
+
logging.error(e)
|
|
1328
1355
|
else:
|
|
1329
1356
|
self.app.show_success_message(SuccessMessages.NO_XML_CHANGES)
|
|
1330
1357
|
|
|
@@ -1430,6 +1457,50 @@ class VMCard(Static):
|
|
|
1430
1457
|
else:
|
|
1431
1458
|
self.app.worker_manager.run(worker, name=f"start_console_{self.vm.name()}")
|
|
1432
1459
|
|
|
1460
|
+
def _handle_tmux_console_button(self, event: Button.Pressed) -> None:
|
|
1461
|
+
"""Handles the text console button press by opening a new tmux window."""
|
|
1462
|
+
logging.info(f"Attempting to open text console for VM: {self.name}")
|
|
1463
|
+
|
|
1464
|
+
# Check if running in tmux
|
|
1465
|
+
if not os.environ.get("TMUX"):
|
|
1466
|
+
self.app.show_error_message("This feature requires running inside tmux.")
|
|
1467
|
+
return
|
|
1468
|
+
|
|
1469
|
+
try:
|
|
1470
|
+
# Use cached values to avoid libvirt calls where possible
|
|
1471
|
+
uri = self.app.vm_service.get_uri_for_connection(self.conn)
|
|
1472
|
+
if not uri:
|
|
1473
|
+
uri = self.conn.getURI()
|
|
1474
|
+
|
|
1475
|
+
# Get proper domain name
|
|
1476
|
+
_, domain_name = self.app.vm_service.get_vm_identity(self.vm, self.conn)
|
|
1477
|
+
|
|
1478
|
+
# Construct command
|
|
1479
|
+
# tmux new-window -n "Console: <vm_name>" "virsh -c <uri> console <vm_name>; read"
|
|
1480
|
+
help_msg = (
|
|
1481
|
+
"echo '---------------------------------------------------------'; "
|
|
1482
|
+
"echo 'Tmux Navigation Help:'; "
|
|
1483
|
+
"echo ' Ctrl+B N or P - Move to the next or previous window.'; "
|
|
1484
|
+
"echo ' Ctrl+B W - Open a panel to navigate across windows in multiple sessions.'; "
|
|
1485
|
+
"echo ' Ctrl+] - Close the current view.'; "
|
|
1486
|
+
"echo ' Ctrl+B ? - View all keybindings. Press Q to exit.';"
|
|
1487
|
+
"echo '---------------------------------------------------------'; "
|
|
1488
|
+
"echo 'Starting console...'; sleep 1;"
|
|
1489
|
+
)
|
|
1490
|
+
cmd = [
|
|
1491
|
+
"tmux", "new-window",
|
|
1492
|
+
"-n", f"{domain_name}",
|
|
1493
|
+
f"{help_msg} virsh -c {uri} console {domain_name}; echo '\nConsole session ended. Press Enter to close window.'; read"
|
|
1494
|
+
]
|
|
1495
|
+
|
|
1496
|
+
logging.info(f"Launching tmux console: {' '.join(cmd)}")
|
|
1497
|
+
subprocess.Popen(cmd)
|
|
1498
|
+
self.app.show_quick_message(f"Opened console for {domain_name}")
|
|
1499
|
+
|
|
1500
|
+
except Exception as e:
|
|
1501
|
+
logging.error(f"Failed to open tmux console: {e}")
|
|
1502
|
+
self.app.show_error_message(f"Failed to open console: {e}")
|
|
1503
|
+
|
|
1433
1504
|
def _handle_snapshot_take_button(self, event: Button.Pressed) -> None:
|
|
1434
1505
|
"""Handles the snapshot take button press."""
|
|
1435
1506
|
logging.info(f"Attempting to take snapshot for VM: {self.name}")
|
|
@@ -1922,8 +1993,6 @@ class VMCard(Static):
|
|
|
1922
1993
|
finally:
|
|
1923
1994
|
self.app.vm_service.unsuppress_vm_events(internal_id)
|
|
1924
1995
|
|
|
1925
|
-
num_snapshots = self.vm.snapshotNum(0)
|
|
1926
|
-
|
|
1927
1996
|
def on_confirm_rename(confirmed: bool, delete_snapshots=False) -> None:
|
|
1928
1997
|
if confirmed:
|
|
1929
1998
|
do_rename()
|
|
@@ -1960,10 +2029,10 @@ class VMCard(Static):
|
|
|
1960
2029
|
def get_details_worker():
|
|
1961
2030
|
try:
|
|
1962
2031
|
result = self.app.vm_service.get_vm_details(
|
|
1963
|
-
active_uris,
|
|
1964
|
-
uuid,
|
|
1965
|
-
domain=vm_obj,
|
|
1966
|
-
conn=conn_obj,
|
|
2032
|
+
active_uris,
|
|
2033
|
+
uuid,
|
|
2034
|
+
domain=vm_obj,
|
|
2035
|
+
conn=conn_obj,
|
|
1967
2036
|
cached_ips=cached_ips
|
|
1968
2037
|
)
|
|
1969
2038
|
|
|
@@ -1975,22 +2044,27 @@ class VMCard(Static):
|
|
|
1975
2044
|
|
|
1976
2045
|
vm_info, domain, conn_for_domain = result
|
|
1977
2046
|
|
|
1978
|
-
def on_detail_modal_dismissed(
|
|
2047
|
+
def on_detail_modal_dismissed(_=None):
|
|
1979
2048
|
self.post_message(VmCardUpdateRequest(self.internal_id))
|
|
1980
2049
|
self._perform_tooltip_update()
|
|
1981
2050
|
|
|
1982
2051
|
self.app.push_screen(
|
|
1983
|
-
VMDetailModal(
|
|
2052
|
+
VMDetailModal(
|
|
2053
|
+
vm_name,
|
|
2054
|
+
vm_info,
|
|
2055
|
+
domain,
|
|
2056
|
+
conn_for_domain,
|
|
2057
|
+
self.app.vm_service.invalidate_vm_state_cache),
|
|
1984
2058
|
on_detail_modal_dismissed
|
|
1985
|
-
|
|
2059
|
+
)
|
|
1986
2060
|
|
|
1987
2061
|
self.app.call_from_thread(show_details)
|
|
1988
2062
|
|
|
1989
2063
|
except Exception as e:
|
|
1990
|
-
def show_error():
|
|
2064
|
+
def show_error(error_instance):
|
|
1991
2065
|
loading_modal.dismiss()
|
|
1992
|
-
self.app.show_error_message(ErrorMessages.ERROR_GETTING_VM_DETAILS_TEMPLATE.format(vm_name=vm_name, error=
|
|
1993
|
-
self.app.call_from_thread(show_error)
|
|
2066
|
+
self.app.show_error_message(ErrorMessages.ERROR_GETTING_VM_DETAILS_TEMPLATE.format(vm_name=vm_name, error=error_instance))
|
|
2067
|
+
self.app.call_from_thread(show_error, e)
|
|
1994
2068
|
|
|
1995
2069
|
self.app.worker_manager.run(get_details_worker, name=f"get_details_{uuid}")
|
|
1996
2070
|
|
|
@@ -2003,27 +2077,16 @@ class VMCard(Static):
|
|
|
2003
2077
|
self.app.show_error_message(ErrorMessages.SELECT_AT_LEAST_TWO_SERVERS_FOR_MIGRATION)
|
|
2004
2078
|
return
|
|
2005
2079
|
|
|
2006
|
-
selected_vm_uuids = self.app.selected_vm_uuids
|
|
2080
|
+
selected_vm_uuids = list(self.app.selected_vm_uuids)
|
|
2007
2081
|
selected_vms = []
|
|
2008
2082
|
if selected_vm_uuids:
|
|
2083
|
+
found_domains_dict = self.app.vm_service.find_domains_by_uuids(self.app.active_uris, selected_vm_uuids)
|
|
2009
2084
|
for uuid in selected_vm_uuids:
|
|
2010
|
-
|
|
2011
|
-
with self.app.vm_service._cache_lock:
|
|
2012
|
-
domain = self.app.vm_service._domain_cache.get(uuid)
|
|
2013
|
-
|
|
2085
|
+
domain = found_domains_dict.get(uuid)
|
|
2014
2086
|
if domain:
|
|
2015
|
-
|
|
2016
|
-
# Verify domain is still valid
|
|
2017
|
-
domain.info()
|
|
2018
|
-
selected_vms.append(domain)
|
|
2019
|
-
found_domain = True
|
|
2020
|
-
except libvirt.libvirtError:
|
|
2021
|
-
found_domain = False
|
|
2087
|
+
selected_vms.append(domain)
|
|
2022
2088
|
else:
|
|
2023
|
-
|
|
2024
|
-
if not vm_info:
|
|
2025
|
-
self.app.show_error_message(ErrorMessages.SELECTED_VM_NOT_FOUND_ON_ACTIVE_SERVER_TEMPLATE.format(uuid=uuid))
|
|
2026
|
-
|
|
2089
|
+
self.app.show_error_message(ErrorMessages.SELECTED_VM_NOT_FOUND_ON_ACTIVE_SERVER_TEMPLATE.format(uuid=uuid))
|
|
2027
2090
|
if not selected_vms:
|
|
2028
2091
|
selected_vms = [self.vm]
|
|
2029
2092
|
|
|
@@ -2053,7 +2116,7 @@ class VMCard(Static):
|
|
|
2053
2116
|
state, _ = state_tuple
|
|
2054
2117
|
if state in [libvirt.VIR_DOMAIN_RUNNING, libvirt.VIR_DOMAIN_PAUSED]:
|
|
2055
2118
|
active_vms.append(vm)
|
|
2056
|
-
except:
|
|
2119
|
+
except Exception:
|
|
2057
2120
|
# Fallback to isActive() if cache lookup fails
|
|
2058
2121
|
if vm.isActive():
|
|
2059
2122
|
active_vms.append(vm)
|
|
@@ -2118,7 +2181,7 @@ class VMCard(Static):
|
|
|
2118
2181
|
try:
|
|
2119
2182
|
# Use vm_service to get XML (handles caching)
|
|
2120
2183
|
self.app.vm_service._get_domain_xml(self.vm, internal_id=self.internal_id)
|
|
2121
|
-
|
|
2184
|
+
|
|
2122
2185
|
# Update tooltip on main thread
|
|
2123
2186
|
self.app.call_from_thread(self._perform_tooltip_update)
|
|
2124
2187
|
self.app.call_from_thread(self.app.show_quick_message, f"Info refreshed for {self.name}")
|
vmanager/webconsole_manager.py
CHANGED
|
@@ -17,7 +17,7 @@ from urllib.parse import urlparse
|
|
|
17
17
|
|
|
18
18
|
import libvirt
|
|
19
19
|
|
|
20
|
-
from .constants import AppInfo
|
|
20
|
+
from .constants import AppInfo, ErrorMessages, SuccessMessages
|
|
21
21
|
from .events import VmCardUpdateRequest
|
|
22
22
|
from .config import load_config, get_log_path
|
|
23
23
|
from .vm_queries import get_vm_graphics_info
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|