Skip to content

Commit

Permalink
Monitor: Support monitor on the multi-host
Browse files Browse the repository at this point in the history
Signed-off-by: Yongxue Hong <yhong@redhat.com>
  • Loading branch information
YongxueHong committed Oct 7, 2024
1 parent e69c15e commit 9e59b2e
Show file tree
Hide file tree
Showing 13 changed files with 1,822 additions and 170 deletions.
443 changes: 273 additions & 170 deletions virttest/qemu_monitor.py

Large diffs are not rendered by default.

Empty file.
38 changes: 38 additions & 0 deletions virttest/vt_agent/connect_client/qemu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import logging
import os

from vt_agent.core.data_dir import LOG_DIR

from virttest.vt_monitor import client

LOG = logging.getLogger("avocado.service." + __name__)


def create_connect_client(instance_id, name, protocol, client_params, log_file=None):
backend = client_params.get("backend")

if log_file is None:
log_file = "%s-instance-%s.log" % (name, instance_id)
log_file = os.path.join(LOG_DIR, log_file)

if backend == "socket":
host = client_params.get("host")
port = client_params.get("port")
path = client_params.get("path")
if host and port:
address = (host, port)
backend_type = "tcp_socket"
elif path:
address = path
backend_type = "unix_socket"
else:
raise ValueError("No address specified for connect client")
else:
raise NotImplementedError("Not support connect backend type %s" % backend)

if protocol == "qmp":
return client.QMPMonitor(instance_id, name, backend_type, address, log_file)
elif protocol == "hmp":
return client.HumanMonitor(instance_id, name, backend_type, address, log_file)
else:
raise NotImplementedError("Unsupported connect protocol %s" % protocol)
19 changes: 19 additions & 0 deletions virttest/vt_agent/managers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import logging

from .connect import ConnectManager
from .console import ConsoleManager

connect_mgr = ConnectManager()
console_mgr = ConsoleManager()

LOG = logging.getLogger("avocado.service." + __name__)

# workaround to skip the failure of the import managers
try:
from .image import ImageHandlerManager
from .resource_backing import ResourceBackingManager

resbacking_mgr = ResourceBackingManager()
image_handler_mgr = ImageHandlerManager()
except ImportError as e:
LOG.warning(f"Failed to import managers: {e}")
48 changes: 48 additions & 0 deletions virttest/vt_agent/managers/connect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import logging

from vt_agent.connect_client import qemu

LOG = logging.getLogger("avocado.service." + __name__)


class ConnectManager(object):
"""
Manager the connection to the monitor
"""

def __init__(self):
self._connects = {}

@staticmethod
def create_connect(
instance_id, instance_type, name, protocol, params, log_file=None
):
LOG.info(f"<Instance: {instance_id}> Create a {protocol} connection {name}")
if instance_type == "qemu":
connect = qemu.create_connect_client(
instance_id, name, protocol, params, log_file=log_file
)
return connect
elif instance_type == "libvirt":
raise NotImplementedError("Not implemented")
else:
raise NotImplementedError(f"Unsupported connect type {instance_type}")

def register_connect(self, con_id, connect):
if con_id in self._connects:
raise ValueError
self._connects[con_id] = connect

def unregister_connect(self, con_id):
del self._connects[con_id]

def get_connect(self, con_id=None):
return self._connects.get(con_id)

def get_connects_by_instance(self, instance_id):
connects = []
for connect in self._connects.values():
if connect.instance_id == instance_id:
connects.append(connect)

return connects
78 changes: 78 additions & 0 deletions virttest/vt_agent/managers/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import logging
import os

import aexpect
from vt_agent.core.data_dir import LOG_DIR

from virttest import utils_logfile, utils_misc

LOG = logging.getLogger("avocado.service." + __name__)


class Console(object):
def __init__(self, instance_id, console_type, console=None):
self._instance_id = instance_id
self._console_type = console_type
self._console = console

@property
def instance_id(self):
return self._instance_id


