155 line
4.5 KiB
Python
155 line
4.5 KiB
Python
"""libvirt 多主机连接管理器"""
|
|
from __future__ import annotations
|
|
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()
|