From b9f9e7d1b05a4c6ef44d3c47bce46b8c63cb4a58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Thu, 19 Mar 2020 19:07:12 +0100 Subject: [PATCH] Handle libvirt errors and logging --- lxc_net/libvirt_error.py | 105 +++++++++++++++++++++++++++++++++++++++ spawn_network.py | 6 ++- 2 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 lxc_net/libvirt_error.py diff --git a/lxc_net/libvirt_error.py b/lxc_net/libvirt_error.py new file mode 100644 index 0000000..f185288 --- /dev/null +++ b/lxc_net/libvirt_error.py @@ -0,0 +1,105 @@ +from collections import namedtuple +import libvirt + +import logging + +logger = logging.getLogger(__name__) + + +class LibvirtErrorCode: + reverse_errors = { + getattr(libvirt, err_name): err_name + for err_name in filter(lambda x: x.startswith("VIR_ERR_"), libvirt.__dir__()) + } + + def __init__(self, code): + self.code = code + self.err = self.reverse_errors.get(code, None) + + def __repr__(self): + return "{}".format(self.code) + + def __str__(self): + return "{} [{}]".format(self.err, self.code) + + def __eq__(self, val): + return self.code == val + + +class LibvirtErrorDomain: + reverse_domains = { + getattr(libvirt, err_name): err_name + for err_name in filter(lambda x: x.startswith("VIR_DOMAIN_"), libvirt.__dir__()) + } + + def __init__(self, code): + self.code = code + self.domain = self.reverse_domains.get(code, None) + + def __repr__(self): + return "{}".format(self.code) + + def __str__(self): + return "{} [{}]".format(self.domain, self.code) + + def __eq__(self, val): + return self.code == val + + +class LibvirtError: + params = ( + "code", + "domain", + "message", + "level", + "str1", + "str2", + "str3", + "int1", + "int2", + ) + + def __init__(self, code, domain, message, level, str1, str2, str3, int1, int2): + self.code = LibvirtErrorCode(code) + self.domain = LibvirtErrorDomain(domain) + self.message = message + self.level = level + self.str1 = str1 + self.str2 = str2 + self.str3 = str3 + self.int1 = int1 + self.int2 = int2 + + def __repr__(self): + out = "{}(".format(self.__class__.__name__) + for param in self.__class__.params: + out += "{}={}, ".format(param, getattr(self, param)) + out += ")" + return out + + def __str__(self): + return "Libvirt error {}, domain {}. {}".format( + self.code, self.domain, self.message + ) + + @property + def loglevel(self): + levelmap = { + 0: logging.INFO, + 1: logging.WARNING, + 2: logging.ERROR, + } + + return levelmap.get(self.level, logging.CRITICAL) + + +def libvirt_error_handler(_, raw_error): + """ Called upon libvirt error """ + + error = LibvirtError(*raw_error) + loglevel = error.loglevel + + if error.code == libvirt.VIR_ERR_NO_DOMAIN: + loglevel = logging.DEBUG + + logger.log(loglevel, str(error)) diff --git a/spawn_network.py b/spawn_network.py index 2f1775a..f169280 100755 --- a/spawn_network.py +++ b/spawn_network.py @@ -1,9 +1,10 @@ #!/usr/bin/env python3 -from lxc_net import parse_network +from lxc_net import parse_network, libvirt_error import signal import libvirt import argparse +import logging def parse_args(): @@ -28,6 +29,9 @@ def main(): signal.signal(signal.SIGINT, handle_sigint) + logging.basicConfig(level=logging.WARNING) + libvirt.registerErrorHandler(libvirt_error.libvirt_error_handler, None) + conn = libvirt.open("lxc:///") topology = parse_network.YamlTopology(args.topology, conn)