在这里插入图片描述

每日一句正能量

生活在这个世界上,没有人一帆风顺,也没有人天天幸运。有惊喜,有意外;有成功,有失败。只有吃过命运的苦,才会享受到生活的甜。

前言

摘要: Xshell作为Windows平台最主流的SSH客户端,其内置的脚本和插件机制常被忽视。本文将深入Xshell的Python扩展接口,从基础的会话自动化到复杂的网络设备批量管理平台,带你解锁终端工具的二次开发能力,让重复的运维工作自动化、智能化。


目录


一、Xshell架构解析:为什么需要插件开发

1.1 Xshell的技术架构

Xshell采用分层架构设计,其脚本引擎基于Python 2.7/3.x嵌入式解释器,通过COM接口与主程序交互:

┌─────────────────────────────────────────┐
│           Xshell主程序 (Xshell.exe)      │
│  ┌─────────────┐  ┌─────────────────┐  │
│  │  会话管理器   │  │   UI渲染引擎     │  │
│  │  (Session)  │  │   (MFC/Qt)      │  │
│  └──────┬──────┘  └─────────────────┘  │
│         │                               │
│  ┌──────┴───────────────────────────┐   │
│  │      脚本引擎层 (Scripting)       │   │
│  │  ┌─────────┐  ┌───────────────┐  │   │
│  │  │ Python  │  │  VBScript/JScript │  │   │
│  │  │ Engine  │  │     Engine      │  │   │
│  │  └────┬────┘  └───────────────┘  │   │
│  └───────┼──────────────────────────┘   │
│          │                               │
│  ┌───────┴───────────────────────────┐   │
│  │      扩展接口层 (XAPI)             │   │
│  │  - Session对象 (连接/断开/发送)    │   │
│  │  - Screen对象 (屏幕捕获/解析)      │   │
│  │  - Dialog对象 (UI交互)            │   │
│  │  - FileTransfer对象 (SFTP/SCP)    │   │
│  └───────────────────────────────────┘   │
└─────────────────────────────────────────┘

1.2 插件开发的典型场景

场景 原生Xshell限制 插件解决方案
批量设备巡检 只能逐个会话操作 Python脚本批量并发执行
配置合规检查 无自动化比对能力 自动采集+基线比对+报告生成
密码轮换 手动修改数百台设备 自动化SSH密钥分发与验证
网络拓扑发现 无网络可视化能力 采集LLDP/CDP信息生成拓扑图
审计合规 本地日志易丢失 实时上传操作日志到SIEM

二、环境准备:Xshell Python引擎深度配置

2.1 Python环境配置

Xshell 7+支持Python 3.8+,但需要手动配置:

# 检查Xshell内置Python版本
# 在Xshell中:工具 -> 脚本 -> 运行 -> 输入以下代码

import sys
import platform

print(f"Python版本: {platform.python_version()}")
print(f"解释器路径: {sys.executable}")
print(f"系统路径: {sys.path}")

# 输出示例:
# Python版本: 3.8.10
# 解释器路径: C:\Program Files (x86)\NetSarang\Xshell 7\Python\python.exe
# 系统路径: ['', 'C:\\Program Files (x86)\\NetSarang\\Xshell 7\\Python\\python38.zip', ...]

2.2 扩展包安装

Xshell使用独立的Python环境,需要手动安装第三方库:

:: 以管理员身份运行CMD,进入Xshell Python目录
cd "C:\Program Files (x86)\NetSarang\Xshell 7\Python"

:: 安装必要的扩展包
.\python.exe -m pip install paramiko netmiko textfsm jinja2 openpyxl
.\python.exe -m pip install pyvis networkx matplotlib  :: 用于拓扑可视化

:: 验证安装
.\python.exe -c "import paramiko; print(paramiko.__version__)"

2.3 开发目录结构

XshellPlugins/
├── core/                       # 核心框架
│   ├── __init__.py
│   ├── session_manager.py      # 会话管理封装
│   ├── device_connector.py     # 设备连接基类
│   └── logger.py               # 统一日志
├── plugins/                    # 功能插件
│   ├── __init__.py
│   ├── network_discovery.py    # 网络发现
│   ├── config_backup.py        # 配置备份
│   ├── compliance_check.py     # 合规检查
│   └── password_rotation.py    # 密码轮换
├── templates/                  # 配置模板
│   ├── cisco_base_config.txt
│   ├── huawei_base_config.txt
│   └── report_template.html
├── output/                     # 输出目录
│   ├── logs/
│   ├── backups/
│   └── reports/
└── xshell_plugin_main.py       # Xshell入口脚本

三、基础实战:会话自动化与批量操作

3.1 Xshell Python API核心对象

# xshell_api_wrapper.py - Xshell API封装层
# 在Xshell中运行,使用xsh.Session等内置对象

import time
import re

class XshellSession:
    """
    封装Xshell会话操作,提供Pythonic接口
    """
    def __init__(self, session_name=None):
        self.session = xsh.Session if session_name is None else xsh.Sessions(session_name)
        self.screen = xsh.Screen
        self.dialog = xsh.Dialog
        
    def connect(self, hostname, username, password, port=22):
        """建立新连接"""
        try:
            self.session.Open(f"ssh://{username}:{password}@{hostname}:{port}")
            time.sleep(2)  # 等待连接建立
            return self.is_connected()
        except Exception as e:
            print(f"连接失败: {e}")
            return False
    
    def is_connected(self):
        """检查连接状态"""
        return self.session.Connected
    
    def send_command(self, command, wait_for_prompt=True, timeout=30):
        """
        发送命令并等待响应
        """
        if not self.is_connected():
            raise ConnectionError("会话未连接")
        
        # 发送命令
        self.screen.Send(command + "\r")
        
        if not wait_for_prompt:
            return None
        
        # 等待提示符出现(支持多厂商)
        prompts = [
            r'[>#]\s*$',           # Cisco/Huawei/Generic
            r'\$\s*$',             # Linux普通用户
            r'%\s*$',              # Juniper
            r']\s*$',              # H3C
        ]
        
        start_time = time.time()
        while time.time() - start_time < timeout:
            # 获取当前屏幕内容
            screen_content = self.screen.Get(1, 1, self.screen.Rows, self.screen.Columns)
            
            for prompt in prompts:
                if re.search(prompt, screen_content, re.MULTILINE):
                    # 提取命令输出(去掉命令本身和提示符)
                    lines = screen_content.strip().split('\n')
                    # 过滤掉命令行和最后的提示符
                    output_lines = []
                    capture = False
                    for line in lines:
                        if command in line:
                            capture = True
                            continue
                        if re.search(prompt, line):
                            break
                        if capture:
                            output_lines.append(line)
                    
                    return '\n'.join(output_lines)
            
            time.sleep(0.5)
        
        raise TimeoutError(f"等待提示符超时({timeout}秒)")
    
    def send_config_commands(self, commands, save_config=True):
        """
        发送配置命令序列(自动进入配置模式)
        """
        # 检测设备类型并进入配置模式
        hostname = self.send_command("show version")  # Cisco
        if "% Invalid" in hostname:
            hostname = self.send_command("display version")  # Huawei/H3C
        
        # 进入配置模式
        config_entry = "configure terminal" if "Cisco" in hostname else "system-view"
        self.send_command(config_entry)
        
        # 逐条发送配置
        for cmd in commands:
            output = self.send_command(cmd)
            if "error" in output.lower() or "invalid" in output.lower():
                print(f"配置命令失败: {cmd}")
                print(f"错误信息: {output}")
        
        # 保存配置
        if save_config:
            save_cmd = "write memory" if "Cisco" in hostname else "save"
            self.send_command(save_cmd)
            self.screen.Send("y\r")  # 确认保存
    
    def disconnect(self):
        """断开连接"""
        if self.is_connected():
            self.session.Close()
    
    def __enter__(self):
        return self
    
    def __exit__(self, exc_type, exc_val, exc_tb):
        self.disconnect()
        return False

