交换机配置的自动备份与变更告警

image.png

前言

我们做运维的人呢,平时没人在意,出问题的时候才有存在感。所谓日常透明,关键给力嘛。而运维中非常重要的一部分就是备份,它很大程度上决定了我们关键时刻到底能不能给力。

所以今天我们就来聊一聊交换机配置备份的那些事。

配置备份

交换机的配置备份有很多办法,比如

  • netconf 协议通过 API 拉
  • 通过交换机命令向 ftp 服务器上传
  • show run , display cu 等命令直接抓

netconf 比较高端一点,但是交换机未必都能支持。而且存在需要开启 netconf 配置的问题,初始工作量较大。

通过 ftp 上传的配置文件最准确,但是不同品牌,不同型号间对应的 ftp 命令差异很大,做起来也是比较麻烦的。

通过命令 show 配置似乎看起来最为简单和直接,命令基本也就 C 家和仿 C 家系列的 show running-config,H 家和他家基友的 display current-configuration ,J 家的 show configuration 这几种。

当然是自动来 show 配置了,手动是不可能 的,这辈子都不可能手动来做的。我们需要自动化的建立远程连接,show 配置,退出,然后把结果存下来。

首先要考虑下走 SSH 还是 TELNET

  • SSH :安全,连接方式标准,易于实现。但可能存一些交换机未开启 SSH ,需要人工介入开启。
  • TELNET:通常默认开启的远程选项,不安全,各家提示符不一,脚本实现复杂。

所以其实很好抉择,SSH 早晚是要全线开启的,而且安全要求严格一些的话, TELNET 应该是要关闭掉。

况且,批量 SSH 的轮子这么多,我们其实根本不需要自己再写一个 SSH 的脚本。比如直接用 multissh 就可以很好的符合我们的需求。

如下所示,使用 multissh ,一条非常简单的命令我们就可以把 SSH 执行 show run 的结果输出到我们指定的目录里,而且速度非常快,15 台交换机同时完成备份只用了 1.3 秒。

[root@wiki-qfeng multissh]# ./multissh -ips "192.168.15.101-192.168.15.115" -cmds "terminal length 0;show run" -u admin -p password -outTxt -f ./conf/      
2018/04/13 21:26:06 Multissh start
2018/04/13 21:26:08 Multissh finished. Process time 1.350904329s. Number of active ip is 15

[root@wiki-qfeng conf]# ls
192.168.15.101.txt  192.168.15.102.txt  192.168.15.103.txt  192.168.15.104.txt  192.168.15.105.txt

[root@wiki-qfeng conf]# cat 192.168.15.101.txt 
sw-1#terminal length 0
sw-1#show run
Building configuration...

Current configuration : 4246 bytes
!
! Last configuration change at 03:25:58 UTC Sun Apr 8 2018 by admin
! NVRAM config last updated at 03:25:57 UTC Sun Apr 8 2018 by admin
!
……
……

我们可以把不同类型交换机,路由器,防火墙的 show 配置命令都提前写好。以文件的方式让 multissh 去读取。如果交换机的密码不一致的话,也可以统统写到一个配置文件里来调用。详细可以参考 用 Go 写一个轻量级的 ssh 批量操作工具

然后把这个命令放到 crontab 定时任务里去就好了。

变更告警

对于一个运维的团队而言,日常运维的操作记录和回溯是非常重要的一件事情。因为绝大部分的故障并非因为软硬件本身,而是来自运维人员的误操作。GitLab 误删过数据库,AWS 误删过系统,携程误操作崩过一整天。实际上只要是人做的操作,误操作就不可能完全避免。

怎么办呢?

一方面是尽量避免误操作的发生,比如更为严格的权限控制和变更审批,堡垒机操作审查等等。另一方面则是在事件发生后能及时的提示和告警,并能快速的溯源导致故障的操作,以便尽快回退。

所以给交换机的配置变更做个告警就也非常重要;额。刚才我们已经备份下了交换机的配置,所以我们只要把每次备份下的配置和上一次备份的配置做一个比对,就能马上得到配置的变更情况。

简单一点,可以直接用 pythondifflib 库来做两个配置文本的比对。difflib 非常简单。如下例,输入两个数组,返回的对比变更情况,并附带上下文(默认三行)

>>> s1 = ['bacon\n', 'eggs\n', 'ham\n', 'guido\n']
>>> s2 = ['python\n', 'eggy\n', 'hamster\n', 'guido\n']
>>> for line in unified_diff(s1, s2, fromfile='before.py', tofile='after.py'):
...     sys.stdout.write(line)   
--- before.py
+++ after.py
@@ -1,4 +1,4 @@
-bacon
-eggs
-ham
+python
+eggy
+hamster
 guido

不过对于交换机的配置变更告警而言,我们还需要过滤掉一些东西。比如 ntp clock-period 17179963 类似这样的 nto clock-period 行,隔一段时间他本身就会变一次,而我们并不关心这个东西。因为它的变更而发出的告警就毫无意义,反而可能让真正需要的告警信息淹没在其中。

同理还有类似 Current configuration : 22452 bytesCryptochecksum:ff93c7fc1cb0fd5cf9d113715ce16b62Tue Apr 10 18:08:09.028 UTC 等,这些东西本身就有可能变更而事实上我们并不关心这些。