class SerialConsole(Console):
def __init__(self, instance_id, console=None):
super(SerialConsole, self).__init__(instance_id, "serial", console)

def __getattr__(self, item):
if self._console:
return getattr(self._console, item)


class ConsoleManager(object):
"""
Manager the connection to the console for the VM
"""

def __init__(self):
self._consoles = {}

@staticmethod
def create_console(name, instance_id, console_type, file_name, params={}):
if console_type == "serial":
log_name = os.path.join(LOG_DIR, f"{console_type}-{name}-{instance_id}.log")
serial_session = aexpect.ShellSession(
"nc -U %s" % file_name,
auto_close=False,
output_func=utils_logfile.log_line,
output_params=(log_name,),
prompt=params.get("shell_prompt", "[\#\$]"),
status_test_command=params.get("status_test_command", "echo $?"),
encoding="UTF-8",
)
return SerialConsole(instance_id, console=serial_session)
elif console_type == "vnc":
raise NotImplementedError()
elif console_type == "spice":
raise NotImplementedError()
else:
raise NotImplementedError("Not support console type %s" % console_type)

def register_console(self, con_id, console):
if con_id in self._consoles:
raise ValueError
self._consoles[con_id] = console

def unregister_console(self, con_id):
del self._consoles[con_id]

def get_console(self, con_id):
return self._consoles.get(con_id)

def get_consoles_by_instance(self, instance_id):
consoles = []
for console in self._consoles.values():
if console.instance_id == instance_id:
consoles.append(console)

return consoles
1 change: 1 addition & 0 deletions virttest/vt_agent/services/virt/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from . import connect, serial, vmm
80 changes: 80 additions & 0 deletions virttest/vt_agent/services/virt/connect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import logging

from managers import connect_mgr, vmm

from virttest import utils_misc
from virttest.qemu_devices import qdevices

VMM = vmm.VirtualMachinesManager()
LOG = logging.getLogger("avocado.service." + __name__)


def open_connect(instance_id, name, protocol, log_file=None):
devices = VMM.get_instance(instance_id).get("devices")
for device in devices:
if isinstance(device, qdevices.CharDevice):
if name == device.get_qid():
client_params = device.params
break
else:
raise ValueError(f"Not found the qemu client params for {name}")
connect = connect_mgr.create_connect(
instance_id, "qemu", name, protocol, client_params, log_file
)
connect_id = utils_misc.generate_random_string(16)
connect_mgr.register_connect(connect_id, connect)
return connect_id


def is_connected(instance_id, name):
connects = connect_mgr.get_connects_by_instance(instance_id)
for connect in connects:
if connect.name == name:
return True
return False


def close_connect(connect_id):
connect = connect_mgr.get_connect(connect_id)
return connect.close()


def execute_connect_data(
connect_id, data, timeout=None, debug=False, fd=None, data_format=None
):
connect = connect_mgr.get_connect(connect_id)
LOG.info(
f"<Instance: {connect.instance_id}; Monitor: {connect.name}> "
f"Executing {connect.protocol} connect data: {data}"
)
return connect.execute_data(data, timeout, debug, fd, data_format)


def get_connect_events(connect_id):
connect = connect_mgr.get_connect(connect_id)
return connect.get_events()


def get_connect_event(connect_id, name):
connect = connect_mgr.get_connect(connect_id)
return connect.get_event(name)


def clear_connect_events(connect_id):
connect = connect_mgr.get_connect(connect_id)
return connect.clear_events()


def clear_connect_event(connect_id, name):
connect = connect_mgr.get_connect(connect_id)
return connect.clear_event(name)


def is_responsive(connect_id):
connect = connect_mgr.get_connect(connect_id)
return connect.is_responsive()


def get_monitor_log_files(monitor_id):
monitor = connect_mgr.get_connect(monitor_id)
return [log_file for log_file in monitor.open_log_files]
Loading

0 comments on commit 9e59b2e

Please sign in to comment.