3.2 批量设备操作框架

# batch_executor.py - 批量执行框架

import threading
import queue
import json
from concurrent.futures import ThreadPoolExecutor, as_completed
from dataclasses import dataclass
from typing import List, Callable, Optional

@dataclass
class Device:
    hostname: str
    ip: str
    vendor: str  # cisco/huawei/h3c/juniper/linux
    username: str
    password: str
    port: int = 22
    enable_password: Optional[str] = None

@dataclass
class TaskResult:
    device: Device
    success: bool
    output: str
    error_message: Optional[str] = None
    execution_time: float = 0.0

class BatchExecutor:
    """
    批量设备操作执行器
    支持并发控制和结果收集
    """
    def __init__(self, max_workers=10, timeout=300):
        self.max_workers = max_workers
        self.timeout = timeout
        self.results = []
        self.lock = threading.Lock()
        
    def execute_on_devices(self, devices: List[Device], 
                          task_func: Callable[[Device], TaskResult]) -> List[TaskResult]:
        """
        在多个设备上并行执行任务
        
        Args:
            devices: 设备列表
            task_func: 任务函数,接收Device对象返回TaskResult
        """
        self.results = []
        
        with ThreadPoolExecutor(max_workers=self.max_workers) as executor:
            # 提交所有任务
            future_to_device = {
                executor.submit(self._wrapped_task, device, task_func): device 
                for device in devices
            }
            
            # 收集结果
            for future in as_completed(future_to_device, timeout=self.timeout):
                device = future_to_device[future]
                try:
                    result = future.result()
                    with self.lock:
                        self.results.append(result)
                except Exception as e:
                    error_result = TaskResult(
                        device=device,
                        success=False,
                        output="",
                        error_message=str(e),
                        execution_time=0.0
                    )
                    with self.lock:
                        self.results.append(error_result)
        
        return self.results
    
    def _wrapped_task(self, device: Device, task_func) -> TaskResult:
        """包装任务以捕获异常"""
        import time
        start = time.time()
        try:
            result = task_func(device)
            result.execution_time = time.time() - start
            return result
        except Exception as e:
            return TaskResult(
                device=device,
                success=False,
                output="",
                error_message=str(e),
                execution_time=time.time() - start
            )
    
    def generate_report(self, output_file: str):
        """生成JSON格式的执行报告"""
        report = {
            "summary": {
                "total": len(self.results),
                "success": sum(1 for r in self.results if r.success),
                "failed": sum(1 for r in self.results if not r.success),
                "total_time": sum(r.execution_time for r in self.results)
            },
            "details": [
                {
                    "hostname": r.device.hostname,
                    "ip": r.device.ip,
                    "success": r.success,
                    "output": r.output[:1000] if r.success else r.error_message,  # 截断
                    "execution_time": r.execution_time
                }
                for r in self.results
            ]
        }
        
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(report, f, indent=2, ensure_ascii=False)
        
        return report

# 使用示例:批量收集设备版本信息
def collect_version_task(device: Device) -> TaskResult:
    """收集设备版本信息的任务函数"""
    try:
        with XshellSession() as session:
            connected = session.connect(device.ip, device.username, device.password, device.port)
            if not connected:
                return TaskResult(device, False, "", "连接失败")
            
            # 根据厂商选择命令
            version_cmd = {
                'cisco': 'show version',
                'huawei': 'display version',
                'h3c': 'display version',
                'juniper': 'show version',
                'linux': 'uname -a && cat /etc/os-release'
            }.get(device.vendor, 'show version')
            
            output = session.send_command(version_cmd, timeout=60)
            
            return TaskResult(device, True, output)
            
    except Exception as e:
        return TaskResult(device, False, "", str(e))

# 在Xshell中执行批量任务
def main():
    # 加载设备列表
    devices = [
        Device("SW-Core-01", "192.168.1.1", "cisco", "admin", "password123"),
        Device("SW-Core-02", "192.168.1.2", "cisco", "admin", "password123"),
        Device("FW-Border-01", "192.168.1.10", "huawei", "admin", "password123"),
        # ... 更多设备
    ]
    
    executor = BatchExecutor(max_workers=5, timeout=120)
    results = executor.execute_on_devices(devices, collect_version_task)
    
    # 生成报告
    report = executor.generate_report("version_collection_report.json")
    
    # 输出摘要
    print(f"\n执行完成:")
    print(f"  成功: {report['summary']['success']}/{report['summary']['total']}")
    print(f"  失败: {report['summary']['failed']}")
    print(f"  总耗时: {report['summary']['total_time']:.2f}秒")
    print(f"\n详细报告已保存至: version_collection_report.json")

if __name__ == "__main__":
    main()

四、进阶开发:网络设备信息采集平台

4.1 基于TextFSM的解析引擎

网络设备的命令输出是半结构化文本,使用TextFSM模板解析:

# textfsm_parser.py - 网络设备输出解析

import textfsm
from io import StringIO

