Files
kvm-manager/backend/app/libvirt_conn.py
T

155 строки
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()