virtui-manager 1.1.5__py3-none-any.whl → 1.3.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.
Files changed (57) hide show
  1. {virtui_manager-1.1.5.dist-info → virtui_manager-1.3.0.dist-info}/METADATA +1 -1
  2. virtui_manager-1.3.0.dist-info/RECORD +73 -0
  3. vmanager/constants.py +737 -108
  4. vmanager/dialog.css +24 -0
  5. vmanager/firmware_manager.py +4 -1
  6. vmanager/i18n.py +32 -0
  7. vmanager/libvirt_utils.py +132 -3
  8. vmanager/locales/de/LC_MESSAGES/virtui-manager.po +3012 -0
  9. vmanager/locales/fr/LC_MESSAGES/virtui-manager.mo +0 -0
  10. vmanager/locales/fr/LC_MESSAGES/virtui-manager.po +3124 -0
  11. vmanager/locales/it/LC_MESSAGES/virtui-manager.po +3012 -0
  12. vmanager/locales/virtui-manager.pot +3012 -0
  13. vmanager/modals/bulk_modals.py +13 -12
  14. vmanager/modals/cache_stats_modal.py +6 -5
  15. vmanager/modals/capabilities_modal.py +133 -0
  16. vmanager/modals/config_modal.py +25 -24
  17. vmanager/modals/cpu_mem_pc_modals.py +22 -21
  18. vmanager/modals/custom_migration_modal.py +10 -9
  19. vmanager/modals/disk_pool_modals.py +60 -59
  20. vmanager/modals/host_dashboard_modal.py +137 -0
  21. vmanager/modals/howto_disk_modal.py +13 -72
  22. vmanager/modals/howto_network_modal.py +13 -39
  23. vmanager/modals/howto_overlay_modal.py +13 -52
  24. vmanager/modals/howto_ssh_modal.py +12 -67
  25. vmanager/modals/howto_virtiofs_modal.py +13 -64
  26. vmanager/modals/input_modals.py +11 -10
  27. vmanager/modals/log_modal.py +2 -1
  28. vmanager/modals/migration_modals.py +20 -18
  29. vmanager/modals/network_modals.py +45 -36
  30. vmanager/modals/provisioning_modals.py +56 -56
  31. vmanager/modals/select_server_modals.py +8 -7
  32. vmanager/modals/selection_modals.py +7 -6
  33. vmanager/modals/server_modals.py +24 -23
  34. vmanager/modals/server_prefs_modals.py +103 -87
  35. vmanager/modals/utils_modals.py +10 -9
  36. vmanager/modals/virsh_modals.py +3 -2
  37. vmanager/modals/virtiofs_modals.py +6 -5
  38. vmanager/modals/vm_type_info_modal.py +2 -1
  39. vmanager/modals/vmanager_modals.py +19 -19
  40. vmanager/modals/vmcard_dialog.py +57 -57
  41. vmanager/modals/vmdetails_modals.py +115 -123
  42. vmanager/modals/xml_modals.py +3 -2
  43. vmanager/network_manager.py +4 -1
  44. vmanager/storage_manager.py +182 -42
  45. vmanager/utils.py +39 -6
  46. vmanager/vm_actions.py +28 -24
  47. vmanager/vm_queries.py +67 -25
  48. vmanager/vm_service.py +8 -5
  49. vmanager/vmanager.css +46 -0
  50. vmanager/vmanager.py +178 -112
  51. vmanager/vmcard.py +161 -159
  52. vmanager/webconsole_manager.py +21 -21
  53. virtui_manager-1.1.5.dist-info/RECORD +0 -65
  54. {virtui_manager-1.1.5.dist-info → virtui_manager-1.3.0.dist-info}/WHEEL +0 -0
  55. {virtui_manager-1.1.5.dist-info → virtui_manager-1.3.0.dist-info}/entry_points.txt +0 -0
  56. {virtui_manager-1.1.5.dist-info → virtui_manager-1.3.0.dist-info}/licenses/LICENSE +0 -0
  57. {virtui_manager-1.1.5.dist-info → virtui_manager-1.3.0.dist-info}/top_level.txt +0 -0
vmanager/dialog.css CHANGED
@@ -11,6 +11,7 @@ SelectOneServerModal,
11
11
  ConfirmationDialog,
12
12
  ChangeNetworkDialog,
13
13
  XMLDisplayModal,
14
+ CapabilitiesTreeModal,
14
15
  SelectDiskModal,
15
16
  AddDiskModal,
16
17
  EditDiskModal,
@@ -227,6 +228,7 @@ AddDiskModal,
227
228
  EditDiskModal,
228
229
  WebConsoleDialog,
229
230
  XMLDisplayModal,
231
+ CapabilitiesTreeModal,
230
232
  MigrationModal,
231
233
  MoveVolumeModal,
232
234
  SelectDiskModal > #dialog,
