virtui-manager 1.1.5__tar.gz → 1.1.6__tar.gz

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 (78) hide show
  1. {virtui_manager-1.1.5/src/virtui_manager.egg-info → virtui_manager-1.1.6}/PKG-INFO +1 -1
  2. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/pyproject.toml +1 -1
  3. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/setup.cfg +1 -1
  4. {virtui_manager-1.1.5 → virtui_manager-1.1.6/src/virtui_manager.egg-info}/PKG-INFO +1 -1
  5. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/virtui_manager.egg-info/SOURCES.txt +0 -1
  6. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/constants.py +1 -1
  7. virtui_manager-1.1.6/src/vmanager/modals/howto_disk_modal.py +33 -0
  8. virtui_manager-1.1.6/src/vmanager/modals/howto_network_modal.py +33 -0
  9. virtui_manager-1.1.6/src/vmanager/modals/howto_overlay_modal.py +33 -0
  10. virtui_manager-1.1.6/src/vmanager/modals/howto_ssh_modal.py +32 -0
  11. virtui_manager-1.1.6/src/vmanager/modals/howto_virtiofs_modal.py +33 -0
  12. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/server_prefs_modals.py +25 -16
  13. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/storage_manager.py +25 -3
  14. virtui_manager-1.1.5/FEATURES.md +0 -256
  15. virtui_manager-1.1.5/src/vmanager/modals/howto_disk_modal.py +0 -93
  16. virtui_manager-1.1.5/src/vmanager/modals/howto_network_modal.py +0 -60
  17. virtui_manager-1.1.5/src/vmanager/modals/howto_overlay_modal.py +0 -73
  18. virtui_manager-1.1.5/src/vmanager/modals/howto_ssh_modal.py +0 -88
  19. virtui_manager-1.1.5/src/vmanager/modals/howto_virtiofs_modal.py +0 -85
  20. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/LICENSE +0 -0
  21. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/MANIFEST.in +0 -0
  22. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/README.md +0 -0
  23. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/requirements.txt +0 -0
  24. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/virtui_manager.egg-info/dependency_links.txt +0 -0
  25. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/virtui_manager.egg-info/entry_points.txt +0 -0
  26. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/virtui_manager.egg-info/requires.txt +0 -0
  27. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/virtui_manager.egg-info/top_level.txt +0 -0
  28. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/__init__.py +0 -0
  29. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/config.py +0 -0
  30. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/connection_manager.py +0 -0
  31. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/dialog.css +0 -0
  32. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/events.py +0 -0
  33. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/firmware_manager.py +0 -0
  34. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/libvirt_error_handler.py +0 -0
  35. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/libvirt_utils.py +0 -0
  36. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/__init__.py +0 -0
  37. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/base_modals.py +0 -0
  38. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/bulk_modals.py +0 -0
  39. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/cache_stats_modal.py +0 -0
  40. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/config_modal.py +0 -0
  41. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/cpu_mem_pc_modals.py +0 -0
  42. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/custom_migration_modal.py +0 -0
  43. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/disk_pool_modals.py +0 -0
  44. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/input_modals.py +0 -0
  45. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/log_modal.py +0 -0
  46. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/migration_modals.py +0 -0
  47. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/network_modals.py +0 -0
  48. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/provisioning_modals.py +0 -0
  49. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/select_server_modals.py +0 -0
  50. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/selection_modals.py +0 -0
  51. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/server_modals.py +0 -0
  52. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/utils_modals.py +0 -0
  53. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/virsh_modals.py +0 -0
  54. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/virtiofs_modals.py +0 -0
  55. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/vm_type_info_modal.py +0 -0
  56. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/vmanager_modals.py +0 -0
  57. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/vmcard_dialog.py +0 -0
  58. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/vmdetails_modals.py +0 -0
  59. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/modals/xml_modals.py +0 -0
  60. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/network_manager.py +0 -0
  61. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/remote_viewer.py +0 -0
  62. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/remote_viewer_gtk4.py +0 -0
  63. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/utils.py +0 -0
  64. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/virtui_dev.py +0 -0
  65. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vm_actions.py +0 -0
  66. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vm_cache.py +0 -0
  67. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vm_migration.py +0 -0
  68. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vm_provisioner.py +0 -0
  69. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vm_queries.py +0 -0
  70. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vm_service.py +0 -0
  71. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vmanager.css +0 -0
  72. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vmanager.py +0 -0
  73. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vmanager_cmd.py +0 -0
  74. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vmcard.css +0 -0
  75. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vmcard.py +0 -0
  76. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/vmcard_pool.py +0 -0
  77. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/webconsole_manager.py +0 -0
  78. {virtui_manager-1.1.5 → virtui_manager-1.1.6}/src/vmanager/wrapper.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: virtui-manager
3
- Version: 1.1.5
3
+ Version: 1.1.6
4
4
  Summary: Terminal-based interface to manage virtual machines using libvirt
5
5
  Home-page: https://aginies.github.io/virtui-manager/
6
6
  Author: Antoine Ginies
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "virtui-manager"
7
- version = "1.1.5"
7
+ version = "1.1.6"
8
8
  description = "Terminal-based interface to manage virtual machines using libvirt"
9
9
  readme = "README.md"