# Cisco show version 模板
CISCO_VERSION_TEMPLATE = """
Value Model (\S+)
Value Version (\S+)
Value Image (\S+)
Value Uptime (.+)
Value Serial (\S+)
Value Config_Register (\S+)

Start
  ^Cisco IOS Software.*Version\s+${Version},
  ^.*Model number\s*:\s*${Model}
  ^.*System serial number\s*:\s*${Serial}
  ^.*uptime is\s+${Uptime}
  ^Configuration register is\s+${Config_Register}
"""

# Huawei display version 模板
HUAWEI_VERSION_TEMPLATE = """
Value Model (\S+)
Value Version (\S+)
Value Uptime (.+)

Start
  ^Huawei Versatile Routing Platform Software
  ^VRP \(R\) software, Version\s+${Version}
  ^HUAWEI\s+${Model}\s+uptime is\s+${Uptime}
"""

class NetworkDataParser:
    """
    网络设备数据解析器
    """
    def __init__(self):
        self.templates = {
            'cisco': {
                'version': CISCO_VERSION_TEMPLATE,
                'interfaces': self._load_template('cisco_show_interfaces.textfsm'),
                'arp': self._load_template('cisco_show_arp.textfsm'),
                'mac': self._load_template('cisco_show_mac.textfsm'),
            },
            'huawei': {
                'version': HUAWEI_VERSION_TEMPLATE,
                'interfaces': self._load_template('huawei_display_interface.textfsm'),
            }
        }
    
    def _load_template(self, filename):
        """从文件加载TextFSM模板"""
        try:
            with open(f"templates/{filename}", 'r') as f:
                return f.read()
        except FileNotFoundError:
            return None
    
    def parse(self, vendor: str, command: str, raw_output: str) -> list:
        """
        解析命令输出
        
        Args:
            vendor: 设备厂商
            command: 命令类型
            raw_output: 原始命令输出
        """
        template_str = self.templates.get(vendor, {}).get(command)
        if not template_str:
            return [{"raw": raw_output}]  # 无模板时返回原始数据
        
        try:
            template = textfsm.TextFSM(StringIO(template_str))
            parsed = template.ParseText(raw_output)
            
            # 转换为字典列表
            result = []
            headers = template.header
            for row in parsed:
                result.append(dict(zip(headers, row)))
            
            return result
            
        except Exception as e:
            return [{"error": str(e), "raw": raw_output[:500]}]
    
    def parse_interfaces(self, vendor: str, raw_output: str) -> list:
        """专门解析接口信息,提取关键指标"""
        parsed = self.parse(vendor, 'interfaces', raw_output)
        
        # 标准化接口数据
        standardized = []
        for item in parsed:
            std_item = {
                'interface': item.get('INTERFACE') or item.get('Port'),
                'status': item.get('STATUS') or item.get('Link'),
                'protocol': item.get('PROTOCOL') or item.get('Protocol'),
                'description': item.get('DESCRIP') or item.get('Description'),
                'input_rate': self._extract_rate(item.get('INPUT_RATE', '0')),
                'output_rate': self._extract_rate(item.get('OUTPUT_RATE', '0')),
                'errors': int(item.get('INPUT_ERRORS', 0)) + int(item.get('OUTPUT_ERRORS', 0))
            }
            standardized.append(std_item)
        
        return standardized
    
    def _extract_rate(self, rate_str: str) -> int:
        """从速率字符串提取数值(bps)"""
        if not rate_str:
            return 0
        
        rate_str = str(rate_str).upper().strip()
        multipliers = {'K': 1000, 'M': 1000000, 'G': 1000000000}
        
        for suffix, mult in multipliers.items():
            if suffix in rate_str:
                try:
                    return int(float(rate_str.replace(suffix, '').strip()) * mult)
                except:
                    return 0
        
        try:
            return int(rate_str)
        except:
            return 0

4.2 网络资产数据库

# network_inventory.py - 网络资产管理系统

import sqlite3
from datetime import datetime
from typing import List, Optional
from dataclasses import dataclass, asdict

@dataclass
class NetworkAsset:
    id: Optional[int] = None
    hostname: str = ""
    ip_address: str = ""
    vendor: str = ""
    model: str = ""
    software_version: str = ""
    serial_number: str = ""
    site: str = ""
    rack: str = ""
    role: str = ""  # core/distribution/access/wan/firewall
    last_seen: Optional[datetime] = None
    config_backup_path: Optional[str] = None
    compliance_status: str = "unknown"  # pass/fail/unknown
    
    def to_dict(self):
        return asdict(self)