@@ -586,3 +588,25 @@ Button:hover {
586
588
  Button.-active {
587
589
  background: $primary-darken-2;
588
590
  }
591
+
592
+ /* === Capabilities Tree Modal === */
593
+ CapabilitiesTreeModal > #capabilities-dialog {
594
+ width: 90%;
595
+ height: 90%;
596
+ border: round $primary;
597
+ background: $surface;
598
+ padding: 1;
599
+ }
600
+
601
+ CapabilitiesTreeModal #xml-tree {
602
+ height: 80%;
603
+ border: round $primary;
604
+ margin-bottom: 1;
605
+ }
606
+
607
+ CapabilitiesTreeModal #dialog-title {
608
+ width: 100%;
609
+ content-align: center middle;
610
+ margin-bottom: 1;
611
+ text-style: bold;
612
+ }
@@ -6,6 +6,7 @@ import json
6
6
  import libvirt
7
7
  import xml.etree.ElementTree as ET
8
8
  from .utils import log_function_call
9
+ from .libvirt_utils import get_host_domain_capabilities
9
10
 
10
11
  FIRMWARE_META_BASE_DIR = "/usr/share/qemu/firmware/"
11
12
 
@@ -92,7 +93,9 @@ def get_host_sev_capabilities(conn):
92
93
  if conn is None:
93
94
  return sev_caps
94
95
  try:
95
- caps_xml = conn.getCapabilities()
96
+ caps_xml = get_host_domain_capabilities(conn)
97
+ if not caps_xml:
98
+ return sev_caps
96
99
  root = ET.fromstring(caps_xml)
97
100
  sev_elem = root.find('.//host/cpu/sev')
98
101
  if sev_elem is not None:
vmanager/i18n.py ADDED
@@ -0,0 +1,32 @@
1
+ import gettext
2
+ import os
3
+ import locale
4
+ import logging
5
+
6
+ # Setup path to locales
7
+ # This assumes the 'locales' directory is next to this file
8
+ LOCALE_DIR = os.path.join(os.path.dirname(__file__), 'locales')
9
+ DOMAIN = 'virtui-manager'
10
+
11
+ _ = lambda s: s
12
+
13
+ def setup_i18n():
14
+ global _
15
+ try:
16
+ lang, _ = locale.getdefaultlocale()
17
+ except Exception:
18
+ lang = 'en'
19
+
20
+ if not lang:
21
+ lang = 'en'
22
+
23
+ try:
24
+ t = gettext.translation(DOMAIN, LOCALE_DIR, fallback=True)
25
+ _ = t.gettext
26
+ logging.debug(f"i18n initialized for domain '{DOMAIN}' with locale dir '{LOCALE_DIR}'")
27
+ except Exception as e:
28
+ logging.warning(f"Failed to initialize i18n: {e}")
29
+ _ = lambda s: s
30
+
31
+ import sys
32
+ setup_i18n()
vmanager/libvirt_utils.py CHANGED
@@ -130,13 +130,126 @@ def get_cpu_models(conn: libvirt.virConnect, arch: str):
130
130
  print(f"Error getting CPU models for arch {arch}: {e}")
131
131
  return []
132
132
 
