aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorterminaldweller <devi@terminaldweller.com>2023-03-24 13:44:29 +0000
committerterminaldweller <devi@terminaldweller.com>2023-03-24 13:44:29 +0000
commit17b15dfcebce7537e411ef69f6493c8ab1f49544 (patch)
tree9295dca8bc19cbc89b570a37b8fb8a1be191c334
parentfixed the main function so poetry build actually works correctly (diff)
downloadvirttop-17b15dfcebce7537e411ef69f6493c8ab1f49544.tar.gz
virttop-17b15dfcebce7537e411ef69f6493c8ab1f49544.zip
fixed the help option, fixed some breakages, some operations are now async, updated the readme
-rw-r--r--README.md35
-rw-r--r--poetry.lock4
-rw-r--r--pyproject.toml2
-rwxr-xr-xvirttop/virttop.py99
4 files changed, 105 insertions, 35 deletions
diff --git a/README.md b/README.md
index 15649cd..718c522 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,6 @@
# virttop
a top like utility for libvirt
-
![Image](virttop.png)
## How to get
@@ -9,6 +8,23 @@ a top like utility for libvirt
pip install virttop
```
+## Options
+```sh
+usage: virttop.py [-h] [--uri URI [URI ...]] [--config CONFIG]
+ [--active ACTIVE] [--logfile LOGFILE]
+
+options:
+ -h, --help show this help message and exit
+ --uri URI [URI ...], -u URI [URI ...]
+ A list of URIs to connect to seperated by commas
+ --config CONFIG, -c CONFIG
+ Path to the config file
+ --active ACTIVE, -a ACTIVE
+ Show active VMs only
+ --logfile LOGFILE, -l LOGFILE
+ Location of the log file
+```
+
## Configfile
The default location for the config file is '~/.virttop.toml'.
@@ -28,9 +44,14 @@ selected_bg=36
## Keybindings
-`j` and `k` and arrow keys move up and down.</br>
-`g` moves to the top of the list.</br>
-`G` moved to the bottom of the list.</br>
-`r` runs an inactive domain.</br>
-`s` shuts down a running domain.</br>
-`d` destroy a running domain.</br>
+`j`,`k` and arrow keys move up and down.
+
+`g` moves to the top of the list.
+
+`G` moves to the bottom of the list.
+
+`r` runs an inactive domain.
+
+`s` shuts down a running domain.
+
+`d` destroys a running domain.
diff --git a/poetry.lock b/poetry.lock
index d7b6a32..bb5ab8b 100644
--- a/poetry.lock
+++ b/poetry.lock
@@ -8,7 +8,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "libvirt-python"
-version = "9.0.0"
+version = "9.1.0"
description = "The libvirt virtualization API python binding"
category = "main"
optional = false
@@ -25,5 +25,5 @@ defusedxml = [
{file = "defusedxml-0.7.1.tar.gz", hash = "sha256:1bb3032db185915b62d7c6209c5a8792be6a32ab2fedacc84e01b52c51aa3e69"},
]
libvirt-python = [
- {file = "libvirt-python-9.0.0.tar.gz", hash = "sha256:49702d33fa8cbcae19fa727467a69f7ae2241b3091324085ca1cc752b2b414ce"},
+ {file = "libvirt-python-9.1.0.tar.gz", hash = "sha256:6f98d235db975a33ff5e6fc09be566a00d22398039d411f34220384c05590ba4"},
]
diff --git a/pyproject.toml b/pyproject.toml
index d36c74f..73afd96 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -1,6 +1,6 @@
[tool.poetry]
name = "virttop"
-version = "0.2.1"
+version = "0.2.2"
description = "A top like utility for libvirt"
authors = ["terminaldweller <devi@terminaldweller.com>"]
license = "GPL-3.0"
diff --git a/virttop/virttop.py b/virttop/virttop.py
index 47bf53f..0794ea6 100755
--- a/virttop/virttop.py
+++ b/virttop/virttop.py
@@ -10,6 +10,8 @@ import asyncio
import csv
import curses
import dataclasses
+import functools
+import logging
import os
import signal
import sys
@@ -35,7 +37,7 @@ def request_cred(credentials, sasl_user, sasl_pass):
return 0
-def do_cleanup():
+def do_cleanup(stdscr):
"""Return the terminal to a sane state."""
curses.nocbreak()
stdscr.keypad(False)
@@ -45,9 +47,9 @@ def do_cleanup():
# pylint: disable=unused-argument
-def sig_handler_sigint(signum, frame):
+def sig_handler_sigint(signum, frame, stdscr):
"""Just to handle C-c cleanly"""
- do_cleanup()
+ do_cleanup(stdscr)
sys.exit(0)
@@ -79,11 +81,11 @@ class Argparser: # pylint: disable=too-few-public-methods
default=False,
)
self.parser.add_argument(
- "--debug",
- "-d",
- type=bool,
- help="Turn on debug logging",
- default=False,
+ "--logfile",
+ "-l",
+ type=str,
+ help="Location of the log file",
+ default="~/.virttop.log",
)
self.args = self.parser.parse_args()
@@ -176,7 +178,6 @@ def get_disk_info(
# pylint: disable=too-many-locals
-# pylint: disable=too-many-branches
def ffs(
offset: int,
header_list: typing.Optional[typing.List[str]],
@@ -318,8 +319,12 @@ def fill_virt_data_uri(
if not found_the_pool:
virt_data.memory_pool.append("N/A")
- disk_info = get_disk_info(xml_doc)
- image_name = disk_info["file"]
+ try:
+ disk_info = get_disk_info(xml_doc)
+ image_name = disk_info["file"]
+ except Exception as exception:
+ image_name = "X"
+ logging.exception(exception)
if host.ID() >= 0:
_, rd_bytes, _, wr_bytes, _ = dom.blockStats(image_name)
virt_data.disk_reads.append(size_abr(rd_bytes, 1))
@@ -336,7 +341,8 @@ def fill_virt_data_uri(
virt_data.disk_writes.append("-")
virt_data.macs.append("-")
virt_data.ips.append("-")
- except:
+ except Exception as exception:
+ logging.exception(exception)
pass
@@ -402,16 +408,48 @@ def get_visible_rows(max_rows: int, sel: int) -> typing.Tuple[int, int]:
return win_min_row, win_max_row
-async def main_loop() -> None:
+async def start_domain(dom):
+ """Start a domain."""
+ try:
+ task = asyncio.create_task(dom.createWithFlags())
+ await asyncio.sleep(0)
+ return task
+ except Exception as exception:
+ logging.exception(exception)
+
+
+async def destroy_domain(dom):
+ """Destroy a domain gracefully."""
+ try:
+ task = asyncio.create_task(
+ dom.destroyFlags(flags=libvirt.VIR_DOMAIN_DESTROY_GRACEFUL)
+ )
+ await asyncio.sleep(0)
+ return task
+ except Exception as exception:
+ logging.exception(exception)
+
+
+async def shutdown_domain(dom):
+ """Shutdown a domain."""
+ try:
+ task = asyncio.create_task(dom.shutdownFlags())
+ await asyncio.sleep(0)
+ return task
+ except Exception as exception:
+ logging.exception(exception)
+
+
+async def main_loop(argparser, stdscr) -> None:
"""Main TUI loop."""
sel: int = 0
current_row: int = 0
current_visi: int = 0
vm_name_ordered_list: typing.List[str] = []
+ task_list: typing.List[asyncio.Task] = []
- stdscr = curses_init()
- signal.signal(signal.SIGINT, sig_handler_sigint)
- argparser = Argparser()
+ sigint_handler = functools.partial(sig_handler_sigint, stdscr=stdscr)
+ signal.signal(signal.SIGINT, sigint_handler)
config_data = read_config(argparser.args.config)
arp_table = get_arp_table()
init_color_pairs(config_data)
@@ -525,29 +563,40 @@ async def main_loop() -> None:
break
elif char == ord("d"):
dom = conn.lookupByName(vm_name_ordered_list[sel])
- dom.destroyFlags(flags=libvirt.VIR_DOMAIN_DESTROY_GRACEFUL)
+ logging.debug("shutting down domain %s", vm_name_ordered_list[sel])
+ task_list.append(await destroy_domain(dom))
elif char == ord("s"):
dom = conn.lookupByName(vm_name_ordered_list[sel])
- dom.shutdownFlags()
+ logging.debug("shutting down domain %s", vm_name_ordered_list[sel])
+ task_list.append(await shutdown_domain(dom))
elif char == ord("r"):
dom = conn.lookupByName(vm_name_ordered_list[sel])
- dom.createWithFlags()
+ logging.debug("shutting down domain %s", vm_name_ordered_list[sel])
+ task_list.append(await start_domain(dom))
else:
pass
stdscr.refresh()
+ vm_name_ordered_list = []
def main() -> None:
"""Entry point."""
try:
- asyncio.run(main_loop())
- except Exception as e:
- print(e)
- finally:
- do_cleanup()
+ argparser = Argparser()
+ stdscr = curses_init()
+ logging.basicConfig(
+ filename=os.path.expanduser(argparser.args.logfile),
+ filemode="w",
+ format="%(asctime)s,%(msecs)d %(name)s %(levelname)s %(message)s",
+ datefmt="%H:%M:%S",
+ level=logging.DEBUG,
+ )
+ asyncio.run(main_loop(argparser, stdscr))
+ except Exception as exception:
+ logging.exception(exception)
+ do_cleanup(stdscr)
-# FIXME- the finally wipes the screen, effectively rendering the help option useless
if __name__ == "__main__":
main()