记一个相对通用的远程备份交换机配置文件的脚本--python

发布时间 2023-09-19 15:32:27作者: 厚礼蝎

脚本部分

import paramiko
import time
import re
import tarfile, os
from datetime import datetime


def clean_output(raw_output):
    # 使用正则表达式去除 ANSI 转义序列和特殊字符
    cleaned_output = re.sub(r'\x1b\[[0-9;]*[mGK]', '', raw_output)
    # 保留可见字符和换行符
    cleaned_output = ''.join(char for char in cleaned_output if char.isprintable() or char == '\n')
    return cleaned_output


def get_switch_config(ip, username, password, conf_dir):
    conf_name = conf_dir + ip + ".conf"
    print("开始写入文件 ", conf_name)
    with open(conf_name, "w"):
        pass
    with open(conf_name, "a", encoding='latin-1') as file:
        # 创建一个SSH客户端
        client = paramiko.SSHClient()
        client.set_missing_host_key_policy(paramiko.AutoAddPolicy())

        # 连接到交换机
        client.connect(ip, username=username, password=password)

        # 创建一个shell会话
        channel = client.invoke_shell()

        # 发送命令,获取配置
        channel.send("dis cu\n")
        time.sleep(5)  # 等待命令执行
        output = ''
        while not channel.recv_ready():
            time.sleep(0.1)

        while channel.recv_ready():
            output += channel.recv(65535).decode('latin-1')

        # Remove the ANSI escape codes (e.g., '\x1b[42D')
        output = re.sub(r'\x1b\[\d+D', '', output)
        output = clean_output(output)
        ends = "---- More ----"
        w = False
        if output.rstrip().endswith(ends):
            w = True
        output = output.rstrip().rstrip(ends).rstrip()
        output = output.split('dis cu')[-1]
        # print(output)
        if not w:
            last_newline = output.rfind('\n')
            if last_newline != -1:
                output = output[:last_newline]  # 去除最后一行(包括换行符)
        file.write(output)
        file.write("\n")
        while w:
            # print("===========")
            channel.send(" ")
            time.sleep(5)
            while not channel.recv_ready():
                time.sleep(0.1)
            output = ""
            while channel.recv_ready():
                output += channel.recv(65535).decode('latin-1')

            # Remove the ANSI escape codes (e.g., '\x1b[42D')
            output = re.sub(r'\x1b\[\d+D', '', output)
            output = clean_output(output)
            output = output.strip()
            if output.endswith(ends):
                output = output.rstrip(ends).strip()
            else:
                w = False
                last_newline = output.rfind('\n')
                if last_newline != -1:
                    output = output[:last_newline]  # 去除最后一行(包括换行符)
            # print(output)
            file.write(output)
            file.write("\n")
        # 关闭连接
        client.close()


def create_tar_gz_archive(files_to_archive):
    # 获取当前日期和时间,并格式化为字符串
    current_datetime = datetime.now().strftime("%Y%m%d_%H%M%S")
    output_filename = f"{current_datetime}.tar.gz"
    print("开始创建压缩包 ", output_filename)
    with tarfile.open(output_filename, "w:gz") as tar:
        for file in files_to_archive:
            tar.add(file, arcname=os.path.basename(file))


def delete_files(file_list):
    print("开始清理临时文件......")
    for file in file_list:
        try:
            os.remove(file)  # 或者使用 os.unlink(file)
            print(f'Deleted file: {file}')
        except OSError as e:
            print(f'Error deleting file {file}: {str(e)}')


if __name__ == '__main__':
    # 交换机信息
    host = [
        ["192.168.1.2", 'xxxxxxxx', 'xxxxxxxxxxxxx'],
        ["192.168.1.3", 'xxxxxxxx', 'xxxxxxxxxxxxx'],
        ["192.168.1.4", 'xxxxxxxx', 'xxxxxxxxxxxxx'],
        ["192.168.1.5", 'xxxxxxxx', 'xxxxxxxxxxxxx'],
    ]
    conf_dir = "./"
    for i in host:
        print("开始备份", i[0], "的配置文件")
        get_switch_config(*i, conf_dir)

    # 文件列表
    files_to_archive = [conf_dir + i[0] + ".conf" for i in host]
    # 打包
    create_tar_gz_archive(files_to_archive)
    # 删除临时文件
    delete_files(files_to_archive)

编写脚本中遇到的问题

如何导出配置文件

这里采用的是比较原始的方法,通过查看配置文件,然后拼凑出来,这个过程是非常慢的,需要等文本全出来了再发送出来,这个就需要设置等待时间,而每次的等待时间并不是固定的,所以,就需要尽量把时间设置的合适点,否则出来的字符串就是不全的。

为什么会这么做,因为这边的交换机品牌不一样,型号不一致,所以,无法使用比较统一的便捷方法。

以前为统一设备型号的网络设备写过备份脚本,是通过tftp来接收配置文件,然后在交换机上执行发送命令就可以了,这种是最快最方便的方法,也是最省事的。

配置文件过长

配置文件过长,显示一部分,会在结尾加上 ---- More ---- ,然后需要发送空格,才会给出下面部分的配置,所以上面的部分就会有判断文末是否有 ---- More ---- ,有的话,就发送空格

不过,也有其他的解决版本(如果能够修改交换机配置文件的话)

<设备名称>system-view
[设备名称]user-interface vty 0 4   // 进入虚拟终端接口配置模式
[设备名称-ui-vty0-4]screen-length 0   // 设置行数限制为0,表示不限制行数

设置过后,就不再会出现 ---- More ---- 了,但是查看配置的时候就会麻烦些

乱码的问题

在导出配置文件时,如果配置中存在非常见字符,会在接收的时候出现报错

这里试了好几种码,也就 latin-1 通用

所以,咱们这里就使用的 latin-1

channel.recv(65535).decode('latin-1')

输出的配置总是前面夹在这一些特殊空格字符

在导出时,总是会遇到前面有一段空格,但是通过 strip() 又无法剔除,后来发现,是一些不可显示的特殊字符

这边用的剔除方法

def clean_output(raw_output):
    # 使用正则表达式去除 ANSI 转义序列和特殊字符
    cleaned_output = re.sub(r'\x1b\[[0-9;]*[mGK]', '', raw_output)
    # 保留可见字符和换行符
    cleaned_output = ''.join(char for char in cleaned_output if char.isprintable() or char == '\n')
    return cleaned_output

output = re.sub(r'\x1b\[\d+D', '', output)
output = clean_output(output)