133
+ def get_host_resources(conn: libvirt.virConnect) -> dict:
134
+ """
135
+ Retrieves host resource information (CPU, Memory).
136
+ """
137
+ try:
138
+ node_info = conn.getInfo()
139
+ # node_info: [model, memory (KB), cpus, mhz, nodes, sockets, cores, threads]
140
+ mem_stats = conn.getMemoryStats(libvirt.VIR_NODE_MEMORY_STATS_ALL_CELLS)
141
+ # mem_stats might have: total, free, buffers, cached
142
+ host_info = {
143
+ 'model': node_info[0],
144
+ 'total_memory': node_info[1] // 1024, # MB
145
+ 'total_cpus': node_info[2],
146
+ 'mhz': node_info[3],
147
+ 'nodes': node_info[4],
148
+ 'sockets': node_info[5],
149
+ 'cores': node_info[6],
150
+ 'threads': node_info[7],
151
+ 'free_memory': mem_stats.get('free', 0) // 1024, # MB
152
+ 'available_memory': mem_stats.get('total', 0) // 1024, # MB
153
+ }
154
+
155
+ if host_info['available_memory'] == 0:
156
+ host_info['available_memory'] = host_info['total_memory']
157
+
158
+ return host_info
159
+ except libvirt.libvirtError as e:
160
+ logging.error(f"Error getting host resources: {e}")
161
+ return {}
162
+
163
+ def get_total_vm_allocation(conn: libvirt.virConnect, progress_callback=None) -> dict:
164
+ """
165
+ Calculates total resource allocation across all running or paused VMs on a host.
166
+ """
167
+ total_memory = 0
168
+ total_vcpus = 0
169
+ active_memory = 0
170
+ active_vcpus = 0
171
+
172
+ try:
173
+ domains = conn.listAllDomains(0)
174
+ total_vms = len(domains)
175
+
176
+ for i, domain in enumerate(domains):
177
+ if progress_callback:
178
+ progress_callback(i + 1, total_vms)
179
+
180
+ try:
181
+ # domain.info() returns [state, maxMem, memory, nrVirtCpu, cpuTime]
182
+ # memory is in Kilobytes
183
+ info = domain.info()
184
+ state = info[0]
185
+ max_mem = info[1] # KB
186
+ n_cpus = info[3]
187
+
188
+ total_memory += max_mem
189
+ total_vcpus += n_cpus
190
+
191
+ if state in [libvirt.VIR_DOMAIN_RUNNING, libvirt.VIR_DOMAIN_PAUSED]:
192
+ active_memory += max_mem
193
+ active_vcpus += n_cpus
194
+
195
+ except libvirt.libvirtError:
196
+ continue
197
+
198
+ return {
199
+ 'total_allocated_memory': total_memory // 1024, # Convert to MB
200
+ 'total_allocated_vcpus': total_vcpus,
201
+ 'active_allocated_memory': active_memory // 1024, # Convert to MB
202
+ 'active_allocated_vcpus': active_vcpus,
203
+ }
204
+ except libvirt.libvirtError as e:
205
+ logging.error(f"Error calculating VM allocation: {e}")
206
+ return {}
207
+
208
+ def get_active_vm_allocation(conn: libvirt.virConnect, progress_callback=None) -> dict:
209
+ """
210
+ Calculates resource allocation for only active (running/paused) VMs.
211
+ More efficient than get_total_vm_allocation for large numbers of inactive VMs.
212
+ """
213
+ active_memory = 0
214
+ active_vcpus = 0
215
+
216
+ try:
217
+ # Only list active domains
218
+ domains = conn.listAllDomains(libvirt.VIR_CONNECT_LIST_DOMAINS_ACTIVE)
219
+ total_vms = len(domains)
220
+
221
+ for i, domain in enumerate(domains):
222
+ if progress_callback:
223
+ progress_callback(i + 1, total_vms)
224
+
225
+ try:
226
+ info = domain.info()
227
+ max_mem = info[1] # KB
228
+ n_cpus = info[3]
229
+
230
+ active_memory += max_mem
231
+ active_vcpus += n_cpus
232
+
233
+ except libvirt.libvirtError:
234
+ continue
235
+
236
+ return {
237
+ 'active_allocated_memory': active_memory // 1024, # Convert to MB
238
+ 'active_allocated_vcpus': active_vcpus,
239
+ }
240
+ except libvirt.libvirtError as e:
241
+ logging.error(f"Error calculating active VM allocation: {e}")
242
+ return {}
243
+
133
244
  def get_host_architecture(conn: libvirt.virConnect) -> str:
134
245
  """
135
246
  Returns the host architecture (e.g., 'x86_64', 'aarch64').
136
247
  """
137
248
  try:
138
249
  # getCapabilities returns an XML string describing the host capabilities
139
- caps_xml = conn.getCapabilities()
250
+ caps_xml = get_host_domain_capabilities(conn)
251
+ if not caps_xml:
252
+ return 'x86_64'
140
253
  root = ET.fromstring(caps_xml)
141
254
 
142
255
  arch = root.findtext('host/cpu/arch')
@@ -332,12 +445,14 @@ def get_host_numa_nodes(conn: libvirt.virConnect) -> int:
332
445
  """
333
446
  Returns the number of NUMA nodes on the host.
334
447
  """
448
+ caps_xml = get_host_domain_capabilities(conn)
449
+ if not caps_xml:
450
+ return 1
335
451
  try:
336
- caps_xml = conn.getCapabilities()
337
452
  root = ET.fromstring(caps_xml)
338
453
  cells = root.findall(".//host/topology/cells/cell")
339
454
  return len(cells) if cells else 1
340
- except (libvirt.libvirtError, ET.ParseError) as e:
455
+ except ET.ParseError as e:
341
456
  logging.error(f"Error getting host NUMA topology: {e}")
342
457
  return 1
343
458
 
@@ -439,3 +554,17 @@ def get_host_pci_devices(conn: libvirt.virConnect) -> list[dict]:
439
554
  except (libvirt.libvirtError, AttributeError) as e:
440
555
  logging.error(f"Error getting host PCI devices: {e}")
441
556
  return pci_devices
557
+
558
+ @lru_cache(maxsize=8)
559
+ def get_host_domain_capabilities(conn: libvirt.virConnect) -> str | None:
560
+ """
561
+ Get the host capabilities XML (which describes host and guest capabilities).
562
+ The result is cached per connection.
563
+ """
564
+ if not conn:
565
+ return None
566
+ try:
567
+ return conn.getCapabilities()
568
+ except libvirt.libvirtError as e:
569
+ logging.error(f"Error getting host capabilities: {e}")
570
+ return None