class InventoryDB:
    """
    SQLite-based网络资产数据库
    """
    def __init__(self, db_path="network_inventory.db"):
        self.db_path = db_path
        self._init_db()
    
    def _init_db(self):
        """初始化数据库表结构"""
        with sqlite3.connect(self.db_path) as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS assets (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    hostname TEXT UNIQUE NOT NULL,
                    ip_address TEXT NOT NULL,
                    vendor TEXT,
                    model TEXT,
                    software_version TEXT,
                    serial_number TEXT,
                    site TEXT,
                    rack TEXT,
                    role TEXT,
                    last_seen TIMESTAMP,
                    config_backup_path TEXT,
                    compliance_status TEXT DEFAULT 'unknown',
                    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                    updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
                )
            """)
            
            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_ip ON assets(ip_address)
            """)
            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_site ON assets(site)
            """)
            conn.execute("""
                CREATE INDEX IF NOT EXISTS idx_role ON assets(role)
            """)
    
    def upsert_asset(self, asset: NetworkAsset) -> int:
        """插入或更新资产"""
        with sqlite3.connect(self.db_path) as conn:
            cursor = conn.execute("""
                INSERT INTO assets (
                    hostname, ip_address, vendor, model, software_version,
                    serial_number, site, rack, role, last_seen, compliance_status
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
                ON CONFLICT(hostname) DO UPDATE SET
                    ip_address=excluded.ip_address,
                    vendor=excluded.vendor,
                    model=excluded.model,
                    software_version=excluded.software_version,
                    serial_number=excluded.serial_number,
                    site=excluded.site,
                    rack=excluded.rack,
                    role=excluded.role,
                    last_seen=excluded.last_seen,
                    compliance_status=excluded.compliance_status,
                    updated_at=CURRENT_TIMESTAMP
                RETURNING id
            """, (
                asset.hostname, asset.ip_address, asset.vendor, asset.model,
                asset.software_version, asset.serial_number, asset.site,
                asset.rack, asset.role, datetime.now(), asset.compliance_status
            ))
            
            return cursor.fetchone()[0]
    
    def get_assets_by_role(self, role: str) -> List[NetworkAsset]:
        """按角色查询资产"""
        with sqlite3.connect(self.db_path) as conn:
            conn.row_factory = sqlite3.Row
            rows = conn.execute(
                "SELECT * FROM assets WHERE role = ?", (role,)
            ).fetchall()
            
            return [NetworkAsset(**dict(row)) for row in rows]
    
    def get_compliance_report(self) -> dict:
        """生成合规状态报告"""
        with sqlite3.connect(self.db_path) as conn:
            stats = conn.execute("""
                SELECT 
                    compliance_status,
                    COUNT(*) as count,
                    ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 2) as percentage
                FROM assets
                GROUP BY compliance_status
            """).fetchall()
            
            return {
                'total_assets': sum(s[1] for s in stats),
                'status_breakdown': {
                    s[0]: {'count': s[1], 'percentage': s[2]} 
                    for s in stats
                }
            }
    
    def find_outdated_devices(self, days: int = 30) -> List[NetworkAsset]:
        """查找长时间未更新的设备"""
        with sqlite3.connect(self.db_path) as conn:
            conn.row_factory = sqlite3.Row
            rows = conn.execute("""
                SELECT * FROM assets 
                WHERE last_seen < datetime('now', '-{} days')
                OR last_seen IS NULL
            """.format(days)).fetchall()
            
            return [NetworkAsset(**dict(row)) for row in rows]

# 与Xshell集成:自动发现并入库
def auto_discovery_and_inventory(subnet: str):
    """
    自动发现网段内设备并录入资产库
    """
    from pythonping import ping  # pip install pythonping
    import socket
    
    db = InventoryDB()
    discovered = []
    
    # 简化示例:扫描/24网段
    base_ip = subnet.rsplit('.', 1)[0]
    
    for i in range(1, 255):
        ip = f"{base_ip}.{i}"
        
        # Ping探测
        try:
            response = ping(ip, count=1, timeout=0.5)
            if response.success():
                # 尝试SSH连接获取信息
                try:
                    with XshellSession() as session:
                        # 尝试常见凭证(实际应使用密钥或Vault)
                        if session.connect(ip, "admin", "admin", 22):
                            # 获取主机名
                            hostname = session.send_command("hostname").strip()
                            
                            # 创建设备对象
                            device = Device(hostname, ip, "unknown", "admin", "admin")
                            
                            # 收集详细信息
                            result = collect_version_task(device)
                            
                            if result.success:
                                # 解析厂商和型号
                                parser = NetworkDataParser()
                                parsed = parser.parse('cisco', 'version', result.output)
                                
                                if parsed:
                                    info = parsed[0]
                                    asset = NetworkAsset(
                                        hostname=hostname,
                                        ip_address=ip,
                                        vendor=info.get('Model', 'unknown'),
                                        model=info.get('Model', 'unknown'),
                                        software_version=info.get('Version', 'unknown'),
                                        serial_number=info.get('Serial', 'unknown'),
                                        last_seen=datetime.now()
                                    )
                                    
                                    asset_id = db.upsert_asset(asset)
                                    discovered.append(asset)
                                    print(f"发现设备: {hostname} ({ip}) -> ID {asset_id}")
                                    
                except Exception as e:
                    print(f"无法获取 {ip} 的详细信息: {e}")
                    
        except Exception as e:
            continue
    
    print(f"\n发现完成: 共 {len(discovered)} 台设备")
    return discovered

五、高级应用:可视化网络拓扑管理

5.1 基于PyVis的交互式拓扑

# network_topology.py - 网络拓扑可视化

from pyvis.network import Network
import json
from typing import Dict, List, Tuple

class TopologyBuilder:
    """
    基于LLDP/CDP信息构建网络拓扑
    """
    def __init__(self):
        self.nodes = {}
        self.edges = []
        self.net = Network(height="800px", width="100%", bgcolor="#222222", font_color="white")
        
    def add_device(self, device_id: str, label: str, device_type: str, 
                   site: str = "", ip: str = "", **kwargs):
        """添加设备节点"""
        
        # 根据设备类型设置颜色和图标
        colors = {
            'core': '#ff6b6b',      # 红色
            'distribution': '#4ecdc4',  # 青色
            'access': '#45b7d1',    # 蓝色
            'wan': '#f9ca24',       # 黄色
            'firewall': '#6c5ce7',  # 紫色
            'server': '#a29bfe'     # 淡紫
        }
        
        self.nodes[device_id] = {
            'id': device_id,
            'label': label,
            'title': f"IP: {ip}<br>Site: {site}<br>Type: {device_type}",
            'color': colors.get(device_type, '#95a5a6'),
            'size': 30 if device_type == 'core' else 20,
            'group': site,
            **kwargs
        }
    
    def add_link(self, source: str, target: str, 
                 local_port: str = "", remote_port: str = "",
                 bandwidth: str = "1G", **kwargs):
        """添加连接"""
        self.edges.append({
            'from': source,
            'to': target,
            'title': f"{local_port} -> {remote_port}<br>Bandwidth: {bandwidth}",
            'label': bandwidth,
            'width': 3 if '10G' in bandwidth or '40G' in bandwidth else 1,
            'smooth': {'type': 'continuous'},
            **kwargs
        })
    
    def build_from_lldp_data(self, lldp_results: Dict[str, List[Dict]]):
        """
        从LLDP采集结果构建拓扑
        
        lldp_results格式: {
            'device_hostname': [
                {'local_port': 'Gi0/1', 'neighbor': 'SW-02', 'remote_port': 'Gi0/2', 'platform': 'cisco'},
                ...
            ]
        }
        """
        # 首先添加所有设备节点
        all_devices = set(lldp_results.keys())
        for neighbors in lldp_results.values():
            for n in neighbors:
                all_devices.add(n['neighbor'])
        
        # 从资产库获取设备信息
        db = InventoryDB()
        
        for device in all_devices:
            # 查询数据库获取详细信息
            asset = db.get_asset_by_hostname(device)
            if asset:
                self.add_device(
                    device_id=device,
                    label=device,
                    device_type=asset.role,
                    site=asset.site,
                    ip=asset.ip_address
                )
            else:
                # 未知设备
                self.add_device(device, device, 'unknown')
        
        # 添加连接
        for local_device, neighbors in lldp_results.items():
            for neighbor in neighbors:
                self.add_link(
                    source=local_device,
                    target=neighbor['neighbor'],
                    local_port=neighbor['local_port'],
                    remote_port=neighbor['remote_port'],
                    bandwidth=neighbor.get('bandwidth', '1G')
                )
    
    def render(self, output_file: str = "network_topology.html"):
        """生成HTML可视化"""
        # 添加所有节点和边
        for node in self.nodes.values():
            self.net.add_node(**node)
        
        for edge in self.edges:
            self.net.add_edge(**edge)
        
        # 配置物理模拟
        self.net.set_options("""
        {
          "physics": {
            "forceAtlas2Based": {
              "gravitationalConstant": -50,
              "centralGravity": 0.01,
              "springLength": 100,
              "springConstant": 0.08
            },
            "maxVelocity": 50,
            "solver": "forceAtlas2Based",
            "timestep": 0.35,
            "stabilization": {"iterations": 150}
          },
          "interaction": {
            "hover": true,
            "tooltipDelay": 200,
            "hideEdgesOnDrag": true
          }
        }
        """)
        
        # 生成HTML
        self.net.save_graph(output_file)
        print(f"拓扑图已保存至: {output_file}")
        return output_file

# 与Xshell集成:自动采集LLDP并生成拓扑
def auto_build_topology(seed_devices: List[Device]):
    """
    从种子设备开始,自动发现全网拓扑
    """
    lldp_data = {}
    visited = set()
    to_visit = seed_devices.copy()
    
    while to_visit:
        device = to_visit.pop(0)
        if device.hostname in visited:
            continue
        
        print(f"采集 {device.hostname} 的LLDP信息...")
        
        try:
            with XshellSession() as session:
                if not session.connect(device.ip, device.username, device.password):
                    continue
                
                # 根据厂商选择LLDP命令
                lldp_cmd = {
                    'cisco': 'show lldp neighbors detail',
                    'huawei': 'display lldp neighbor',
                    'h3c': 'display lldp neighbor-information verbose'
                }.get(device.vendor, 'show lldp neighbors')
                
                output = session.send_command(lldp_cmd, timeout=60)
                
                # 解析LLDP输出(简化示例)
                neighbors = parse_lldp_output(device.vendor, output)
                lldp_data[device.hostname] = neighbors
                
                # 将新发现的设备加入待访问队列
                for neighbor in neighbors:
                    if neighbor['neighbor'] not in visited:
                        # 尝试从资产库或DNS获取IP
                        neighbor_ip = resolve_hostname(neighbor['neighbor'])
                        if neighbor_ip:
                            new_device = Device(
                                neighbor['neighbor'], 
                                neighbor_ip,
                                guess_vendor(neighbor['platform']),
                                device.username,  # 假设相同凭证
                                device.password
                            )
                            to_visit.append(new_device)
                
                visited.add(device.hostname)
                
        except Exception as e:
            print(f"采集 {device.hostname} 失败: {e}")
    
    # 构建拓扑
    builder = TopologyBuilder()
    builder.build_from_lldp_data(lldp_data)
    html_path = builder.render("auto_discovered_topology.html")
    
    # 自动打开浏览器
    import webbrowser
    webbrowser.open(f"file:///{html_path}")
    
    return lldp_data

六、安全增强:密钥管理与审计日志

6.1 集成HashiCorp Vault

# vault_integration.py - 安全凭证管理

import hvac
import os
from cryptography.fernet import Fernet

class SecureCredentialManager:
    """
    集成HashiCorp Vault管理设备凭证
    支持动态密钥和自动轮换
    """
    def __init__(self, vault_addr: str = None, token: str = None):
        self.vault_addr = vault_addr or os.getenv('VAULT_ADDR', 'http://localhost:8200')
        self.token = token or os.getenv('VAULT_TOKEN')
        
        self.client = hvac.Client(url=self.vault_addr, token=self.token)
        
        if not self.client.is_authenticated():
            raise ConnectionError("无法连接到Vault或Token无效")
    
    def get_device_credentials(self, device_hostname: str) -> Dict:
        """
        从Vault获取设备凭证
        路径: secret/network-devices/{hostname}
        """
        secret_path = f"network-devices/{device_hostname}"
        
        try:
            response = self.client.secrets.kv.v2.read_secret_version(path=secret_path)
            data = response['data']['data']
            
            return {
                'username': data.get('username'),
                'password': data.get('password'),
                'enable_password': data.get('enable_password'),
                'ssh_key': data.get('ssh_private_key'),
                'last_rotated': data.get('metadata', {}).get('created_time')
            }
            
        except hvac.exceptions.InvalidPath:
            raise KeyError(f"未找到设备 {device_hostname} 的凭证")
    
    def rotate_password(self, device_hostname: str, new_password: str = None):
        """
        自动轮换设备密码
        1. 生成新密码
        2. 登录设备修改密码
        3. 更新Vault
        4. 验证新密码
        """
        import secrets
        import string
        
        # 生成强密码
        if not new_password:
            alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
            new_password = ''.join(secrets.choice(alphabet) for _ in range(16))
        
        # 获取当前凭证
        current_creds = self.get_device_credentials(device_hostname)
        
        # 连接设备修改密码(以Cisco为例)
        device = Device(
            hostname=device_hostname,
            ip=self._resolve_ip(device_hostname),
            vendor='cisco',
            username=current_creds['username'],
            password=current_creds['password']
        )
        
        try:
            with XshellSession() as session:
                session.connect(device.ip, device.username, device.password)
                
                # 进入配置模式修改密码
                session.send_command("configure terminal")
                session.send_command(f"username {device.username} privilege 15 secret {new_password}")
                session.send_command("end")
                session.send_command("write memory")
                
                print(f"设备 {device_hostname} 密码已修改")
                
                # 更新Vault
                self.client.secrets.kv.v2.create_or_update_secret(
                    path=f"network-devices/{device_hostname}",
                    secret={
                        'username': device.username,
                        'password': new_password,
                        'enable_password': current_creds.get('enable_password'),
                        'last_rotated': datetime.now().isoformat()
                    }
                )
                
                # 验证新密码
                test_session = XshellSession()
                if test_session.connect(device.ip, device.username, new_password):
                    print("新密码验证成功")
                    return True
                else:
                    raise RuntimeError("新密码验证失败,可能需要手动恢复")
                    
        except Exception as e:
            print(f"密码轮换失败: {e}")
            # 发送告警通知管理员
            self._send_alert(f"Password rotation failed for {device_hostname}: {e}")
            raise
    
    def _resolve_ip(self, hostname: str) -> str:
        """解析主机名到IP"""
        import socket
        return socket.gethostbyname(hostname)
    
    def _send_alert(self, message: str):
        """发送告警(集成企业微信/钉钉/Slack)"""
        # 实际实现略
        print(f"[ALERT] {message}")

# Xshell脚本中使用Vault凭证
def secure_connect_example():
    """使用Vault凭证安全连接设备"""
    vault = SecureCredentialManager()
    
    hostname = "SW-Core-01"
    creds = vault.get_device_credentials(hostname)
    
    device = Device(
        hostname=hostname,
        ip="192.168.1.1",
        vendor="cisco",
        username=creds['username'],
        password=creds['password']
    )
    
    # 使用凭证执行任务...
    result = collect_version_task(device)
    return result

6.2 审计日志系统

# audit_logger.py - 操作审计与合规

import json
import hashlib
import hmac
from datetime import datetime
from typing import Dict, Any

class XshellAuditLogger:
    """
    记录所有Xshell操作日志,支持SIEM集成
    """
    def __init__(self, siem_endpoint: str = None, api_key: str = None):
        self.siem_endpoint = siem_endpoint
        self.api_key = api_key
        self.local_log = "xshell_audit.log"
    
    def log_session_start(self, session_info: Dict):
        """记录会话开始"""
        event = {
            "event_type": "SESSION_START",
            "timestamp": datetime.utcnow().isoformat(),
            "session_id": self._generate_session_id(),
            "user": session_info.get('username'),
            "source_ip": session_info.get('source_ip'),
            "target_host": session_info.get('hostname'),
            "target_ip": session_info.get('ip'),
            "protocol": session_info.get('protocol', 'SSH'),
            "auth_method": session_info.get('auth_method', 'password')
        }
        self._write_log(event)
        return event['session_id']
    
    def log_command(self, session_id: str, command: str, 
                   output: str = None, duration_ms: int = 0):
        """记录执行的命令"""
        # 敏感命令检测
        sensitive_patterns = [
            r'password\s+\S+',
            r'secret\s+\S+',
            r'username\s+\S+\s+password',
            r'crypto\s+key',
            r'ssh-keygen'
        ]
        
        is_sensitive = any(re.search(p, command, re.IGNORECASE) 
                          for p in sensitive_patterns)
        
        event = {
            "event_type": "COMMAND_EXECUTED",
            "timestamp": datetime.utcnow().isoformat(),
            "session_id": session_id,
            "command_hash": self._hash(command),  # 记录哈希而非明文
            "command_masked": self._mask_sensitive(command) if is_sensitive else command[:50],
            "is_sensitive": is_sensitive,
            "output_size": len(output) if output else 0,
            "duration_ms": duration_ms,
            "risk_score": self._calculate_risk_score(command)
        }
        
        self._write_log(event)
        
        # 高风险命令实时告警
        if event['risk_score'] > 80:
            self._send_alert(f"High risk command detected: {command[:100]}")
    
    def log_session_end(self, session_id: str, duration_seconds: int, 
                       bytes_sent: int = 0, bytes_received: int = 0):
        """记录会话结束"""
        event = {
            "event_type": "SESSION_END",
            "timestamp": datetime.utcnow().isoformat(),
            "session_id": session_id,
            "duration_seconds": duration_seconds,
            "bytes_sent": bytes_sent,
            "bytes_received": bytes_received
        }
        self._write_log(event)
    
    def _generate_session_id(self) -> str:
        """生成唯一会话ID"""
        import uuid
        return str(uuid.uuid4())
    
    def _hash(self, data: str) -> str:
        """计算SHA256哈希"""
        return hashlib.sha256(data.encode()).hexdigest()[:16]
    
    def _mask_sensitive(self, command: str) -> str:
        """脱敏处理"""
        # 替换密码等敏感信息
        masked = re.sub(r'(password|secret)\s+\S+', r'\1 ***', command, flags=re.IGNORECASE)
        return masked
    
    def _calculate_risk_score(self, command: str) -> int:
        """计算命令风险分数(0-100)"""
        score = 0
        
        # 危险命令模式
        high_risk = ['delete', 'drop', 'format', 'reload', 'write erase', 'rm -rf']
        medium_risk = ['configure', 'debug', 'terminal monitor', 'sudo']
        
        cmd_lower = command.lower()
        
        for pattern in high_risk:
            if pattern in cmd_lower:
                score += 40
        
        for pattern in medium_risk:
            if pattern in cmd_lower:
                score += 20
        
        # 生产环境标识
        if any(x in cmd_lower for x in ['prod', 'production', 'core', 'border']):
            score += 15
        
        return min(score, 100)
    
    def _write_log(self, event: Dict):
        """写入日志(本地+远程)"""
        # 本地日志
        with open(self.local_log, 'a', encoding='utf-8') as f:
            f.write(json.dumps(event, ensure_ascii=False) + '\n')
        
        # 发送到SIEM(如果配置了)
        if self.siem_endpoint:
            self._send_to_siem(event)
    
    def _send_to_siem(self, event: Dict):
        """发送到SIEM系统"""
        import requests
        
        try:
            headers = {
                'Content-Type': 'application/json',
                'X-API-Key': self.api_key
            }
            requests.post(
                self.siem_endpoint,
                json=event,
                headers=headers,
                timeout=5
            )
        except Exception as e:
            # SIEM发送失败不阻塞主流程,记录到本地
            print(f"SIEM发送失败: {e}")

# 在Xshell脚本中集成审计
logger = XshellAuditLogger(
    siem_endpoint="https://siem.company.com/api/events",
    api_key="your-api-key"
)

def audited_execute(device: Device, command: str):
    """带审计的命令执行"""
    session_id = logger.log_session_start({
        'username': device.username,
        'hostname': device.hostname,
        'ip': device.ip,
        'protocol': 'SSH'
    })
    
    start_time = time.time()
    
    try:
        with XshellSession() as session:
            session.connect(device.ip, device.username, device.password)
            output = session.send_command(command)
            
            duration = int((time.time() - start_time) * 1000)
            logger.log_command(session_id, command, output, duration)
            
            return output
            
    finally:
        total_duration = int(time.time() - start_time)
        logger.log_session_end(session_id, total_duration)

七、性能优化:异步并发与资源控制

7.1 异步IO与连接池

# async_executor.py - 异步批量执行(使用asyncio和asyncssh)

import asyncio
import asyncssh
from typing import List, Callable
import aiofiles

class AsyncXshellExecutor:
    """
    基于asyncio的高性能异步执行器
    相比线程池,可支持数千并发连接
    """
    def __init__(self, max_concurrent: int = 100, 
                 connect_timeout: int = 10,
                 command_timeout: int = 60):
        self.semaphore = asyncio.Semaphore(max_concurrent)
        self.connect_timeout = connect_timeout
        self.command_timeout = command_timeout
        self.results = []
        
    async def execute_on_device(self, device: Device, 
                               commands: List[str]) -> TaskResult:
        """在单台设备上异步执行命令序列"""
        async with self.semaphore:  # 限制并发数
            try:
                async with asyncssh.connect(
                    device.ip,
                    username=device.username,
                    password=device.password,
                    known_hosts=None,  # 生产环境应配置严格主机密钥检查
                    connect_timeout=self.connect_timeout
                ) as conn:
                    
                    outputs = []
                    for cmd in commands:
                        result = await asyncio.wait_for(
                            conn.run(cmd),
                            timeout=self.command_timeout
                        )
                        outputs.append({
                            'command': cmd,
                            'stdout': result.stdout,
                            'stderr': result.stderr,
                            'exit_code': result.exit_status
                        })
                    
                    return TaskResult(
                        device=device,
                        success=True,
                        output=json.dumps(outputs, indent=2)
                    )
                    
            except asyncio.TimeoutError:
                return TaskResult(device, False, "", "连接或执行超时")
            except asyncssh.Error as e:
                return TaskResult(device, False, "", f"SSH错误: {str(e)}")
            except Exception as e:
                return TaskResult(device, False, "", str(e))
    
    async def execute_batch(self, devices: List[Device], 
                           commands: List[str]) -> List[TaskResult]:
        """批量异步执行"""
        tasks = [
            self.execute_on_device(dev, commands) 
            for dev in devices
        ]
        
        self.results = await asyncio.gather(*tasks, return_exceptions=True)
        
        # 处理异常结果
        processed_results = []
        for i, result in enumerate(self.results):
            if isinstance(result, Exception):
                processed_results.append(TaskResult(
                    device=devices[i],
                    success=False,
                    output="",
                    error_message=str(result)
                ))
            else:
                processed_results.append(result)
        
        return processed_results
    
    def run(self, devices: List[Device], commands: List[str]) -> List[TaskResult]:
        """同步接口包装"""
        return asyncio.run(self.execute_batch(devices, commands))

# 使用示例:1000台设备并发采集配置
def mass_config_collection():
    """大规模配置采集"""
    # 从资产库加载所有设备
    db = InventoryDB()
    all_devices = db.get_all_active_devices()  # 假设有1000台
    
    # 要采集的命令
    commands = [
        'show running-config',
        'show version',
        'show interfaces status',
        'show ip route summary'
    ]
    
    # 异步执行(限制100并发,避免网络拥塞)
    executor = AsyncXshellExecutor(max_concurrent=100)
    results = executor.run(all_devices[:100], commands)  # 先测试100台
    
    # 保存结果
    for result in results:
        if result.success:
            filename = f"configs/{result.device.hostname}_{datetime.now():%Y%m%d}.txt"
            with open(filename, 'w') as f:
                f.write(result.output)
    
    # 生成汇总报告
    success_count = sum(1 for r in results if r.success)
    print(f"采集完成: {success_count}/{len(results)} 成功")

八、打包部署:插件分发与版本管理

8.1 插件打包结构

XshellPluginPackage/
├── plugin/
│   ├── __init__.py
│   ├── main.py              # Xshell入口点
│   ├── config.yaml          # 插件配置
│   └── requirements.txt     # 依赖列表
├── lib/                     # 捆绑的依赖库
│   ├── paramiko/
│   ├── netmiko/
│   └── ...
├── tools/                   # 辅助工具
│   ├── install.bat          # Windows安装脚本
│   └── install.sh           # Linux/Mac安装脚本
├── docs/
│   └── README.md
└── XshellPluginInstaller.exe  # 打包的安装程序

8.2 Xshell菜单集成

# main.py - Xshell插件主入口,注册到菜单

import xsh

def initialize_plugin():
    """插件初始化,注册到Xshell菜单"""
    
    # 创建菜单
    menu = xsh.Dialog.CreateMenu("网络自动化工具包")
    
    # 添加菜单项
    menu.AddItem("批量配置备份", lambda: run_config_backup())
    menu.AddItem("合规性检查", lambda: run_compliance_check())
    menu.AddItem("密码批量轮换", lambda: run_password_rotation())
    menu.AddItem("拓扑自动发现", lambda: run_topology_discovery())
    menu.AddItem("实时性能监控", lambda: run_performance_monitor())
    menu.AddSeparator()
    menu.AddItem("查看审计日志", lambda: view_audit_logs())
    menu.AddItem("设置", lambda: open_settings())
    
    # 注册到Xshell工具菜单
    xsh.Dialog.RegisterMenu(menu)

def run_config_backup():
    """配置备份功能入口"""
    # 选择设备(从会话或资产库)
    devices = select_devices_dialog()
    if not devices:
        return
    
    # 执行备份
    executor = BatchExecutor(max_workers=10)
    results = executor.execute_on_devices(devices, backup_config_task)
    
    # 显示结果
    xsh.Dialog.Message(f"备份完成: {sum(r.success for r in results)}/{len(results)}")
    
    # 打开报告
    executor.generate_report("config_backup_report.json")
    xsh.Dialog.OpenFile("config_backup_report.json")

def select_devices_dialog() -> List[Device]:
    """设备选择对话框"""
    # 从Xshell会话读取
    sessions = []
    for session in xsh.Sessions:
        sessions.append({
            'name': session.Name,
            'host': session.Host,
            'protocol': session.Protocol
        })
    
    # 显示选择对话框(多选)
    selected = xsh.Dialog.ShowCheckList("选择要操作的设备", sessions)
    
    # 转换为Device对象
    devices = []
    for sel in selected:
        # 从Vault或本地获取凭证
        creds = get_credentials(sel['name'])
        devices.append(Device(
            hostname=sel['name'],
            ip=sel['host'],
            vendor='unknown',  # 可自动检测
            username=creds['username'],
            password=creds['password']
        ))
    
    return devices

# 插件加载时自动初始化
if __name__ == "__xshell_plugin__":
    initialize_plugin()

九、实战案例:数据中心网络巡检系统

9.1 完整巡检流程

# datacenter_inspection.py - 数据中心巡检系统

class DataCenterInspector:
    """
    数据中心网络设备全面巡检系统
    """
    def __init__(self):
        self.db = InventoryDB()
        self.logger = XshellAuditLogger()
        self.reporter = InspectionReporter()
        
    def run_full_inspection(self, site: str = None):
        """
        执行完整巡检流程
        """
        print(f"开始巡检: {site or '所有站点'}")
        
        # 1. 加载设备清单
        devices = self.db.get_assets_by_site(site) if site else self.db.get_all_assets()
        print(f"加载设备: {len(devices)} 台")
        
        # 2. 连通性检查
        reachable = self._connectivity_check(devices)
        print(f"可达设备: {len(reachable)}/{len(devices)}")
        
        # 3. 信息采集
        device_info = self._collect_device_info(reachable)
        
        # 4. 健康检查
        health_results = self._health_check(reachable)
        
        # 5. 配置合规检查
        compliance_results = self._compliance_check(reachable)
        
        # 6. 生成报告
        report = self.reporter.generate(
            site=site,
            timestamp=datetime.now(),
            summary={
                'total_devices': len(devices),
                'reachable': len(reachable),
                'healthy': sum(1 for h in health_results if h['status'] == 'healthy'),
                'compliant': sum(1 for c in compliance_results if c['passed'])
            },
            details={
                'device_info': device_info,
                'health': health_results,
                'compliance': compliance_results
            }
        )
        
        # 7. 发送邮件通知
        self._send_report_email(report)
        
        # 8. 更新资产库状态
        self._update_asset_status(health_results, compliance_results)
        
        return report
    
    def _connectivity_check(self, devices: List[Device]) -> List[Device]:
        """ICMP连通性检查"""
        reachable = []
        for device in devices:
            # 使用pythonping或系统ping
            from pythonping import ping
            try:
                response = ping(device.ip, count=2, timeout=2)
                if response.success():
                    reachable.append(device)
                    self.db.update_last_seen(device.hostname)
            except:
                pass
        return reachable
    
    def _collect_device_info(self, devices: List[Device]) -> List[Dict]:
        """采集设备详细信息"""
        commands = ['show version', 'show inventory', 'show environment']
        
        executor = AsyncXshellExecutor(max_concurrent=50)
        results = executor.run(devices, commands)
        
        info_list = []
        parser = NetworkDataParser()
        
        for result in results:
            if result.success:
                parsed = parser.parse('cisco', 'version', result.output)
                info_list.append({
                    'hostname': result.device.hostname,
                    'info': parsed[0] if parsed else {},
                    'raw_output': result.output[:2000]  # 截断存储
                })
        
        return info_list
    
    def _health_check(self, devices: List[Device]) -> List[Dict]:
        """健康状态检查"""
        health_checks = [
            ('cpu', 'show processes cpu', self._check_cpu),
            ('memory', 'show memory statistics', self._check_memory),
            ('temperature', 'show environment temperature', self._check_temp),
            ('interfaces', 'show interfaces status', self._check_interfaces)
        ]
        
        results = []
        for device in devices:
            device_health = {'hostname': device.hostname, 'checks': {}}
            
            with XshellSession() as session:
                if not session.connect(device.ip, device.username, device.password):
                    device_health['status'] = 'unreachable'
                    results.append(device_health)
                    continue
                
                for check_name, cmd, check_func in health_checks:
                    try:
                        output = session.send_command(cmd, timeout=30)
                        status, details = check_func(output)
                        device_health['checks'][check_name] = {
                            'status': status,
                            'details': details
                        }
                    except Exception as e:
                        device_health['checks'][check_name] = {
                            'status': 'error',
                            'details': str(e)
                        }
                
                # 综合状态
                if all(c['status'] == 'ok' for c in device_health['checks'].values()):
                    device_health['status'] = 'healthy'
                elif any(c['status'] == 'critical' for c in device_health['checks'].values()):
                    device_health['status'] = 'critical'
                else:
                    device_health['status'] = 'warning'
                
                results.append(device_health)
        
        return results
    
    def _check_cpu(self, output: str) -> Tuple[str, Dict]:
        """解析CPU使用率"""
        # Cisco示例:CPU utilization for five seconds: 15%/0%; one minute: 12%; five minutes: 10%
        import re
        match = re.search(r'five seconds:\s+(\d+)%', output)
        if match:
            usage = int(match.group(1))
            return (
                'ok' if usage < 50 else 'warning' if usage < 80 else 'critical',
                {'cpu_5s_percent': usage}
            )
        return 'unknown', {}
    
    def _compliance_check(self, devices: List[Device]) -> List[Dict]:
        """配置合规性检查"""
        compliance_rules = [
            {
                'name': 'SSH版本检查',
                'command': 'show ip ssh',
                'check': lambda out: 'SSH Enabled - version 2' in out,
                'severity': 'high'
            },
            {
                'name': '未使用端口关闭',
                'command': 'show interfaces description',
                'check': lambda out: 'admin down' in out or 'notconnect' not in out.lower(),
                'severity': 'medium'
            },
            {
                'name': 'NTP配置检查',
                'command': 'show ntp associations',
                'check': lambda out: len(out.strip().split('\n')) > 2,
                'severity': 'medium'
            }
        ]
        
        results = []
        for device in devices:
            device_compliance = {'hostname': device.hostname, 'rules': []}
            
            with XshellSession() as session:
                session.connect(device.ip, device.username, device.password)
                
                all_passed = True
                for rule in compliance_rules:
                    output = session.send_command(rule['command'])
                    passed = rule['check'](output)
                    all_passed = all_passed and passed
                    
                    device_compliance['rules'].append({
                        'name': rule['name'],
                        'passed': passed,
                        'severity': rule['severity']
                    })
                
                device_compliance['passed'] = all_passed
                results.append(device_compliance)
        
        return results

# 执行巡检
if __name__ == "__main__":
    inspector = DataCenterInspector()
    report = inspector.run_full_inspection(site="北京数据中心")
    print(f"巡检报告已生成: {report['file_path']}")

结语

Xshell的Python扩展能力将终端工具从"手动操作"提升为"可编程平台"。通过本文介绍的技术栈——从基础的会话自动化到企业级的资产管理和安全审计——你可以构建完全符合自身需求的网络管理工具链。

关键成功要素:

  1. 分层架构:核心API封装、业务逻辑插件、配置模板分离
  2. 安全第一:Vault集成、审计日志、敏感操作脱敏
  3. 性能优化:异步并发、连接池、资源限制
  4. 可维护性:版本管理、自动部署、完善的日志

在这个网络设备数量爆炸、变更频率加快的时代,自动化不是奢侈品,而是必需品。掌握Xshell插件开发,就是掌握了网络运维的"自定义武器"。


附录:开发资源

  • Xshell Python API文档:NetSarang官方文档中心
  • TextFSM模板库:https://github.com/networktocode/ntc-templates
  • 网络设备MIB库:http://www.circitor.fr/Mibs/Html/

转载自:https://blog.csdn.net/u014727709/article/details/157909537
欢迎 👍点赞✍评论⭐收藏,欢迎指正

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