Files
NetBrain_MCP/docs/data_storage.md
T
2026-05-22 12:17:24 +08:00

1001 lines
32 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# NetBrain MCP 数据存储设计
本文档详细描述NetBrain MCP系统的数据存储设计,包括实际的数据存储方案、数据模型、缓存策略和数据操作方法。
## 1. 数据存储架构概述
### 1.1 存储方案选择
NetBrain MCP系统采用**JSON文件存储**作为主要数据持久化方案,具有以下特点:
- **轻量级部署**:无需额外的数据库服务器
- **简单维护**:人类可读的JSON格式,便于调试和维护
- **快速启动**:无需数据库初始化和配置
- **跨平台兼容**:完全兼容Windows、Linux、macOS
- **备份简单**:数据文件可直接复制备份
### 1.2 数据存储结构
```
NetBrainMCP/
├── data/ # 主数据存储目录
│ ├── devices.json # 设备信息存储
│ └── credentials.json # 凭据信息存储(敏感数据)
├── resource_cache/ # 资源缓存目录
│ ├── device_*.json # 设备资源缓存
│ ├── topology_*.json # 拓扑资源缓存
│ └── scan_*.json # 扫描结果缓存
├── templates/ # 模板缓存目录
│ └── *.json # 模板元数据缓存
└── logs/ # 日志文件目录
├── netbrain_mcp.log # 应用主日志
├── device_connector.log # 设备连接日志
└── audit.log # 审计日志(安全增强)
```
## 2. 核心数据模型
### 2.1 网络设备模型
#### 2.1.1 NetworkDevice数据类
基于实际代码实现(`network_devices.py`第125-155行):
```python
@dataclass
class NetworkDevice:
"""网络设备模型"""
id: str = field(default_factory=lambda: str(uuid.uuid4()))
name: str = ""
ip_address: str = ""
device_type: DeviceType = DeviceType.OTHER
vendor: DeviceVendor = DeviceVendor.OTHER
platform: str = "" # Scrapli平台类型(重要字段)
model: str = ""
os_version: str = ""
status: DeviceStatus = DeviceStatus.UNKNOWN
location: str = ""
credential_id: Optional[str] = None
description: str = ""
tags: List[str] = field(default_factory=list)
last_seen: Optional[datetime] = None
created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime = field(default_factory=datetime.now)
custom_attributes: Dict[str, Any] = field(default_factory=dict)
```
#### 2.1.2 设备相关枚举
```python
class DeviceType(Enum):
"""网络设备类型枚举"""
ROUTER = "router"
SWITCH = "switch"
FIREWALL = "firewall"
LOAD_BALANCER = "load_balancer"
WIRELESS_CONTROLLER = "wireless_controller"
ACCESS_POINT = "access_point"
OTHER = "other"
class DeviceVendor(Enum):
"""网络设备厂商枚举"""
CISCO = "cisco"
HUAWEI = "huawei"
H3C = "h3c"
JUNIPER = "juniper"
ARISTA = "arista"
FORTINET = "fortinet"
CHECKPOINT = "checkpoint"
OTHER = "other"
class DeviceStatus(Enum):
"""设备状态枚举"""
ONLINE = "online"
OFFLINE = "offline"
UNREACHABLE = "unreachable"
MAINTENANCE = "maintenance"
UNKNOWN = "unknown"
```
#### 2.1.3 设备数据JSON示例
```json
{
"device-uuid-123": {
"id": "device-uuid-123",
"name": "Router-01",
"ip_address": "192.168.1.1",
"device_type": "router",
"vendor": "cisco",
"platform": "cisco_iosxe",
"model": "ISR4321",
"os_version": "16.09.04",
"status": "online",
"location": "数据中心机房A",
"credential_id": "cred-uuid-456",
"description": "核心路由器",
"tags": ["core", "production", "datacenter"],
"last_seen": "2024-01-15T10:30:45.123456",
"created_at": "2024-01-10T09:00:00.000000",
"updated_at": "2024-01-15T10:30:45.123456",
"custom_attributes": {
"management_vlan": "100",
"backup_schedule": "daily"
}
}
}
```
### 2.2 设备凭据模型
#### 2.2.1 DeviceCredential类
基于实际代码实现(`network_devices.py`第64-123行):
```python
class DeviceCredential:
"""设备凭据类"""
def __init__(self,
id: str = None,
name: str = "",
username: str = "",
password: str = "",
protocol: ConnectionProtocol = ConnectionProtocol.SSH,
port: int = None,
enable_password: str = None,
ssh_key_file: str = None):
self.id = id or str(uuid.uuid4())
self.name = name
self.username = username
self.password = password
self.protocol = protocol
self.port = port or (22 if protocol == ConnectionProtocol.SSH else 23)
self.enable_password = enable_password
self.ssh_key_file = ssh_key_file
```
#### 2.2.2 连接协议枚举
```python
class ConnectionProtocol(Enum):
"""连接协议枚举"""
SSH = "ssh"
TELNET = "telnet"
SNMP = "snmp"
HTTP = "http"
HTTPS = "https"
NETCONF = "netconf"
```
#### 2.2.3 凭据数据JSON示例
```json
{
"cred-uuid-456": {
"id": "cred-uuid-456",
"name": "思科设备SSH凭据",
"username": "admin",
"password": "cisco123",
"protocol": "ssh",
"port": 22,
"enable_password": "enable123",
"ssh_key_file": null
}
}
```
**⚠️ 安全注意**: 当前凭据以明文形式存储,生产环境中应实施加密存储。
## 3. 数据管理器实现
### 3.1 DeviceManager类
基于实际代码实现(`network_devices.py`第161-462行):
#### 3.1.1 初始化和配置
```python
class DeviceManager:
"""设备管理器,负责设备的创建、查询和管理"""
def __init__(self):
self.devices: Dict[str, NetworkDevice] = {}
self.credentials: Dict[str, DeviceCredential] = {}
self.data_dir = "data"
self.devices_file = os.path.join(self.data_dir, "devices.json")
self.credentials_file = os.path.join(self.data_dir, "credentials.json")
# 创建数据目录(如不存在)
os.makedirs(self.data_dir, exist_ok=True)
# 加载持久化数据
self.load_data()
logger.info("设备管理器初始化完成")
```
#### 3.1.2 数据持久化方法
**保存数据**
```python
def save_data(self):
"""将设备和凭据数据保存到文件"""
# 保存设备数据
devices_data = {}
for device_id, device in self.devices.items():
devices_data[device_id] = device.to_dict()
with open(self.devices_file, 'w', encoding='utf-8') as f:
json.dump(devices_data, f, ensure_ascii=False, indent=2)
# 保存凭据数据
credentials_data = {}
for cred_id, credential in self.credentials.items():
cred_dict = {
"id": credential.id,
"name": credential.name,
"username": credential.username,
"password": credential.password, # 明文存储(待加密)
"protocol": credential.protocol.value,
"port": credential.port,
"enable_password": credential.enable_password,
"ssh_key_file": credential.ssh_key_file
}
credentials_data[cred_id] = cred_dict
with open(self.credentials_file, 'w', encoding='utf-8') as f:
json.dump(credentials_data, f, ensure_ascii=False, indent=2)
logger.info(f"数据已保存到: {self.data_dir}")
```
**加载数据**
```python
def load_data(self):
"""从文件加载设备和凭据数据"""
# 加载设备数据
if os.path.exists(self.devices_file):
try:
with open(self.devices_file, 'r', encoding='utf-8') as f:
devices_data = json.load(f)
for device_id, device_dict in devices_data.items():
# 转换枚举类型
device_type = DeviceType(device_dict.get("device_type", "other"))
vendor = DeviceVendor(device_dict.get("vendor", "other"))
status = DeviceStatus(device_dict.get("status", "unknown"))
# 转换日期时间
created_at = datetime.fromisoformat(device_dict.get("created_at", datetime.now().isoformat()))
updated_at = datetime.fromisoformat(device_dict.get("updated_at", datetime.now().isoformat()))
last_seen = None
if device_dict.get("last_seen"):
last_seen = datetime.fromisoformat(device_dict.get("last_seen"))
# 创建设备对象
device = NetworkDevice(
id=device_id,
name=device_dict.get("name", ""),
ip_address=device_dict.get("ip_address", ""),
device_type=device_type,
vendor=vendor,
platform=device_dict.get("platform", ""),
model=device_dict.get("model", ""),
os_version=device_dict.get("os_version", ""),
status=status,
location=device_dict.get("location", ""),
credential_id=device_dict.get("credential_id"),
description=device_dict.get("description", ""),
tags=device_dict.get("tags", []),
last_seen=last_seen,
created_at=created_at,
updated_at=updated_at,
custom_attributes=device_dict.get("custom_attributes", {})
)
self.devices[device_id] = device
logger.info(f"从 {self.devices_file} 加载了 {len(self.devices)} 个设备")
except Exception as e:
logger.error(f"加载设备数据失败: {e}")
```
#### 3.1.3 CRUD操作
**添加设备**
```python
def add_device(self, device: NetworkDevice) -> str:
"""添加设备"""
self.devices[device.id] = device
logger.info(f"设备添加成功: {device.name} ({device.ip_address})")
self.save_data() # 立即保存到文件
return device.id
```
**查询设备**
```python
def get_device(self, device_id: str) -> Optional[NetworkDevice]:
"""获取设备"""
return self.devices.get(device_id)
def list_devices(self,
vendor: Optional[DeviceVendor] = None,
device_type: Optional[DeviceType] = None,
status: Optional[DeviceStatus] = None,
tag: Optional[str] = None) -> List[NetworkDevice]:
"""列出设备,支持过滤条件"""
devices = list(self.devices.values())
if vendor:
devices = [d for d in devices if d.vendor == vendor]
if device_type:
devices = [d for d in devices if d.device_type == device_type]
if status:
devices = [d for d in devices if d.status == status]
if tag:
devices = [d for d in devices if tag in d.tags]
return devices
```
**更新设备**
```python
def update_device(self, device_id: str, **kwargs) -> Optional[NetworkDevice]:
"""更新设备信息"""
device = self.get_device(device_id)
if not device:
logger.warning(f"设备不存在: {device_id}")
return None
for key, value in kwargs.items():
if hasattr(device, key):
setattr(device, key, value)
device.updated_at = datetime.now()
logger.info(f"设备更新成功: {device.name} ({device.ip_address})")
self.save_data() # 保存更改
return device
```
**删除设备**
```python
def delete_device(self, device_id: str) -> bool:
"""删除设备"""
if device_id in self.devices:
device = self.devices[device_id]
del self.devices[device_id]
logger.info(f"设备删除成功: {device.name} ({device.ip_address})")
self.save_data() # 保存更改
return True
return False
```
## 4. 缓存系统设计
### 4.1 资源缓存机制
基于实际代码实现(`mcp_resources.py`第56-180行):
#### 4.1.1 ResourceManager缓存实现
```python
class ResourceManager:
"""MCP资源管理器,负责资源注册和提供"""
def __init__(self):
self.resources = {}
self.resource_patterns = {}
self.resource_cache = {} # 内存缓存
self.cache_expiration = {} # 缓存过期时间
self.default_cache_ttl = 300 # 默认缓存5分钟
# 缓存目录
self.cache_dir = os.path.join(os.getcwd(), "resource_cache")
if not os.path.exists(self.cache_dir):
try:
os.makedirs(self.cache_dir)
logger.info(f"已创建资源缓存目录: {self.cache_dir}")
except Exception as e:
logger.warning(f"无法创建资源缓存目录: {str(e)}")
```
#### 4.1.2 双层缓存策略
**内存缓存**
```python
async def get_resource(self, uri: str, use_cache: bool = True, cache_ttl: Optional[int] = None) -> Dict[str, Any]:
"""获取资源"""
# 检查内存缓存
if use_cache and uri in self.resource_cache:
# 检查缓存是否过期
if uri in self.cache_expiration and self.cache_expiration[uri] > datetime.datetime.now():
logger.info(f"从内存缓存获取资源: {uri}")
return self.resource_cache[uri]
else:
# 缓存过期,从缓存中删除
logger.info(f"资源缓存已过期: {uri}")
if uri in self.resource_cache:
del self.resource_cache[uri]
if uri in self.cache_expiration:
del self.cache_expiration[uri]
```
**文件缓存**
```python
# 从文件缓存加载
if use_cache:
cache_file = self._get_cache_filename(uri)
if os.path.exists(cache_file):
try:
with open(cache_file, 'r', encoding='utf-8') as f:
cache_data = json.load(f)
# 检查缓存是否过期
if "expiration" in cache_data:
expiration = datetime.datetime.fromisoformat(cache_data["expiration"])
if expiration > datetime.datetime.now():
logger.info(f"从文件缓存加载资源: {uri}")
self.resource_cache[uri] = cache_data["data"]
self.cache_expiration[uri] = expiration
return cache_data["data"]
except Exception as e:
logger.warning(f"加载资源缓存文件失败: {str(e)}")
```
**缓存写入**
```python
# 缓存结果
if use_cache:
self.resource_cache[uri] = result
ttl = cache_ttl or self.default_cache_ttl
expiration = datetime.datetime.now() + datetime.timedelta(seconds=ttl)
self.cache_expiration[uri] = expiration
# 保存到文件缓存
try:
cache_file = self._get_cache_filename(uri)
with open(cache_file, 'w', encoding='utf-8') as f:
json.dump({
"data": result,
"expiration": expiration.isoformat()
}, f, ensure_ascii=False, indent=2)
except Exception as e:
logger.warning(f"保存资源缓存文件失败: {str(e)}")
```
#### 4.1.3 缓存文件命名
```python
def _get_cache_filename(self, uri: str) -> str:
"""获取缓存文件名"""
# 将URI转换为文件名安全的字符串
safe_uri = uri.replace(':', '_').replace('/', '_').replace('.', '_').replace('\\', '_')
return os.path.join(self.cache_dir, f"{safe_uri}.json")
```
#### 4.1.4 缓存清理机制
```python
def clear_cache(self, uri: Optional[str] = None) -> bool:
"""清除资源缓存"""
try:
if uri:
if uri in self.resource_cache:
del self.resource_cache[uri]
if uri in self.cache_expiration:
del self.cache_expiration[uri]
# 删除文件缓存
cache_file = self._get_cache_filename(uri)
if os.path.exists(cache_file):
os.remove(cache_file)
logger.info(f"已清除资源缓存: {uri}")
else:
self.resource_cache.clear()
self.cache_expiration.clear()
# 删除所有缓存文件
if os.path.exists(self.cache_dir):
for filename in os.listdir(self.cache_dir):
if filename.endswith('.json'):
os.remove(os.path.join(self.cache_dir, filename))
logger.info("已清除所有资源缓存")
return True
except Exception as e:
logger.error(f"清除缓存失败: {str(e)}")
return False
```
### 4.2 缓存配置策略
#### 4.2.1 默认缓存时间(TTL
```python
# 资源类型对应的缓存时间
CACHE_TTL_CONFIG = {
"device": 300, # 设备信息:5分钟
"config": 600, # 设备配置:10分钟
"topology": 900, # 拓扑信息:15分钟
"scan": 1800, # 扫描结果:30分钟
"system": 60, # 系统状态:1分钟
"credential": 3600 # 凭据信息:1小时
}
```
#### 4.2.2 缓存策略
- **即时缓存**:数据获取后立即缓存
- **延迟过期**:使用TTL控制缓存生命周期
- **主动清理**:提供手动清理接口
- **容量控制**:内存缓存大小限制(待实现)
## 5. 连接数据管理
### 5.1 连接状态存储
基于实际代码实现(`device_connector.py`第659-865行):
#### 5.1.1 ConnectionManager实现
```python
class ConnectionManager:
"""连接管理器"""
def __init__(self):
self.active_connections: Dict[str, DeviceConnector] = {}
# 连接键格式:"{device_id}:{credential_id}"
def _get_connection_key(self, device_id: str, credential_id: str) -> str:
"""生成连接键"""
return f"{device_id}:{credential_id}"
```
#### 5.1.2 连接状态数据结构
```python
# 活动连接信息示例
{
"device-123:cred-456": {
"device_connector": ScrapliConnector实例,
"device_info": {
"id": "device-123",
"name": "Router-01",
"ip_address": "192.168.1.1"
},
"credential_info": {
"id": "cred-456",
"username": "admin",
"protocol": "ssh"
},
"connection_status": {
"connected": True,
"last_activity": "2024-01-15T10:30:45",
"connection_time": "2024-01-15T10:25:30"
}
}
}
```
#### 5.1.3 连接查询接口
```python
def get_active_connections(self) -> List[Dict[str, Any]]:
"""获取活动连接列表"""
connections = []
for connection_key, connector in self.active_connections.items():
device_id, credential_id = connection_key.split(':', 1)
connection_info = {
"device_id": device_id,
"credential_id": credential_id,
"device_name": connector.device.name,
"device_ip": connector.device.ip_address,
"connected": connector.connected,
"last_activity": connector.last_activity.isoformat() if connector.last_activity else None,
"connection_key": connection_key
}
connections.append(connection_info)
return connections
```
## 6. 日志数据管理
### 6.1 日志存储结构
#### 6.1.1 日志文件分类
**应用主日志**`logs/netbrain_mcp.log`):
```
2024-01-15 10:30:45,123 - netbrain_mcp - INFO - MCP服务器已启动
2024-01-15 10:31:02,456 - network_devices - INFO - 设备添加成功: Router-01 (192.168.1.1)
2024-01-15 10:31:15,789 - mcp_resources - INFO - 从缓存获取资源: device/device-123
```
**设备连接日志**`logs/device_connector.log`):
```
2024-01-15 10:32:00,123 - device_connector - INFO - 正在通过Scrapli连接到设备: Router-01 (192.168.1.1)
2024-01-15 10:32:03,456 - device_connector - INFO - 已成功连接到设备: Router-01 (192.168.1.1)
2024-01-15 10:32:10,789 - device_connector - INFO - 命令 'show version' 在设备 Router-01 上执行成功
```
**审计日志**`logs/audit.log`,安全增强功能):
```json
{"timestamp": "2024-01-15T10:30:45.123Z", "event_type": "device.added", "user_id": "admin", "resource": "device:123", "action": "add", "result": "success"}
{"timestamp": "2024-01-15T10:32:10.789Z", "event_type": "command.executed", "user_id": "admin", "resource": "device:123", "details": {"command": "show version"}}
```
#### 6.1.2 日志格式化
基于实际代码实现(所有模块的JsonFormatter):
```python
class JsonFormatter(logging.Formatter):
def __init__(self, fmt=None, datefmt=None, style='%'):
super().__init__(fmt, datefmt, style)
def format(self, record):
log_record = super().format(record)
return log_record.encode('utf-8', errors='replace').decode('utf-8')
# 配置日志格式
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.StreamHandler(stream=sys.stdout)
]
)
# 设置所有处理器使用UTF-8编码格式化
for handler in logging.root.handlers:
if isinstance(handler, logging.StreamHandler):
handler.setFormatter(JsonFormatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
```
## 7. 数据备份和恢复
### 7.1 数据备份策略
#### 7.1.1 手动备份
```bash
# 备份核心数据文件
cp -r data/ backup/data_$(date +%Y%m%d_%H%M%S)/
# 备份缓存数据(可选)
cp -r resource_cache/ backup/cache_$(date +%Y%m%d_%H%M%S)/
# 备份日志文件
cp -r logs/ backup/logs_$(date +%Y%m%d_%H%M%S)/
```
#### 7.1.2 自动备份脚本
```python
# backup.py - 数据备份脚本
import os
import shutil
import datetime
from pathlib import Path
def backup_data():
"""执行数据备份"""
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
backup_dir = Path(f"backup/backup_{timestamp}")
backup_dir.mkdir(parents=True, exist_ok=True)
# 备份数据文件
if Path("data").exists():
shutil.copytree("data", backup_dir / "data")
print(f"数据文件已备份到: {backup_dir / 'data'}")
# 备份重要日志
if Path("logs").exists():
shutil.copytree("logs", backup_dir / "logs")
print(f"日志文件已备份到: {backup_dir / 'logs'}")
return backup_dir
if __name__ == "__main__":
backup_path = backup_data()
print(f"备份完成: {backup_path}")
```
### 7.2 数据恢复
#### 7.2.1 数据恢复流程
```python
def restore_data(backup_path: str):
"""从备份恢复数据"""
backup_dir = Path(backup_path)
if not backup_dir.exists():
raise ValueError(f"备份目录不存在: {backup_path}")
# 备份当前数据(安全措施)
current_backup = backup_data()
print(f"当前数据已备份到: {current_backup}")
try:
# 恢复数据文件
if (backup_dir / "data").exists():
if Path("data").exists():
shutil.rmtree("data")
shutil.copytree(backup_dir / "data", "data")
print("数据文件恢复完成")
# 重新加载数据到内存
device_manager.load_data()
print("数据已重新加载到内存")
except Exception as e:
print(f"恢复失败: {str(e)}")
raise
```
## 8. 性能优化
### 8.1 数据访问优化
#### 8.1.1 内存索引
```python
class OptimizedDeviceManager(DeviceManager):
"""优化的设备管理器"""
def __init__(self):
super().__init__()
self._ip_index = {} # IP地址索引
self._name_index = {} # 设备名称索引
self._vendor_index = {} # 厂商索引
self._build_indexes()
def _build_indexes(self):
"""构建索引"""
for device_id, device in self.devices.items():
# IP地址索引
self._ip_index[device.ip_address] = device_id
# 名称索引
self._name_index[device.name.lower()] = device_id
# 厂商索引
vendor = device.vendor.value
if vendor not in self._vendor_index:
self._vendor_index[vendor] = []
self._vendor_index[vendor].append(device_id)
def get_device_by_ip(self, ip_address: str) -> Optional[NetworkDevice]:
"""通过IP地址快速查找设备"""
device_id = self._ip_index.get(ip_address)
return self.devices.get(device_id) if device_id else None
```
#### 8.1.2 批量操作优化
```python
def bulk_add_devices(self, devices: List[NetworkDevice]) -> List[str]:
"""批量添加设备(减少I/O操作)"""
device_ids = []
for device in devices:
self.devices[device.id] = device
device_ids.append(device.id)
logger.info(f"设备添加到内存: {device.name}")
# 一次性保存所有更改
self.save_data()
logger.info(f"批量添加 {len(devices)} 个设备完成")
return device_ids
```
### 8.2 缓存优化
#### 8.2.1 预加载策略
```python
async def preload_common_resources(self):
"""预加载常用资源"""
common_resources = [
"system/status",
"credentials",
"topology/statistics"
]
for uri in common_resources:
try:
await self.get_resource(uri, use_cache=True)
logger.info(f"预加载资源: {uri}")
except Exception as e:
logger.warning(f"预加载资源失败: {uri}, {str(e)}")
```
#### 8.2.2 智能缓存清理
```python
def cleanup_expired_cache(self):
"""清理过期缓存"""
now = datetime.datetime.now()
expired_uris = []
for uri, expiration in self.cache_expiration.items():
if expiration <= now:
expired_uris.append(uri)
for uri in expired_uris:
self.clear_cache(uri)
logger.info(f"清理了 {len(expired_uris)} 个过期缓存项")
```
## 9. 数据迁移
### 9.1 版本升级迁移
#### 9.1.1 数据格式版本管理
```python
DATA_FORMAT_VERSION = "1.0"
def check_data_format_version():
"""检查数据格式版本"""
version_file = Path("data/version.json")
if not version_file.exists():
# 首次运行,创建版本文件
create_version_file()
return True
with open(version_file, 'r') as f:
version_data = json.load(f)
current_version = version_data.get("version", "0.0")
if current_version != DATA_FORMAT_VERSION:
logger.info(f"检测到数据格式升级: {current_version} -> {DATA_FORMAT_VERSION}")
migrate_data(current_version, DATA_FORMAT_VERSION)
update_version_file()
return True
```
#### 9.1.2 数据迁移处理
```python
def migrate_data(from_version: str, to_version: str):
"""执行数据迁移"""
logger.info(f"开始数据迁移: {from_version} -> {to_version}")
# 备份原始数据
backup_path = backup_data()
logger.info(f"原始数据已备份到: {backup_path}")
try:
if from_version == "0.9" and to_version == "1.0":
migrate_0_9_to_1_0()
# 添加其他版本迁移逻辑
logger.info("数据迁移完成")
except Exception as e:
logger.error(f"数据迁移失败: {str(e)}")
raise
def migrate_0_9_to_1_0():
"""0.9版本到1.0版本的迁移"""
# 示例:添加platform字段
for device in device_manager.devices.values():
if not hasattr(device, 'platform') or not device.platform:
# 根据vendor自动推断platform
if device.vendor == DeviceVendor.CISCO:
device.platform = "cisco_iosxe"
elif device.vendor == DeviceVendor.HUAWEI:
device.platform = "huawei_vrp"
# 添加其他厂商映射
# 保存迁移后的数据
device_manager.save_data()
```
## 10. 监控和维护
### 10.1 数据健康检查
```python
def health_check():
"""数据健康检查"""
issues = []
# 检查数据文件完整性
if not Path("data/devices.json").exists():
issues.append("设备数据文件缺失")
if not Path("data/credentials.json").exists():
issues.append("凭据数据文件缺失")
# 检查数据一致性
for device in device_manager.devices.values():
if device.credential_id and not device_manager.get_credential(device.credential_id):
issues.append(f"设备 {device.name} 引用的凭据不存在: {device.credential_id}")
# 检查缓存目录
if not Path("resource_cache").exists():
issues.append("资源缓存目录缺失")
if issues:
logger.warning(f"发现 {len(issues)} 个数据问题:")
for issue in issues:
logger.warning(f" - {issue}")
else:
logger.info("数据健康检查通过")
return len(issues) == 0
```
### 10.2 存储空间管理
```python
def get_storage_usage():
"""获取存储使用情况"""
def get_dir_size(path):
total = 0
for dirpath, dirnames, filenames in os.walk(path):
for filename in filenames:
filepath = os.path.join(dirpath, filename)
if os.path.exists(filepath):
total += os.path.getsize(filepath)
return total
usage = {
"data": get_dir_size("data") if Path("data").exists() else 0,
"cache": get_dir_size("resource_cache") if Path("resource_cache").exists() else 0,
"logs": get_dir_size("logs") if Path("logs").exists() else 0,
"templates": get_dir_size("templates") if Path("templates").exists() else 0
}
total = sum(usage.values())
logger.info(f"存储使用情况 (总计: {total / 1024 / 1024:.2f} MB):")
for category, size in usage.items():
logger.info(f" {category}: {size / 1024 / 1024:.2f} MB")
return usage
```
## 11. 总结
NetBrain MCP的数据存储设计采用了轻量级、高效的JSON文件存储方案,具有以下特点:
### 11.1 设计优势
- **简单可靠**:JSON格式人类可读,便于维护和调试
- **无依赖性**:无需额外的数据库服务器
- **跨平台**:完全兼容各种操作系统
- **快速部署**:零配置即可使用
- **易于备份**:简单的文件复制即可备份
### 11.2 性能特性
- **双层缓存**:内存+文件双层缓存机制
- **智能过期**:基于TTL的缓存过期策略
- **批量操作**:支持批量数据操作以减少I/O
- **索引优化**:内存索引提高查询性能
### 11.3 扩展能力
- **版本迁移**:完整的数据格式升级迁移机制
- **健康检查**:数据完整性和一致性检查
- **监控告警**:存储使用情况监控
- **安全增强**:计划实施数据加密存储
这种数据存储设计很好地平衡了简单性、性能和可维护性,为NetBrain MCP系统提供了稳定可靠的数据持久化基础。