"""libvirt 多主机连接管理器""" import libvirt from contextlib import contextmanager import logging from app.hosts import get_host, update_host_status, list_hosts logger = logging.getLogger(__name__) class LibvirtConnection: """单个 libvirt 连接管理器(只读保持,读写短连接)""" def __init__(self, uri: str, host_id: str = "local"): self._uri = uri self._host_id = host_id self._conn = None def _connect(self): """建立只读连接""" try: conn = libvirt.openReadOnly(self._uri) if conn is None: raise ConnectionError(f"无法连接到 libvirt: {self._uri}") return conn except libvirt.libvirtError as e: logger.error(f"libvirt 连接错误 ({self._host_id}): {e}") raise def _connect_rw(self): """建立可读写连接""" try: conn = libvirt.open(self._uri) if conn is None: raise ConnectionError(f"无法连接到 libvirt (RW): {self._uri}") return conn except libvirt.libvirtError as e: logger.error(f"libvirt RW 连接错误 ({self._host_id}): {e}") raise @property def conn(self): """获取只读连接(带自动重连)""" try: if self._conn is None or not self._conn.isAlive(): self._conn = self._connect() except Exception: self._conn = self._connect() return self._conn @contextmanager def get_rw(self): """获取读写连接(短连接,用完关闭)""" conn = None try: conn = self._connect_rw() yield conn finally: if conn is not None: try: conn.close() except Exception: pass def get_host_info(self): """获取宿主机信息""" c = self.conn return { "hostname": c.getHostname(), "hypervisor": c.getType(), "libvirt_version": c.getLibVersion(), "hypervisor_version": c.getVersion(), "cpu_model": c.getInfo()[5] if len(c.getInfo()) > 5 else "Unknown", "cpu_cores": c.getInfo()[2], "memory_total": c.getInfo()[1], # KB "cpu_speed": c.getInfo()[3], # MHz } def close(self): """关闭连接""" if self._conn is not None: try: self._conn.close() except Exception: pass self._conn = None class ConnectionPool: """多主机连接池""" def __init__(self): self._pool: dict[str, LibvirtConnection] = {} def get(self, host_id: str = "local") -> LibvirtConnection: """获取指定主机的连接管理器""" if host_id not in self._pool: host = get_host(host_id) if not host: raise ValueError(f"主机 '{host_id}' 不存在") self._pool[host_id] = LibvirtConnection(host.uri, host_id) return self._pool[host_id] def remove(self, host_id: str): """移除并关闭指定主机的连接""" if host_id in self._pool: self._pool[host_id].close() del self._pool[host_id] def get_conn(self, host_id: str = "local"): """快捷获取 libvirt 只读连接""" return self.get(host_id).conn @contextmanager def get_rw(self, host_id: str = "local"): """快捷获取 libvirt 读写连接""" mgr = self.get(host_id) with mgr.get_rw() as conn: yield conn def refresh_all(self): """刷新所有连接状态""" for host_id, mgr in list(self._pool.items()): try: alive = mgr.conn.isAlive() update_host_status(host_id, "online" if alive else "offline") except Exception: update_host_status(host_id, "offline") mgr.close() # 全局连接池单例 conn_pool = ConnectionPool() # 保持向后兼容:本机连接的直接引用 libvirt_conn = property(lambda self: conn_pool.get("local")) class _CompatConn: """向后兼容包装,让现有 `libvirt_conn.conn` 等调用继续工作""" @property def conn(self): return conn_pool.get_conn("local") def get_rw(self): return conn_pool.get_rw("local") def get_host_info(self): return conn_pool.get("local").get_host_info() # 全局兼容单例 libvirt_conn = _CompatConn()