同时还有一些特殊情况,比如在锐捷的一些版本上,我们就不得不忽略掉 radius-server key 7 xxxxx 这里的变更。因为它丫的这个也自己会变——【交换机】10.X交换机配置radius-server key密文显示后,show run发现密文不同时间会显示不一样

综合这些因素,滤除掉我们不需要参加比较的行之后,我们再对两个配置文件去做 diff 比较。此时的结果就可以发告警了。用 smtplib 来调用 smtp 发送邮件,用 email.mime 来做附件封装的工作,把配置文件作为附件传上去。

完整代码大概如下(有删减)

#!/usr/bin/python
# encoding: utf-8
import difflib
import time
import os
import shutil
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
import smtplib


conf_path = '/opt/swbackup/conf/'
backup_path = '/opt/swbackup/backup/'
mailto_list=["user1@example.org","user2@example.org","user3@example.org"]
mail_host="smtp.example.org"
mail_user="user@example.org"
mail_pass="mailpasswd"
RuiJie = ["ruijie1.txt","ruijie2.txt","ruijie3.txt"]

####################

def compare(source,target,current,original):
    lines1 = get_lines(source)
    lines2 = get_lines(target)

    result = list(difflib.unified_diff(lines1, lines2, fromfile=current, tofile=original))
    return result

def get_lines(filename):
    f = open(filename)
    lines = f.read().splitlines()
    newlines = []
    split_list = filename.split('/')
    filename = split_list[(len(split_list)-1)]
    for l in lines:
        # 滤除时间行
        if "!Time: " in l:
            continue
        # 滤除空白行
        if (l == "") or (l == "!"):
            continue
        # 滤除配置变更提示行
        if "! Last configuration change at" in l:
            continue
        if "! NVRAM config last updated at" in l:
            continue
        # 滤除配置大小行
        if "Current configuration :" in l:
            continue
        # 滤除 checksum 提示行
        if "Cryptochecksum:" in l:
            continue
        # 滤除 enable 输入行
        if ">enable" in l:
            continue
        # 滤除 Password 输入行
        if "Password:" in l:
            continue
        # 滤除 terminal length 0 输入行
        if "terminal length 0" in l:
            continue
        # 滤除 show run 命令行
        if "show run" in l:
            continue
        # 滤除 display cu 行
        if "display cu" in l:
            continue
        # 滤除 VTY is now available 行
        if ("VTY" in l) and ("is now available" in l):
            continue
        # 滤除时间行
        if (" UTC" in l) and ("config" not in l):
            continue
        # 滤除 ntp clock 行
        if "ntp clock-period" in l:
            continue
        # 滤除 login 时间行
        if "The last login time is" in l:
            continue
        # 滤除 login 时间行
        if "The current login time is" in l:
            continue
        # 对于锐捷,滤除 radius-server key 变更行
        if (filename in RuiJie) and ("radius-server key 7" in l):
            continue
        newlines.append(l + "\n")
    f.close()
    return newlines


def load_conf(path):
    files= os.listdir(path)
    return files


def send_mail(to_list,sub,content,att_path):
    msg = MIMEMultipart()

    msg.attach(MIMEText(content,_subtype='plain',_charset='utf-8'))
    split_list = att_path.split('/')
    filename = split_list[(len(split_list)-1)]
    att = MIMEText(open(att_path, 'rb').read(), 'base64', 'utf-8')
    att["Content-Type"] = 'application/octet-stream'
    att["Content-Disposition"] = 'attachment; filename=' + filename
    msg.attach(att)
    
    msg['Subject'] = sub
    msg['From'] = mail_user
    msg['To'] = ",".join(to_list)
    try:
        s = smtplib.SMTP()
        s.connect(mail_host)
        s.login(mail_user,mail_pass)
        s.sendmail(mail_user, to_list, msg.as_string())
        s.close()
        return True
    except Exception, e:
        print str(e)
        return False

if __name__=='__main__':
    conf_file = load_conf(conf_path)
    backup_file = load_conf(backup_path)

    for f in conf_file:
        now = conf_path + f
        backup = backup_path + f
        if f in backup_file:
            res = compare(backup, now, f + " original",f + " current")
            if len(res) >0:
                content = "".join(res)
                send_mail(mailto_list, "switch_backup " + f, content, now)
        else:
            fl = open(now)
            content = fl.read()
            fl.close()
            send_mail(mailto_list, "switch_backup " + f, content, now)
        shutil.copy(now,  backup)

大抵逻辑就是准备两个目录,一个放备份的配置,一个放 multissh show 出来的配置。

  1. 先分别列出两个目录下的文件,然后拿当前获取的配置文件去备份目录下查。
  2. 如果找不到说明是新备份的交换机,发邮件。配置做正文和附件。
  3. 如果找到了相同的配置文件,那么做 diff 对比。
  4. diff 对比前先过滤掉一些可能会造成误报的配置行。
  5. 如果产生配置变更,发邮件。diff 变更内容做正文,配置做附件。
  6. 所有逻辑执行完以后,将当前 multissh show 得的配置备份至备份目录。等待一下次执行。

很简单的一个脚本。也做个定时任务,把他放在 multissh 的定时任务之后就行。整个交换机配置的备份和变更告警就全自动化完成了。看下效果:


image.png

感觉还行。

参考文献

difflib
【交换机】10.X交换机配置radius-server key密文显示后,show run发现密文不同时间会显示不一样
用 Go 写一个轻量级的 ssh 批量操作工具

以上

转载授权

CC BY-SA

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342