10
10
  authors = [
@@ -1,6 +1,6 @@
1
1
  [metadata]
2
2
  name = virtui-manager
3
- version = 1.1.5
3
+ version = 1.1.6
4
4
  description = Terminal-based interface to manage virtual machines using libvirt
5
5
  long_description = file: README.md
6
6
  long_description_content_type = text/markdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: virtui-manager
3
- Version: 1.1.5
3
+ Version: 1.1.6
4
4
  Summary: Terminal-based interface to manage virtual machines using libvirt
5
5
  Home-page: https://aginies.github.io/virtui-manager/
6
6
  Author: Antoine Ginies
@@ -1,4 +1,3 @@
1
- FEATURES.md
2
1
  LICENSE
3
2
  MANIFEST.in
4
3
  README.md
@@ -37,7 +37,7 @@ class AppInfo:
37
37
  """Define app data"""
38
38
  name = "virtui-manager"
39
39
  namecase = "VirtUI Manager"
40
- version = "1.1.5"
40
+ version = "1.1.6"
41
41
 
42
42
  class VmAction:
43
43
  """Defines constants for VM action types."""
@@ -0,0 +1,33 @@
1
+ """
2
+ Modal to show how to manage VM disks.
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+ from textual.app import ComposeResult
7
+ from textual.containers import Vertical, Horizontal, ScrollableContainer
8
+ from textual.widgets import Button, Markdown
9
+ from textual import on
10
+ from .base_modals import BaseModal
11
+
12
+ class HowToDiskModal(BaseModal[None]):
13
+ """A modal to display instructions for managing VM disks."""
14
+
15
+ def compose(self) -> ComposeResult:
16
+ # Load markdown from external file
17
+ docs_path = Path(__file__).parent.parent / "appdocs" / "howto_disk.md"
18
+ try:
19
+ with open(docs_path, "r") as f:
20
+ content = f.read()
21
+ except FileNotFoundError:
22
+ content = "# Error: Documentation file not found."
23
+
24
+ with Vertical(id="howto-disk-dialog"):
25
+ with ScrollableContainer(id="howto-disk-content"):
26
+ yield Markdown(content, id="howto-disk-markdown")
27
+ with Horizontal(id="dialog-buttons"):
28
+ yield Button("Close", id="close-btn", variant="primary")
29
+
30
+ @on(Button.Pressed)
31
+ def on_button_pressed(self, event: Button.Pressed) -> None:
32
+ """Handle button presses."""
33
+ self.dismiss()
@@ -0,0 +1,33 @@
1
+ """
2
+ Modal to show how to configure networks.
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+ from textual.app import ComposeResult
7
+ from textual.containers import Vertical, Horizontal, ScrollableContainer
8
+ from textual.widgets import Button, Markdown
9
+ from textual import on
10
+ from .base_modals import BaseModal
11
+
12
+ class HowToNetworkModal(BaseModal[None]):
13
+ """A modal to display instructions for network configuration."""
14
+
15
+ def compose(self) -> ComposeResult:
16
+ # Load markdown from external file
17
+ docs_path = Path(__file__).parent.parent / "appdocs" / "howto_network.md"
18
+ try:
19
+ with open(docs_path, "r") as f:
20
+ content = f.read()
21
+ except FileNotFoundError:
22
+ content = "# Error: Documentation file not found."
23
+
24
+ with Vertical(id="howto-network-dialog"):
25
+ with ScrollableContainer(id="howto-network-content"):
26
+ yield Markdown(content, id="howto-network-markdown")
27
+ with Horizontal(id="dialog-buttons"):
28
+ yield Button("Close", id="close-btn", variant="primary")
29
+
30
+ @on(Button.Pressed)
31
+ def on_button_pressed(self, event: Button.Pressed) -> None:
32
+ """Handle button presses."""
33
+ self.dismiss()
@@ -0,0 +1,33 @@
1
+ """
2
+ Modal to show how overlay disks work.
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+ from textual.app import ComposeResult
7
+ from textual.containers import Vertical, Horizontal, ScrollableContainer
8
+ from textual.widgets import Button, Markdown
9
+ from textual import on
10
+ from .base_modals import BaseModal
11
+
12
+ class HowToOverlayModal(BaseModal[None]):
13
+ """A modal to display instructions for disk overlays."""
14
+
15
+ def compose(self) -> ComposeResult:
16
+ # Load markdown from external file
17
+ docs_path = Path(__file__).parent.parent / "appdocs" / "howto_overlay.md"
18
+ try:
19
+ with open(docs_path, "r") as f:
20
+ content = f.read()
21
+ except FileNotFoundError:
22
+ content = "# Error: Documentation file not found."
23
+
24
+ with Vertical(id="howto-overlay-dialog", classes="howto-dialog"):
25
+ with ScrollableContainer(id="howto-overlay-content"):
26
+ yield Markdown(content, id="howto-overlay-markdown")
27
+ with Horizontal(id="dialog-buttons"):
28
+ yield Button("Close", id="close-btn", variant="primary")
29
+
30
+ @on(Button.Pressed)
31
+ def on_button_pressed(self, event: Button.Pressed) -> None:
32
+ """Handle button presses."""
33
+ self.dismiss()
@@ -0,0 +1,32 @@
1
+ """
2
+ Modal to show how to use ssh-agent.
3
+ """
4
+ from pathlib import Path
5
+ from textual.app import ComposeResult
6
+ from textual.containers import Vertical, Horizontal, ScrollableContainer
7
+ from textual.widgets import Button, Markdown
8
+ from textual import on
9
+ from .base_modals import BaseModal
10
+
11
+ class HowToSSHModal(BaseModal[None]):
12
+ """A modal to display instructions for using an ssh-agent."""
13
+
14
+ def compose(self) -> ComposeResult:
15
+ # Load markdown from external file
16
+ docs_path = Path(__file__).parent.parent / "appdocs" / "howto_ssh.md"
17
+ try:
18
+ with open(docs_path, "r") as f:
19
+ content = f.read()
20
+ except FileNotFoundError:
21
+ content = "# Error: Documentation file not found."
22
+
23
+ with Vertical(id="howto-ssh-dialog"):
24
+ with ScrollableContainer(id="howto-ssh-content"):
25
+ yield Markdown(content, id="howto-ssh-markdown")
26
+ with Horizontal(id="dialog-buttons"):
27
+ yield Button("Close", id="close-btn", variant="primary")
28
+
29
+ @on(Button.Pressed)
30
+ def on_button_pressed(self, event: Button.Pressed) -> None:
31
+ """Handle button presses."""
32
+ self.dismiss()
@@ -0,0 +1,33 @@
1
+ """
2
+ Modal to show how to use VirtIO-FS.
3
+ """
4
+ import os
5
+ from pathlib import Path
6
+ from textual.app import ComposeResult
7
+ from textual.containers import Vertical, Horizontal, ScrollableContainer
8
+ from textual.widgets import Button, Markdown
9
+ from textual import on
10
+ from .base_modals import BaseModal
11
+
12
+ class HowToVirtIOFSModal(BaseModal[None]):
13
+ """A modal to display instructions for using VirtIO-FS."""
14
+
15
+ def compose(self) -> ComposeResult:
16
+ # Load markdown from external file
17
+ docs_path = Path(__file__).parent.parent / "appdocs" / "howto_virtiofs.md"
18
+ try:
19
+ with open(docs_path, "r") as f:
20
+ content = f.read()
21
+ except FileNotFoundError:
22
+ content = "# Error: Documentation file not found."
23
+
24
+ with Vertical(id="howto-virtiofs-dialog"):
25
+ with ScrollableContainer(id="howto-virtiofs-content"):
26
+ yield Markdown(content, id="howto-virtiofs-markdown")
27
+ with Horizontal(id="dialog-buttons"):
28
+ yield Button("Close", id="close-btn", variant="primary")
29
+
30
+ @on(Button.Pressed)
31
+ def on_button_pressed(self, event: Button.Pressed) -> None:
32
+ """Handle button presses."""
33
+ self.dismiss()
@@ -248,20 +248,28 @@ class ServerPrefModal(BaseModal[None]):
248
248
  pools = storage_manager.list_storage_pools(self.conn)
249
249
  node_to_select = None
250
250
  for pool_data in pools:
251
- pool_name = pool_data['name']
252
- status = pool_data['status']
253
- autostart = "autostart" if pool_data['autostart'] else "no autostart"
254
- label = f"{pool_name} [{status}, {autostart}]"
255
- pool_node = tree.root.add(label, data=pool_data)
256
- pool_node.data["type"] = "pool"
257
- # Add a dummy node to make the pool node expandable
258
- pool_node.add_leaf("Loading volumes...")
259
-
260
- if expand_pools and pool_name in expand_pools:
261
- self.app.call_later(pool_node.expand)
262
-
263
- if select_pool and pool_name == select_pool:
264
- node_to_select = pool_node
251
+ try:
252
+ pool_name = pool_data['name']
253
+ status = pool_data['status']
254
+ autostart = "autostart" if pool_data['autostart'] else "no autostart"
255
+ label = f"{pool_name} [{status}, {autostart}]"
256
+ pool_node = tree.root.add(label, data=pool_data)
257
+ pool_node.data["type"] = "pool"
258
+ # Add a dummy node to make the pool node expandable
259
+ pool_node.add_leaf("Loading volumes...")
260
+
261
+ if expand_pools and pool_name in expand_pools:
262
+ self.app.call_later(pool_node.expand)
263
+
264
+ if select_pool and pool_name == select_pool:
265
+ node_to_select = pool_node
266
+ except libvirt.libvirtError:
267
+ # Handle cases where pool might be listed but inaccessible (e.g. NFS down)
268
+ pool_name = pool_data.get('name', 'Unknown')
269
+ label = f"{pool_name} [unavailable]"
270
+ pool_node = tree.root.add(label, data=pool_data)
271
+ pool_node.data["type"] = "pool"
272
+ pool_node.add_leaf("Pool unavailable")
265
273
 
266
274
  if node_to_select:
267
275
  self.app.call_later(tree.select_node, node_to_select)
@@ -308,7 +316,8 @@ class ServerPrefModal(BaseModal[None]):
308
316
  if len(node.children) == 1 and node.children[0].data is None:
309
317
  node.remove_children()
310
318
  pool = node_data.get('pool')
311
- if pool and pool.isActive():
319
+ # Check cached status instead of calling pool.isActive() to avoid blocking
320
+ if pool and node_data.get('status') == 'active':
312
321
  volumes = storage_manager.list_storage_volumes(pool)
313
322
  for vol_data in volumes:
314
323
  vol_name = vol_data['name']
@@ -404,7 +413,7 @@ class ServerPrefModal(BaseModal[None]):
404
413
  self.app.show_error_message("Could not find pool object to edit.")
405
414
  return
406
415
 
407
- if pool.isActive():
416
+ if node_data.get('status') == 'active':
408
417
  self.app.show_error_message("Pool must be inactive to edit its XML definition.")
409
418
  return
410
419
 
@@ -29,18 +29,40 @@ def list_storage_pools(conn: libvirt.virConnect) -> List[Dict[str, Any]]:
29
29
  pools = conn.listAllStoragePools(0)
30
30
  for pool in pools:
31
31
  try:
32
+ # Try to get basic info
33
+ try:
34
+ name = pool.name()
35
+ except libvirt.libvirtError:
36
+ name = "Unknown Pool"
37
+
32
38
  is_active = pool.isActive()
33
39
  info = pool.info()
34
40
  pools_info.append({
35
- 'name': pool.name(),
41
+ 'name': name,
36
42
  'pool': pool,
37
43
  'status': 'active' if is_active else 'inactive',
38
44
  'autostart': pool.autostart() == 1,
39
45
  'capacity': info[1],
40
46
  'allocation': info[2],
41
47
  })
42
- except libvirt.libvirtError:
43
- continue
48
+ except libvirt.libvirtError as e:
49
+ # If we fail to get details (e.g. NFS down), still list the pool but as unavailable
50
+ if 'name' not in locals():
51
+ try:
52
+ name = pool.name()
53
+ except:
54
+ name = "Unknown Pool"
55
+
56
+ logging.warning(f"Failed to get details for pool '{name}': {e}")
57
+ pools_info.append({
58
+ 'name': name,
59
+ 'pool': pool,
60
+ 'status': 'unavailable',
61
+ 'autostart': False,
62
+ 'capacity': 0,
63
+ 'allocation': 0,
64
+ 'error': str(e)
65
+ })
44
66
  except libvirt.libvirtError:
45
67
  return []
46
68
 
@@ -1,256 +0,0 @@
1
- # Virtui Manager - Features
2
-
3
- ## Overview
4
- A Textual-based TUI (Terminal User Interface) application for managing QEMU/KVM virtual machines using the libvirt Python API. It provides a comprehensive interface for VM management with features that go beyond basic management.
5
-
6
- ## Main Interface Features
7
-
8
- ### Multi-server Management
9
- - Connect to multiple libvirt servers simultaneously
10
- - Transhypervisor view showing VMs from different servers
11
- - Server selection and management interface
12
-
13
- ### VM Grid Display
14
- - VMs displayed in a responsive grid layout (up to 5x3 = 15 VM)
15
- - Color-coded status indicators (Running, Paused, Stopped)
16
- - CPU, Memory, Disk, Net usage sparklines for running VMs
17
- - Pagination controls for large VM lists
18
-
19
- ### VM Management Actions
20
- - Start, Shutdown, Force Off (destroy), Pause, Resume
21
- - Delete VM with optional storage cleanup: Now more robust, always using libvirt API for managed storage volumes, preventing permission errors. Automatically deletes VM snapshot metadata.
22
- - Hide restore snapshot if the VM is running or loading
23
- - Clone VM functionality: Clone VMs with advanced options, including specifying a custom suffix, cloning multiple instances at once, and automatic creation of storage for the new clones.
24
- - Rename VM with snapshot handling
25
- - Take, restore, and delete VM snapshots
26
- - View/Edit XML configuration
27
- - Connect to VM via virt-viewer
28
- - Web console access via novnc (when available)
29
- - **Multi-session Support**: Support for multiple concurrent remote web console sessions via automatic port allocation/checking on the remote server.
30
- - When connecting to a remote libvirt server via SSH, the web console can be configured to run either locally (default) or directly on the remote server.
31
- - To enable running the web console on the remote server, set `REMOTE_WEBCONSOLE: True` in your `config.yaml`.
32
- - When `REMOTE_WEBCONSOLE` is enabled, `websockify` and `novnc` assets must be installed on the remote server at the paths specified in `config.yaml` (default: `/usr/bin/websockify` and `/usr/share/novnc/`).
33
- - For secure (HTTPS) remote web console access, `cert.pem` and `key.pem` files must also be present on the remote server in `~/.config/virtui-manager/`.
34
- - Bulk actions on selected VMs (start, stop, force off, pause, delete)
35
- - Edit Configuration Bulk action available
36
- - VM Migration (Live and Offline) with pre-checking server and VM configuration
37
- - Custom migration: migrate offline VMs with their volumes, snapshots, and overlays, allowing users to select where to place volumes on the destination server pool.
38
- - Always copy storage in custom migration by default
39
-
40
- ### Disk Overlay Management (External Snapshots)
41
- - **Create Overlay**: Create a new QCOW2 overlay on top of the current disk (freezes base image).
42
- - **Discard Overlay**: Revert to the backing file and delete the overlay (discard changes).
43
- - **Commit Disk**: Merge changes from the overlay back into the base image (make changes permanent).
44
- - Visual management of overlay state.
45
- - Comparison guide between Internal Snapshots and External Overlays included in the UI.
46
-
47
- ### Advanced Features
48
- - Filter VMs by status (All, Running, Paused, Stopped) and search by name
49
- - Filter VMs to show only VMs from a specific hypervisor
50
- - Server preferences configuration
51
- - Virsh shell access
52
- - Detailed VM information view
53
- - Web console management with automatic port allocation
54
- - Configuration file management for server lists
55
- - Create new VMs (with single server connection)
56
- - Bulk operations on multiple VMs
57
- - VMs sorted in human-readable order
58
- - Select which server to autoconnect at startup
59
-
60
- ## Configure VM Features
61
-
62
- ### CPU Configuration
63
- - Edit CPU count
64
- - Select CPU model from available models (including host-passthrough and default options)
65
- - CPU model selection is disabled when VM is running
66
-
67
- ### Memory Configuration
68
- - Edit memory size in MB
69
- - Enable/disable shared memory (disabled when VM is running)
70
-
71
- ### Firmware Configuration
72
- - Select firmware type (BIOS or UEFI)
73
- - For UEFI firmware:
74
- - Enable/disable Secure Boot
75
- - Enable/disable AMD-SEV and AMD-SEV-ES (when supported)
76
- - Select UEFI file from available options
77
- - Machine type selection (disabled when VM is running)
78
-
79
- ### Boot Configuration
80
- - Enable/disable boot menu
81
- - Boot device management
82
- - Set boot order for devices
83
-
84
- ### Disk Management
85
- - View all disks in a table format
86
- - Add new disk (create new or attach existing)
87
- - Create new disk in specific storage pool
88
- - Attach existing volume to VM
89
- - Attach existing disk from storage pools
90
- - Remove disk
91
- - Disable disk
92
- - Enable disk
93
- - Edit disk properties (cache mode and discard mode)
94
- - Disk status indicators (enabled/disabled)
95
- - Set disk cache and discard modes
96
- - Display disk bus information
97
-
98
- ### Network Configuration
99
- - View network interfaces with MAC addresses and IP addresses
100
- - Change network interface to a different network
101
- - View network DNS and gateway information
102
- - Add new network interface
103
- - Remove network interface
104
- - Change network interface model
105
-
106
- ### VirtIO-FS Configuration
107
- - View existing VirtIO-FS mounts
108
- - Add new VirtIO-FS mount
109
- - Edit existing VirtIO-FS mount
110
- - Delete VirtIO-FS mount
111
- - Requires shared memory to be enabled
112
-
113
- ### Video Configuration
114
- - Select video model (virtio, qxl, vga, cirrus, bochs, ramfb, none, default)
115
- - Enable/disable 3D acceleration (virtio only)
116
- - Video model selection is disabled when VM is running
117
-
118
- ### Graphics Configuration
119
- - Select graphics type (VNC, Spice, or None)
120
- - Configure listen type (Address or None)
121
- - Set address (Hypervisor default, Localhost only, All interfaces)
122
- - Enable/disable auto port allocation
123
- - Set port number (when auto port is disabled)
124
- - Enable/disable password protection
125
- - Set password for graphics access
126
- - Apply graphics settings (disabled when VM is running)
127
- - When switching from Spice to VNC: If other SPICE-related devices (channels, audio, QXL video) are detected, the user is prompted to remove them for a clean switch. This process automatically removes SPICE channels and USB redirection, changes SPICE audio to 'none', and converts QXL video to 'virtio'. A default VNC graphics device is added if no other graphics device exists after removal.
128
-
129
- ### TPM Configuration
130
- - Select TPM model (tpm-crb, tpm-tis, or none)
131
- - Select TPM type (emulated or passthrough)
132
- - Configure device path for passthrough TPM
133
- - Configure backend type and path for passthrough TPM
134
- - Apply TPM settings (disabled when VM is running)
135
-
136
- ### RNG Configuration
137
- - Configure Random Number Generator (RNG) host device.
138
- - Apply RNG settings (disabled when VM is running).
139
-
140
- ### Sound Configuration
141
- - Select sound model (ac97, ich6, sb16, pcspk, es1370, hda, default)
142
- - Sound model selection is disabled when VM is running
143
-
144
- ### Watchdog Configuration
145
- - Configure Watchdog device for VM
146
- - Set watchdog model and action (reset, shutdown, poweroff)
147
- - Watchdog configuration is disabled when VM is running
148
-
149
- ### Input Configuration
150
- - Configure input devices (keyboard, mouse, tablet)
151
- - Set input device type and bus (usb, ps2, virtio)
152
- - Input configuration is disabled when VM is running
153
-
154
- ### USB Host Configuration
155
- - Add/Remove USB host devices
156
- - Configure vendor ID, product ID, and address
157
-
158
- ### PCI Passthrough Configuration
159
- - View available PCI host devices
160
- - Attach PCI devices to VM (experimental)
161
- - Detach PCI devices from VM (experimental)
162
- - PCI device status indicators (attached/disconnected)
163
-
164
- ### Serial Configuration
165
- - Add/Remove serial devices
166
- - Configure serial device type (Pty)
167
-
168
- ### Additional Features
169
- - Tabbed interface for organized configuration
170
- - Toggle between main and extended configuration tabs
171
- - Real-time status indicators
172
- - Confirmation dialogs for destructive actions
173
- - Error handling and user feedback
174
- - VM status validation (prevents configuration changes when VM is running)
175
-
176
- ## Server Management Features
177
-
178
- ### Network Management
179
- - View all networks in a table format
180
- - Create new network with NAT or routed type
181
- - Edit network properties including DHCP settings
182
- - Delete network with confirmation
183
- - Toggle network active state
184
- - Toggle network autostart state
185
- - View network XML details
186
- - Get list of VMs using a specific network
187
-
188
- ### Storage Management
189
- - View storage pools in a tree format
190
- - Create new storage pool (directory or network file system)
191
- - Edit storage pool properties (path)
192
- - Delete storage pool with confirmation
193
- - Create new storage volume
194
- - Delete storage volume with confirmation
195
- - Toggle storage pool active state
196
- - Toggle storage pool autostart state
197
- - List unused storage volumes
198
- - Get all storage volumes across all pools
199
- - Move storage volume between pools
200
-
201
- ## User Interface Features
202
-
203
- ### Keyboard Shortcuts
204
- - `v` - View Log
205
- - `f` - Filter VM
206
- - `p` - Pattern Selection of VM
207
- - `m` - Servers List
208
- - `s` - Select Servers
209
- - `ctrl+a` - Select/Deselect All VMs on current page
210
- - `ctrl+u` - Unselect all VMs
211
- - `c` - User Application Configuration
212
- - `ctrl+v` - Virsh Shell
213
- - `q` - Quit
214
-
215
- For Debugging the app:
216
- - `ctrl+l` - Start/Stop Log Stats
217
- - `ctrl+s` - Show Cache Stats
218
-
219
-
220
- ### Visual Elements
221
- - Color-coded server identification
222
- - Status indicators with color coding (Running, Paused, Stopped)
223
- - Sparkline graphs for CPU, Memory, Disk, Network usage
224
- - Responsive layout that adapts to terminal size
225
- - Tabbed interface for organized information display
226
- - Selection indicators for multiple VMs
227
-
228
- ## Technical Capabilities
229
-
230
- ### Connection Management
231
- - Support for multiple libvirt connection types (local, SSH)
232
- - Automatic detection of virt-viewer, websockify, and novnc availability
233
- - Error handling and logging
234
- - Responsive UI that adapts to terminal size
235
- - Command-line mode support (--cmd flag)
236
- - Improved cache invalidation mechanism using UUID@URI for better accuracy
237
-
238
- ## User Experience
239
- - Visual feedback through notifications
240
- - Confirmation dialogs for destructive actions
241
- - Loading indicators for long-running operations
242
- - Detailed error messages
243
- - Command-line mode for advanced users
244
- - Bulk operations
245
- - Real-time VM status updates
246
-
247
- ## Extra Command-Line Tool (vmanager_cmd.py)
248
-
249
- In addition to the main TUI application, `vmanager` also provides a command-line interface (`vmanager_cmd.py`) for managing virtual machines and storage. This tool offers the following key features:
250
-
251
- * **Multi-server Management**: Connect to and manage multiple `libvirt` servers simultaneously from a single shell.
252
- * **Bulk VM Operations**: Execute commands like `start`, `stop`, `status`, `pause`, `resume`, `force_off`, and `delete` on multiple VMs across different connected servers at once.
253
- * **Advanced VM Selection**: Select VMs for operations using direct names or powerful regular expression patterns.
254
- * **Interactive VM Deletion**: Delete VMs with interactive confirmation, including an option to also remove associated storage volumes.
255
- * **Storage Management**: List storage pools and identify unused storage volumes across all connected servers.
256
- * **Tab Autocompletion**: Enjoy context-aware autocompletion for server names, VM names, and storage pool names, enhancing usability and speed.
@@ -1,93 +0,0 @@
1
- """
2
- Modal to show how to manage VM disks.
3
- """
4
- from textual.app import ComposeResult
5
- from textual.containers import Vertical, Horizontal, ScrollableContainer
6
- from textual.widgets import Button, Markdown
7
- from textual import on
8
- from .base_modals import BaseModal
9
-
10
- HOW_TO_DISK_TEXT = """
11
- # Managing VM Disks
12
-
13
- This guide explains the functions of the buttons available in the "Disks" tab.
14
-
15
- ---
16
-
17
- ### Add Disk
18
-
19
- This button allows you to create a **brand new virtual disk** and attach it to the VM.
20
-
21
- - You will be prompted to select a **Storage Pool** where the new disk image file will be created.
22
- - You must provide a **Volume Name** for the new disk (e.g., `new-data-disk.qcow2`).
23
- - You must specify its **Size** and **Format** (`qcow2` or `raw`).
24
- - The VM does **not** need to be stopped to add a new disk.
25
-
26
- ---
27
-
28
- ### Attach Existing Disk
29
-
30
- This attaches a **pre-existing disk image** to your VM. This is useful if you have an existing `.qcow2`, `.raw`, or `.iso` file you want to use.
31
-
32
- - The system will first ask you to select a **Storage Pool** that contains the disk volume you want to attach.
33
- - If the disk file is not part of a libvirt storage pool yet, you should first use the "Attach" button in the **Server Preferences -> Storage** tab to make it known to libvirt. This might involve creating a new storage pool for the directory containing your disk file.
34
-
35
- ---
36
-
37
- ### Edit Disk
38
-
39
- *(This button is enabled only when a disk is selected in the table)*
40
-
41
- Allows you to modify properties of an attached disk, such as:
42
- - **Bus Type:** `virtio`, `sata`, `scsi`, etc.
43
- - **Cache Mode:** `none`, `writeback`, etc.
44
- - **Discard Mode:** `unmap`, `ignore`.
45
-
46
- > **Important:** The VM must be **stopped** to edit disk properties.
47
-
48
- ---
49
-
50
- ### Remove Disk
51
-
52
- *(This button is enabled only when a disk is selected)*
53
-
54
- This **detaches** the selected disk from the virtual machine.
55
-
56
- - **This action does NOT delete the disk image file.** The file remains in its storage pool or on your filesystem.
57
- - It only removes the disk from this specific VM's configuration.
58
- - The VM must be **stopped** to remove most types of disks.
59
-
60
- ---
61
-
62
- ### Disable Disk
63
-
64
- *(This button is enabled for active disks)*
65
-
66
- This temporarily "unplugs" the disk from the VM **without removing its configuration**.
67
-
68
- - The disk will become invisible to the guest operating system but remains in the disk list with a `(disabled)` status.
69
- - This is useful for troubleshooting or temporarily preventing access to a disk without fully removing it.
70
-
71
- ---
72
-
73
- ### Enable Disk
74
-
75
- *(This button is enabled for disabled disks)*
76
-
77
- This "re-plugs" a disabled disk back into the VM, making it available to the guest operating system again.
78
- """
79
-
80
- class HowToDiskModal(BaseModal[None]):
81
- """A modal to display instructions for managing VM disks."""
82
-
83
- def compose(self) -> ComposeResult:
84
- with Vertical(id="howto-disk-dialog"):
85
- with ScrollableContainer(id="howto-disk-content"):
86
- yield Markdown(HOW_TO_DISK_TEXT, id="howto-disk-markdown")
87
- with Horizontal(id="dialog-buttons"):
88
- yield Button("Close", id="close-btn", variant="primary")
89
-
90
- @on(Button.Pressed)
91
- def on_button_pressed(self, event: Button.Pressed) -> None:
92
- """Handle button presses."""
93
- self.dismiss()
@@ -1,60 +0,0 @@
1
- """
2
- Modal to show how to configure networks.
3
- """
4
- from textual.app import ComposeResult
5
- from textual.containers import Vertical, Horizontal, ScrollableContainer
6
- from textual.widgets import Button, Markdown
7
- from textual import on
8
- from .base_modals import BaseModal
9
-
10
- HOW_TO_NETWORK_TEXT = """
11
- # Understanding Network Configuration in libvirt
12
-
13
- libvirt provides flexible networking capabilities for virtual machines, allowing them to communicate with each other, the host, and external networks.
14
-
15
- ### Types of Networks
16
-
17
- 1. **NAT (Network Address Translation) Network:**
18
- * **Purpose:** Allows VMs to access the external network (internet) but prevents external machines from directly initiating connections to the VMs.
19
- * **Mechanism:** libvirt creates a virtual bridge (e.g., `virbr0`) on the host, and VMs connect to this bridge. The host acts as a router, performing NAT for outgoing connections from VMs. VMs get IP addresses from a DHCP server managed by libvirt.
20
- * **Use Cases:** Most common setup for general VM usage where VMs just need internet access.
21
-
22
- 2. **Routed Network:**
23
- * **Purpose:** VMs can communicate with other machines on the host's physical network, and potentially external networks, with proper routing configured on the host and potentially external routers. VMs will have IP addresses on the same subnet as the host's physical network, or a dedicated routed subnet.
24
- * **Mechanism:** Similar to NAT, a virtual bridge is used, but without NAT. The host needs to be configured to route traffic between the virtual bridge and the physical interface. VMs typically get IP addresses from a DHCP server on the physical network or static IPs.
25
- * **Use Cases:** When VMs need to be directly accessible from the physical network, or participate as full members of an existing network.
26
-
27
- 3. **Isolated Network:**
28
- * **Purpose:** VMs on this network can only communicate with each other and the host, but not with external networks.
29
- * **Mechanism:** A virtual bridge is created, but no routing or NAT is configured to connect it to physical interfaces.
30
- * **Use Cases:** Testing environments, isolated services, or when you need a private network segment for VMs.
31
-
32
- ### Key Concepts
33
-
34
- * **Bridge:** A software-based network device that connects multiple network segments at the data link layer. VMs connect to a virtual bridge, which then connects to a physical interface (for NAT/routed) or remains isolated.
35
- * **DHCP:** Dynamic Host Configuration Protocol. Automatically assigns IP addresses to VMs within a network.
36
- * **Forward Device:** The physical network interface on the host through which the virtual network's traffic is forwarded (for NAT or routed modes).
37
- * **MAC Address:** Media Access Control address. A unique identifier assigned to network interfaces. For KVM/QEMU, MAC addresses often start with `52:54:00:`.
38
-
39
- ### Common Tasks
40
-
41
- * **Create/Edit Network:** Define network parameters like name, IP range, DHCP settings, and forward mode.
42
- * **Activate/Deactivate Network:** Start or stop a virtual network.
43
- * **Autostart Network:** Configure a network to start automatically when the libvirt daemon starts.
44
- * **View XML:** Examine the underlying libvirt XML definition of a network, which provides full details of its configuration.
45
- """
46
-
47
- class HowToNetworkModal(BaseModal[None]):
48
- """A modal to display instructions for network configuration."""
49
-
50
- def compose(self) -> ComposeResult:
51
- with Vertical(id="howto-network-dialog"):
52
- with ScrollableContainer(id="howto-network-content"):
53
- yield Markdown(HOW_TO_NETWORK_TEXT, id="howto-network-markdown")
54
- with Horizontal(id="dialog-buttons"):
55
- yield Button("Close", id="close-btn", variant="primary")
56
-
57
- @on(Button.Pressed)
58
- def on_button_pressed(self, event: Button.Pressed) -> None:
59
- """Handle button presses."""
60
- self.dismiss()
@@ -1,73 +0,0 @@
1
- """
2
- Modal to show how overlay disks work.
3
- """
4
- from textual.app import ComposeResult
5
- from textual.containers import Vertical, Horizontal, ScrollableContainer
6
- from textual.widgets import Button, Markdown
7
- from textual import on
8
- from .base_modals import BaseModal
9
-
10
- HOW_TO_OVERLAY_TEXT = """
11
- # Understanding Snapshots and Disk Overlays
12
-
13
- Virtual machines in this manager support two ways to preserve states and manage changes: **Snapshots** and **Disk Overlays**. While they share similar goals, they work differently and are suited for different use cases.
14
-
15
- ---
16
-
17
- ### 1. Snapshots (Internal)
18
- Snapshots are managed directly by libvirt and are typically stored **inside** the disk image (if using QCOW2 format).
19
-
20
- * **How they work:** When you take a snapshot, libvirt records the current state of the VM's disks and, if the VM is running, its memory (RAM). You can have many snapshots for a single VM, creating a timeline you can jump back and forth in.
21
- * **Operations:**
22
- * **Take Snapshot:** Creates a new restore point.
23
- * **Restore Snapshot:** Reverts the VM to a previous state.
24
- * **Delete Snapshot:** Removes the restore point from the timeline.
25
- * **Best for:** Quick restore points before risky operations, and preserving the full "live" state of a running VM.
26
-
27
- ### 2. Disk Overlays (External)
28
- Overlays are **new files** created on top of a base disk image. This is also known as "External Snapshots" or "Backing Files".
29
-
30
- * **Key Concepts:**
31
- * **Base Image (Backing File):** The original disk image that becomes read-only.
32
- * **Overlay Image:** A new QCOW2 file that records only the changes made *after* its creation.
33
- * **Backing Chain:** The relationship between layers (e.g., Base -> Overlay 1 -> Overlay 2).
34
- * **Operations:**
35
- * **New Overlay:** Freezes the current disk and starts a new layer.
36
- * **Discard Overlay (Revert):** Deletes the overlay file and reverts to the base image.
37
- * **Commit Disk (Merge):** Merges changes from the overlay into the base image, making them permanent.
38
- * **Best for:** Maintaining "Golden Images", branching multiple VMs from a single base, and isolating large changes in separate files.
39
-
40
- ---
41
-
42
- ### Comparison: Snapshot vs. Overlay
43
-
44
- | Feature | Snapshots (Internal) | Disk Overlays (External) |
45
- | :--- | :--- | :--- |
46
- | **Storage** | Inside the existing disk file | In a new, separate file |
47
- | **VM State** | Can include RAM (Live state) | Disk only (requires VM stop) |
48
- | **Management** | Timeline (multiple points) | Layered (Base + Changes) |
49
- | **Primary Use** | Quick restore points | Permanent branching / Golden images |
50
-
51
- ---
52
-
53
- ### How Overlays Work (Technical)
54
-
55
- 1. **Creation:** When you create a "New Overlay", the current disk image is set as the backing file for a newly created QCOW2 file. The VM is then updated to point to this new overlay file instead of the original disk.
56
- 2. **Read Operations:** When the VM needs to read data, it first checks the overlay. If the data has been modified, it reads from the overlay. If not, it transparently reads from the backing file.
57
- 3. **Write Operations:** All writes are directed to the overlay file. The backing file remains untouched and pristine.
58
- """
59
-
60
- class HowToOverlayModal(BaseModal[None]):
61
- """A modal to display instructions for disk overlays."""
62
-
63
- def compose(self) -> ComposeResult:
64
- with Vertical(id="howto-overlay-dialog", classes="howto-dialog"):
65
- with ScrollableContainer(id="howto-overlay-content"):
66
- yield Markdown(HOW_TO_OVERLAY_TEXT, id="howto-overlay-markdown")
67
- with Horizontal(id="dialog-buttons"):
68
- yield Button("Close", id="close-btn", variant="primary")
69
-
70
- @on(Button.Pressed)
71
- def on_button_pressed(self, event: Button.Pressed) -> None:
72
- """Handle button presses."""
73
- self.dismiss()
@@ -1,88 +0,0 @@
1
- """
2
- Modal to show how to use ssh-agent.
3
- """
4
- from textual.app import ComposeResult
5
- from textual.containers import Vertical, Horizontal, ScrollableContainer
6
- from textual.widgets import Button, Markdown
7
- from textual import on
8
- from .base_modals import BaseModal
9
-
10
- HOW_TO_SSH_TEXT = """
11
- # Using an SSH Agent for Passwordless Connections
12
-
13
- Using an `ssh-agent` is the standard and most secure way to handle passphrase-protected SSH keys, allowing you to connect without repeatedly entering your passphrase.
14
-
15
- ### What is an SSH Agent?
16
-
17
- An `ssh-agent` is a background program that securely stores your private SSH keys in memory. When you try to connect to a remote server, SSH can ask the agent for the key, and the agent provides it. You only need to "unlock" your key once.
18
-
19
- ---
20
-
21
- ### Step 1: Start the `ssh-agent`
22
-
23
- On most modern desktop environments, an agent is often started automatically. If not, run this in your terminal:
24
-
25
- ```bash
26
- eval "$(ssh-agent -s)"
27
- ```
28
-
29
- > To make this permanent, add the command to your shell's startup file (e.g., `~/.bashrc` or `~/.zshrc`).
30
-
31
- ---
32
-
33
- ### Step 2: Add Your SSH Key to the Agent
34
-
35
- Use the `ssh-add` command. If your key is in a default location (`~/.ssh/id_rsa`, etc.), you can just run:
36
-
37
- ```bash
38
- ssh-add
39
- ```
40
-
41
- If your key is elsewhere, specify the path to the **private key**:
42
-
43
- ```bash
44
- ssh-add /path/to/your/private_key
45
- ```
46
-
47
- You will be prompted for your key's passphrase **one time**.
48
-
49
- To verify the key was added, list the agent's keys:
50
- ```bash
51
- ssh-add -l
52
- ```
53
- ---
54
-
55
- ### Step 3: Connect
56
-
57
- That's it! `Virtui Manager` will now use the agent to authenticate for any `qemu+ssh://` connections without any more prompts.
58
-
59
- ---
60
-
61
- ### SSH Compression for Performance
62
-
63
- For connections over slower networks, enabling SSH compression can significantly improve performance. This is configured in your SSH client's configuration file.
64
-
65
- To enable compression for a specific host, add the following to your `~/.ssh/config` file:
66
-
67
- ```
68
- Host your_remote_host_name
69
- Compression yes
70
- ```
71
-
72
- Replace `your_remote_host_name` with the actual hostname or IP address you use in your `qemu+ssh://` URI. If you want to enable compression for all SSH connections, you can use `Host *`.
73
- """
74
-
75
- class HowToSSHModal(BaseModal[None]):
76
- """A modal to display instructions for using an ssh-agent."""
77
-
78
- def compose(self) -> ComposeResult:
79
- with Vertical(id="howto-ssh-dialog"):
80
- with ScrollableContainer(id="howto-ssh-content"):
81
- yield Markdown(HOW_TO_SSH_TEXT, id="howto-ssh-markdown")
82
- with Horizontal(id="dialog-buttons"):
83
- yield Button("Close", id="close-btn", variant="primary")
84
-
85
- @on(Button.Pressed)
86
- def on_button_pressed(self, event: Button.Pressed) -> None:
87
- """Handle button presses."""
88
- self.dismiss()
@@ -1,85 +0,0 @@
1
- """
2
- Modal to show how to use VirtIO-FS.
3
- """
4
- from textual.app import ComposeResult
5
- from textual.containers import Vertical, Horizontal, ScrollableContainer
6
- from textual.widgets import Button, Markdown
7
- from textual import on
8
- from .base_modals import BaseModal
9
-
10
- HOW_TO_VIRTIOFS_TEXT = """
11
- # Using VirtIO-FS for Host-Guest File Sharing
12
-
13
- VirtIO-FS is a high-performance shared filesystem that lets you share a directory from your host machine directly with a guest VM.
14
-
15
- ---
16
-
17
- ### Host Prerequisites
18
-
19
- 1. **Shared Memory:** VirtIO-FS requires shared memory to be enabled for the VM. You can enable this in the **"Mem"** tab.
20
- 2. **Permissions:** The user running QEMU/libvirt on the host must have the necessary permissions to read (and write, if needed) the source directory you want to share.
21
-
22
- ---
23
-
24
- ### Adding a VirtIO-FS Mount
25
-
26
- - **Source Path:** The absolute path to the directory on your **host machine** that you want to share.
27
- - **Target Path:** This is a "mount tag" or a label that the guest VM will use to identify the shared directory. It is **not** a path inside the guest. For example, you could use `shared-data`.
28
-
29
- ---
30
-
31
- ### Mounting in a Linux Guest
32
-
33
- Most modern Linux distributions include the necessary VirtIO-FS drivers.
34
-
35
- **1. Create a Mount Point:**
36
- This is the directory inside your VM where the shared files will appear.
37
-
38
- ```bash
39
- sudo mkdir /mnt/my_host_share
40
- ```
41
-
42
- **2. Mount the Share:**
43
- Use the `mount` command with the filesystem type `virtiofs` and the **Target Path (mount tag)** you defined.
44
-
45
- ```bash
46
- sudo mount -t virtiofs 'your-target-path' /mnt/my_host_share
47
- ```
48
- *(Replace `'your-target-path'` with the actual tag you set)*
49
-
50
- **3. Automount on Boot (Optional):**
51
- To make the share available automatically every time the VM boots, add an entry to `/etc/fstab`:
52
-
53
- ```
54
- your-target-path /mnt/my_host_share virtiofs defaults,nofail 0 0
55
- ```
56
- > The `nofail` option is recommended to prevent boot issues if the share is not available.
57
-
58
- ---
59
-
60
- ### Mounting in a Windows Guest
61
-
62
- **1. Install Drivers:**
63
- You must install the VirtIO-FS drivers in the Windows guest. These are included in the **"VirtIO-Win Guest Tools"** package, which you can typically download as an ISO file.
64
- - Download the latest stable `virtio-win.iso` from the [Fedora VirtIO-Win project](https://github.com/virtio-win/virtio-win-pkg-scripts/blob/master/README.md).
65
- - Attach the ISO to your VM as a CD-ROM.
66
- - Open the CD-ROM in Windows and run the `virtio-win-guest-tools.exe` installer, ensuring the **"VirtIO-FS"** feature is selected.
67
-
68
- **2. Access the Share:**
69
- After installation and a reboot, the VirtIO-FS service will start. The shared folder will automatically appear as a network drive in **This PC** (or My Computer). The drive will be named after the **Target Path (mount tag)** you set.
70
- """
71
-
72
- class HowToVirtIOFSModal(BaseModal[None]):
73
- """A modal to display instructions for using VirtIO-FS."""
74
-
75
- def compose(self) -> ComposeResult:
76
- with Vertical(id="howto-virtiofs-dialog"):
77
- with ScrollableContainer(id="howto-virtiofs-content"):
78
- yield Markdown(HOW_TO_VIRTIOFS_TEXT, id="howto-virtiofs-markdown")
79
- with Horizontal(id="dialog-buttons"):
80
- yield Button("Close", id="close-btn", variant="primary")
81
-
82
- @on(Button.Pressed)
83
- def on_button_pressed(self, event: Button.Pressed) -> None:
84
- """Handle button presses."""
85
- self.dismiss()
File without changes
File without changes