From 52867614df4923fc06bd94a8295636390d67af20 Mon Sep 17 00:00:00 2001 From: meow Date: Mon, 30 Dec 2019 14:58:05 +0500 Subject: [PATCH] Remove unused code + Increase frequeuncy of host heartbeat update --- ucloud/api/helper.py | 32 -- ucloud/api/main.py | 9 - ucloud/api/schemas.py | 3 - ucloud/common/helpers.py | 40 --- ucloud/common/network.py | 12 - ucloud/host/main.py | 2 +- ucloud/host/qmp/__init__.py | 570 ------------------------------------ ucloud/host/qmp/qmp.py | 257 ---------------- 8 files changed, 1 insertion(+), 924 deletions(-) delete mode 100644 ucloud/common/helpers.py delete mode 100755 ucloud/host/qmp/__init__.py delete mode 100755 ucloud/host/qmp/qmp.py diff --git a/ucloud/api/helper.py b/ucloud/api/helper.py index a77a151..6fdeb30 100755 --- a/ucloud/api/helper.py +++ b/ucloud/api/helper.py @@ -132,38 +132,6 @@ def generate_mac( return separator.join(byte_fmt % b for b in mac) -def get_ip_addr(mac_address, device): - """Return IP address of a device provided its mac address / link local address - and the device with which it is connected. - - For Example, if we call get_ip_addr(mac_address="52:54:00:12:34:56", device="br0") - the following two scenarios can happen - 1. It would return None if we can't be able to find device whose mac_address is equal - to the arg:mac_address or the mentioned arg:device does not exists or the ip address - we found is local. - 2. It would return ip_address of device whose mac_address is equal to arg:mac_address - and is connected/neighbor of arg:device - """ - try: - output = sp.check_output( - ["ip", "-6", "neigh", "show", "dev", device], stderr=sp.PIPE - ) - except sp.CalledProcessError: - return None - else: - result = [] - output = output.strip().decode("utf-8") - output = output.split("\n") - for entry in output: - entry = entry.split() - if entry: - ip = ipaddress.ip_address(entry[0]) - mac = entry[2] - if ip.is_global and mac_address == mac: - result.append(ip) - return result - - def mac2ipv6(mac, prefix): # only accept MACs separated by a colon parts = mac.split(":") diff --git a/ucloud/api/main.py b/ucloud/api/main.py index c63babf..d4cdbe9 100644 --- a/ucloud/api/main.py +++ b/ucloud/api/main.py @@ -19,15 +19,6 @@ from . import schemas from .helper import generate_mac, mac2ipv6 -def get_parent(obj, attr): - parent = getattr(obj, attr) - child = parent - while parent is not None: - child = parent - parent = getattr(parent, attr) - return child - - logger = logging.getLogger(__name__) app = Flask(__name__) diff --git a/ucloud/api/schemas.py b/ucloud/api/schemas.py index a848a7d..91289b0 100755 --- a/ucloud/api/schemas.py +++ b/ucloud/api/schemas.py @@ -150,7 +150,6 @@ class CreateImageSchema(BaseSchema): class CreateHostSchema(OTPSchema): def __init__(self, data): - self.parsed_specs = {} # Fields self.specs = Field("specs", dict, data.get("specs", KeyError)) self.hostname = Field( @@ -234,8 +233,6 @@ class CreateHostSchema(OTPSchema): class CreateVMSchema(OTPSchema): def __init__(self, data): - self.parsed_specs = {} - # Fields self.specs = Field("specs", dict, data.get("specs", KeyError)) self.vm_name = Field( diff --git a/ucloud/common/helpers.py b/ucloud/common/helpers.py deleted file mode 100644 index 501aa90..0000000 --- a/ucloud/common/helpers.py +++ /dev/null @@ -1,40 +0,0 @@ -import logging -import socket -import requests -import json - -from ipaddress import ip_address - -from os.path import join as join_path -from . import logger - - -# TODO: Should be removed as soon as migration -# mechanism is finalized inside ucloud -def get_ipv4_address(): - # If host is connected to internet - # Return IPv4 address of machine - # Otherwise, return 127.0.0.1 - with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s: - try: - s.connect(("8.8.8.8", 80)) - except socket.timeout: - address = "127.0.0.1" - except Exception as e: - logger.exception(e) - address = "127.0.0.1" - else: - address = s.getsockname()[0] - - return address - - -def get_ipv6_address(): - try: - r = requests.get("https://api6.ipify.org?format=json") - content = json.loads(r.content.decode("utf-8")) - ip = ip_address(content["ip"]).exploded - except Exception as e: - logger.exception(e) - else: - return ip diff --git a/ucloud/common/network.py b/ucloud/common/network.py index 61dbd64..adba108 100644 --- a/ucloud/common/network.py +++ b/ucloud/common/network.py @@ -70,15 +70,3 @@ def delete_network_interface(iface): except Exception: logger.exception("Interface %s Deletion failed", iface) - -def find_free_port(): - with closing( - socket.socket(socket.AF_INET, socket.SOCK_STREAM) - ) as s: - try: - s.bind(("", 0)) - s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) - except Exception: - return None - else: - return s.getsockname()[1] diff --git a/ucloud/host/main.py b/ucloud/host/main.py index 88dfb7c..f25a984 100755 --- a/ucloud/host/main.py +++ b/ucloud/host/main.py @@ -21,7 +21,7 @@ def update_heartbeat(hostname): while True: this_host.update_heartbeat() host_pool.put(this_host) - time.sleep(10) + time.sleep(3) def maintenance(host): diff --git a/ucloud/host/qmp/__init__.py b/ucloud/host/qmp/__init__.py deleted file mode 100755 index 40ac3a4..0000000 --- a/ucloud/host/qmp/__init__.py +++ /dev/null @@ -1,570 +0,0 @@ -# QEMU library -# -# Copyright (C) 2015-2016 Red Hat Inc. -# Copyright (C) 2012 IBM Corp. -# -# Authors: -# Fam Zheng -# -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. -# -# Based on qmp.py. -# - -import errno -import logging -import os -import shutil -import socket -import subprocess -import tempfile - -from . import qmp - -LOG = logging.getLogger(__name__) - -# Mapping host architecture to any additional architectures it can -# support which often includes its 32 bit cousin. -ADDITIONAL_ARCHES = {"x86_64": "i386", "aarch64": "armhf"} - - -def kvm_available(target_arch=None): - host_arch = os.uname()[4] - if target_arch and target_arch != host_arch: - if target_arch != ADDITIONAL_ARCHES.get(host_arch): - return False - return os.access("/dev/kvm", os.R_OK | os.W_OK) - - -class QEMUMachineError(Exception): - """ - Exception called when an error in QEMUMachine happens. - """ - - -class QEMUMachineAddDeviceError(QEMUMachineError): - """ - Exception raised when a request to add a device can not be fulfilled - - The failures are caused by limitations, lack of information or conflicting - requests on the QEMUMachine methods. This exception does not represent - failures reported by the QEMU binary itself. - """ - - -class MonitorResponseError(qmp.QMPError): - """ - Represents erroneous QMP monitor reply - """ - - def __init__(self, reply): - try: - desc = reply["error"]["desc"] - except KeyError: - desc = reply - super(MonitorResponseError, self).__init__(desc) - self.reply = reply - - -class QEMUMachine(object): - """ - A QEMU VM - - Use this object as a context manager to ensure the QEMU process terminates:: - - with VM(binary) as vm: - ... - # vm is guaranteed to be shut down here - """ - - def __init__( - self, - binary, - args=None, - wrapper=None, - name=None, - test_dir="/var/tmp", - monitor_address=None, - socket_scm_helper=None, - ): - """ - Initialize a QEMUMachine - - @param binary: path to the qemu binary - @param args: list of extra arguments - @param wrapper: list of arguments used as prefix to qemu binary - @param name: prefix for socket and log file names (default: qemu-PID) - @param test_dir: where to create socket and log file - @param monitor_address: address for QMP monitor - @param socket_scm_helper: helper program, required for send_fd_scm() - @note: Qemu process is not started until launch() is used. - """ - if args is None: - args = [] - if wrapper is None: - wrapper = [] - if name is None: - name = "qemu-%d" % os.getpid() - self._name = name - self._monitor_address = monitor_address - self._vm_monitor = None - self._qemu_log_path = None - self._qemu_log_file = None - self._popen = None - self._binary = binary - self._args = list( - args - ) # Force copy args in case we modify them - self._wrapper = wrapper - self._events = [] - self._iolog = None - self._socket_scm_helper = socket_scm_helper - self._qmp = None - self._qemu_full_args = None - self._test_dir = test_dir - self._temp_dir = None - self._launched = False - self._machine = None - self._console_set = False - self._console_device_type = None - self._console_address = None - self._console_socket = None - - # just in case logging wasn't configured by the main script: - logging.basicConfig(level=logging.DEBUG) - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.shutdown() - return False - - # This can be used to add an unused monitor instance. - def add_monitor_null(self): - self._args.append("-monitor") - self._args.append("null") - - def add_fd(self, fd, fdset, opaque, opts=""): - """ - Pass a file descriptor to the VM - """ - options = ["fd=%d" % fd, "set=%d" % fdset, "opaque=%s" % opaque] - if opts: - options.append(opts) - - # This did not exist before 3.4, but since then it is - # mandatory for our purpose - if hasattr(os, "set_inheritable"): - os.set_inheritable(fd, True) - - self._args.append("-add-fd") - self._args.append(",".join(options)) - return self - - # Exactly one of fd and file_path must be given. - # (If it is file_path, the helper will open that file and pass its - # own fd) - def send_fd_scm(self, fd=None, file_path=None): - # In iotest.py, the qmp should always use unix socket. - assert self._qmp.is_scm_available() - if self._socket_scm_helper is None: - raise QEMUMachineError("No path to socket_scm_helper set") - if not os.path.exists(self._socket_scm_helper): - raise QEMUMachineError( - "%s does not exist" % self._socket_scm_helper - ) - - # This did not exist before 3.4, but since then it is - # mandatory for our purpose - if hasattr(os, "set_inheritable"): - os.set_inheritable(self._qmp.get_sock_fd(), True) - if fd is not None: - os.set_inheritable(fd, True) - - fd_param = [ - "%s" % self._socket_scm_helper, - "%d" % self._qmp.get_sock_fd(), - ] - - if file_path is not None: - assert fd is None - fd_param.append(file_path) - else: - assert fd is not None - fd_param.append(str(fd)) - - devnull = open(os.path.devnull, "rb") - proc = subprocess.Popen( - fd_param, - stdin=devnull, - stdout=subprocess.PIPE, - stderr=subprocess.STDOUT, - close_fds=False, - ) - output = proc.communicate()[0] - if output: - LOG.debug(output) - - return proc.returncode - - @staticmethod - def _remove_if_exists(path): - """ - Remove file object at path if it exists - """ - try: - os.remove(path) - except OSError as exception: - if exception.errno == errno.ENOENT: - return - raise - - def is_running(self): - return self._popen is not None and self._popen.poll() is None - - def exitcode(self): - if self._popen is None: - return None - return self._popen.poll() - - def get_pid(self): - if not self.is_running(): - return None - return self._popen.pid - - def _load_io_log(self): - if self._qemu_log_path is not None: - with open(self._qemu_log_path, "r") as iolog: - self._iolog = iolog.read() - - def _base_args(self): - if isinstance(self._monitor_address, tuple): - moncdev = "socket,id=mon,host=%s,port=%s" % ( - self._monitor_address[0], - self._monitor_address[1], - ) - else: - moncdev = "socket,id=mon,path=%s" % self._vm_monitor - args = ["-chardev", moncdev, "-mon", "chardev=mon,mode=control"] - if self._machine is not None: - args.extend(["-machine", self._machine]) - if self._console_set: - self._console_address = os.path.join( - self._temp_dir, self._name + "-console.sock" - ) - chardev = ( - "socket,id=console,path=%s,server,nowait" - % self._console_address - ) - args.extend(["-chardev", chardev]) - if self._console_device_type is None: - args.extend(["-serial", "chardev:console"]) - else: - device = ( - "%s,chardev=console" % self._console_device_type - ) - args.extend(["-device", device]) - return args - - def _pre_launch(self): - self._temp_dir = tempfile.mkdtemp(dir=self._test_dir) - if self._monitor_address is not None: - self._vm_monitor = self._monitor_address - else: - self._vm_monitor = os.path.join( - self._temp_dir, self._name + "-monitor.sock" - ) - self._qemu_log_path = os.path.join( - self._temp_dir, self._name + ".log" - ) - self._qemu_log_file = open(self._qemu_log_path, "wb") - - self._qmp = qmp.QEMUMonitorProtocol( - self._vm_monitor, server=True - ) - - def _post_launch(self): - self._qmp.accept() - - def _post_shutdown(self): - if self._qemu_log_file is not None: - self._qemu_log_file.close() - self._qemu_log_file = None - - self._qemu_log_path = None - - if self._console_socket is not None: - self._console_socket.close() - self._console_socket = None - - if self._temp_dir is not None: - shutil.rmtree(self._temp_dir) - self._temp_dir = None - - def launch(self): - """ - Launch the VM and make sure we cleanup and expose the - command line/output in case of exception - """ - - if self._launched: - raise QEMUMachineError("VM already launched") - - self._iolog = None - self._qemu_full_args = None - try: - self._launch() - self._launched = True - except: - self.shutdown() - - LOG.debug("Error launching VM") - if self._qemu_full_args: - LOG.debug("Command: %r", " ".join(self._qemu_full_args)) - if self._iolog: - LOG.debug("Output: %r", self._iolog) - raise Exception(self._iolog) - raise - - def _launch(self): - """ - Launch the VM and establish a QMP connection - """ - devnull = open(os.path.devnull, "rb") - self._pre_launch() - self._qemu_full_args = ( - self._wrapper - + [self._binary] - + self._base_args() - + self._args - ) - LOG.debug( - "VM launch command: %r", " ".join(self._qemu_full_args) - ) - self._popen = subprocess.Popen( - self._qemu_full_args, - stdin=devnull, - stdout=self._qemu_log_file, - stderr=subprocess.STDOUT, - shell=False, - close_fds=False, - ) - self._post_launch() - - def wait(self): - """ - Wait for the VM to power off - """ - self._popen.wait() - self._qmp.close() - self._load_io_log() - self._post_shutdown() - - def shutdown(self): - """ - Terminate the VM and clean up - """ - if self.is_running(): - try: - self._qmp.cmd("quit") - self._qmp.close() - except: - self._popen.kill() - self._popen.wait() - - self._load_io_log() - self._post_shutdown() - - exitcode = self.exitcode() - if exitcode is not None and exitcode < 0: - msg = "qemu received signal %i: %s" - if self._qemu_full_args: - command = " ".join(self._qemu_full_args) - else: - command = "" - LOG.warn(msg, -exitcode, command) - - self._launched = False - - def qmp(self, cmd, conv_keys=True, **args): - """ - Invoke a QMP command and return the response dict - """ - qmp_args = dict() - for key, value in args.items(): - if conv_keys: - qmp_args[key.replace("_", "-")] = value - else: - qmp_args[key] = value - - return self._qmp.cmd(cmd, args=qmp_args) - - def command(self, cmd, conv_keys=True, **args): - """ - Invoke a QMP command. - On success return the response dict. - On failure raise an exception. - """ - reply = self.qmp(cmd, conv_keys, **args) - if reply is None: - raise qmp.QMPError("Monitor is closed") - if "error" in reply: - raise MonitorResponseError(reply) - return reply["return"] - - def get_qmp_event(self, wait=False): - """ - Poll for one queued QMP events and return it - """ - if len(self._events) > 0: - return self._events.pop(0) - return self._qmp.pull_event(wait=wait) - - def get_qmp_events(self, wait=False): - """ - Poll for queued QMP events and return a list of dicts - """ - events = self._qmp.get_events(wait=wait) - events.extend(self._events) - del self._events[:] - self._qmp.clear_events() - return events - - @staticmethod - def event_match(event, match=None): - """ - Check if an event matches optional match criteria. - - The match criteria takes the form of a matching subdict. The event is - checked to be a superset of the subdict, recursively, with matching - values whenever the subdict values are not None. - - This has a limitation that you cannot explicitly check for None values. - - Examples, with the subdict queries on the left: - - None matches any object. - - {"foo": None} matches {"foo": {"bar": 1}} - - {"foo": None} matches {"foo": 5} - - {"foo": {"abc": None}} does not match {"foo": {"bar": 1}} - - {"foo": {"rab": 2}} matches {"foo": {"bar": 1, "rab": 2}} - """ - if match is None: - return True - - try: - for key in match: - if key in event: - if not QEMUMachine.event_match( - event[key], match[key] - ): - return False - else: - return False - return True - except TypeError: - # either match or event wasn't iterable (not a dict) - return match == event - - def event_wait(self, name, timeout=60.0, match=None): - """ - event_wait waits for and returns a named event from QMP with a timeout. - - name: The event to wait for. - timeout: QEMUMonitorProtocol.pull_event timeout parameter. - match: Optional match criteria. See event_match for details. - """ - return self.events_wait([(name, match)], timeout) - - def events_wait(self, events, timeout=60.0): - """ - events_wait waits for and returns a named event from QMP with a timeout. - - events: a sequence of (name, match_criteria) tuples. - The match criteria are optional and may be None. - See event_match for details. - timeout: QEMUMonitorProtocol.pull_event timeout parameter. - """ - - def _match(event): - for name, match in events: - if event["event"] == name and self.event_match( - event, match - ): - return True - return False - - # Search cached events - for event in self._events: - if _match(event): - self._events.remove(event) - return event - - # Poll for new events - while True: - event = self._qmp.pull_event(wait=timeout) - if _match(event): - return event - self._events.append(event) - - return None - - def get_log(self): - """ - After self.shutdown or failed qemu execution, this returns the output - of the qemu process. - """ - return self._iolog - - def add_args(self, *args): - """ - Adds to the list of extra arguments to be given to the QEMU binary - """ - self._args.extend(args) - - def set_machine(self, machine_type): - """ - Sets the machine type - - If set, the machine type will be added to the base arguments - of the resulting QEMU command line. - """ - self._machine = machine_type - - def set_console(self, device_type=None): - """ - Sets the device type for a console device - - If set, the console device and a backing character device will - be added to the base arguments of the resulting QEMU command - line. - - This is a convenience method that will either use the provided - device type, or default to a "-serial chardev:console" command - line argument. - - The actual setting of command line arguments will be be done at - machine launch time, as it depends on the temporary directory - to be created. - - @param device_type: the device type, such as "isa-serial". If - None is given (the default value) a "-serial - chardev:console" command line argument will - be used instead, resorting to the machine's - default device type. - """ - self._console_set = True - self._console_device_type = device_type - - @property - def console_socket(self): - """ - Returns a socket connected to the console - """ - if self._console_socket is None: - self._console_socket = socket.socket( - socket.AF_UNIX, socket.SOCK_STREAM - ) - self._console_socket.connect(self._console_address) - return self._console_socket diff --git a/ucloud/host/qmp/qmp.py b/ucloud/host/qmp/qmp.py deleted file mode 100755 index ad187eb..0000000 --- a/ucloud/host/qmp/qmp.py +++ /dev/null @@ -1,257 +0,0 @@ -# QEMU Monitor Protocol Python class -# -# Copyright (C) 2009, 2010 Red Hat Inc. -# -# Authors: -# Luiz Capitulino -# -# This work is licensed under the terms of the GNU GPL, version 2. See -# the COPYING file in the top-level directory. - -import errno -import json -import logging -import socket - - -class QMPError(Exception): - pass - - -class QMPConnectError(QMPError): - pass - - -class QMPCapabilitiesError(QMPError): - pass - - -class QMPTimeoutError(QMPError): - pass - - -class QEMUMonitorProtocol(object): - #: Logger object for debugging messages - logger = logging.getLogger("QMP") - #: Socket's error class - error = socket.error - #: Socket's timeout - timeout = socket.timeout - - def __init__(self, address, server=False): - """ - Create a QEMUMonitorProtocol class. - - @param address: QEMU address, can be either a unix socket path (string) - or a tuple in the form ( address, port ) for a TCP - connection - @param server: server mode listens on the socket (bool) - @raise socket.error on socket connection errors - @note No connection is established, this is done by the connect() or - accept() methods - """ - self.__events = [] - self.__address = address - self.__sock = self.__get_sock() - self.__sockfile = None - if server: - self.__sock.setsockopt( - socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 - ) - self.__sock.bind(self.__address) - self.__sock.listen(1) - - def __get_sock(self): - if isinstance(self.__address, tuple): - family = socket.AF_INET - else: - family = socket.AF_UNIX - return socket.socket(family, socket.SOCK_STREAM) - - def __negotiate_capabilities(self): - greeting = self.__json_read() - if greeting is None or "QMP" not in greeting: - raise QMPConnectError - # Greeting seems ok, negotiate capabilities - resp = self.cmd("qmp_capabilities") - if "return" in resp: - return greeting - raise QMPCapabilitiesError - - def __json_read(self, only_event=False): - while True: - data = self.__sockfile.readline() - if not data: - return - resp = json.loads(data) - if "event" in resp: - self.logger.debug("<<< %s", resp) - self.__events.append(resp) - if not only_event: - continue - return resp - - def __get_events(self, wait=False): - """ - Check for new events in the stream and cache them in __events. - - @param wait (bool): block until an event is available. - @param wait (float): If wait is a float, treat it as a timeout value. - - @raise QMPTimeoutError: If a timeout float is provided and the timeout - period elapses. - @raise QMPConnectError: If wait is True but no events could be - retrieved or if some other error occurred. - """ - - # Check for new events regardless and pull them into the cache: - self.__sock.setblocking(0) - try: - self.__json_read() - except socket.error as err: - if err[0] == errno.EAGAIN: - # No data available - pass - self.__sock.setblocking(1) - - # Wait for new events, if needed. - # if wait is 0.0, this means "no wait" and is also implicitly false. - if not self.__events and wait: - if isinstance(wait, float): - self.__sock.settimeout(wait) - try: - ret = self.__json_read(only_event=True) - except socket.timeout: - raise QMPTimeoutError("Timeout waiting for event") - except: - raise QMPConnectError("Error while reading from socket") - if ret is None: - raise QMPConnectError("Error while reading from socket") - self.__sock.settimeout(None) - - def connect(self, negotiate=True): - """ - Connect to the QMP Monitor and perform capabilities negotiation. - - @return QMP greeting dict - @raise socket.error on socket connection errors - @raise QMPConnectError if the greeting is not received - @raise QMPCapabilitiesError if fails to negotiate capabilities - """ - self.__sock.connect(self.__address) - self.__sockfile = self.__sock.makefile() - if negotiate: - return self.__negotiate_capabilities() - - def accept(self): - """ - Await connection from QMP Monitor and perform capabilities negotiation. - - @return QMP greeting dict - @raise socket.error on socket connection errors - @raise QMPConnectError if the greeting is not received - @raise QMPCapabilitiesError if fails to negotiate capabilities - """ - self.__sock.settimeout(15) - self.__sock, _ = self.__sock.accept() - self.__sockfile = self.__sock.makefile() - return self.__negotiate_capabilities() - - def cmd_obj(self, qmp_cmd): - """ - Send a QMP command to the QMP Monitor. - - @param qmp_cmd: QMP command to be sent as a Python dict - @return QMP response as a Python dict or None if the connection has - been closed - """ - self.logger.debug(">>> %s", qmp_cmd) - try: - self.__sock.sendall(json.dumps(qmp_cmd).encode("utf-8")) - except socket.error as err: - if err[0] == errno.EPIPE: - return - raise socket.error(err) - resp = self.__json_read() - self.logger.debug("<<< %s", resp) - return resp - - def cmd(self, name, args=None, cmd_id=None): - """ - Build a QMP command and send it to the QMP Monitor. - - @param name: command name (string) - @param args: command arguments (dict) - @param cmd_id: command id (dict, list, string or int) - """ - qmp_cmd = {"execute": name} - if args: - qmp_cmd["arguments"] = args - if cmd_id: - qmp_cmd["id"] = cmd_id - return self.cmd_obj(qmp_cmd) - - def command(self, cmd, **kwds): - """ - Build and send a QMP command to the monitor, report errors if any - """ - ret = self.cmd(cmd, kwds) - if "error" in ret: - raise Exception(ret["error"]["desc"]) - return ret["return"] - - def pull_event(self, wait=False): - """ - Pulls a single event. - - @param wait (bool): block until an event is available. - @param wait (float): If wait is a float, treat it as a timeout value. - - @raise QMPTimeoutError: If a timeout float is provided and the timeout - period elapses. - @raise QMPConnectError: If wait is True but no events could be - retrieved or if some other error occurred. - - @return The first available QMP event, or None. - """ - self.__get_events(wait) - - if self.__events: - return self.__events.pop(0) - return None - - def get_events(self, wait=False): - """ - Get a list of available QMP events. - - @param wait (bool): block until an event is available. - @param wait (float): If wait is a float, treat it as a timeout value. - - @raise QMPTimeoutError: If a timeout float is provided and the timeout - period elapses. - @raise QMPConnectError: If wait is True but no events could be - retrieved or if some other error occurred. - - @return The list of available QMP events. - """ - self.__get_events(wait) - return self.__events - - def clear_events(self): - """ - Clear current list of pending events. - """ - self.__events = [] - - def close(self): - self.__sock.close() - self.__sockfile.close() - - def settimeout(self, timeout): - self.__sock.settimeout(timeout) - - def get_sock_fd(self): - return self.__sock.fileno() - - def is_scm_available(self): - return self.__sock.family == socket.AF_UNIX