用于更改 Cisco 设备上的 ip-helper IP 的 Python 脚本?

网络工程 思科 自动化
2021-08-01 03:31:38

堆栈交换和 Python 的新手。希望这被允许被问到。

我正在上 Python 课程,终于开始将脚趾浸入水中。

我有很多东西要学习,但我已经有了一个我个人认为有用且实用的用例。

我有一个即将进行的项目,需要我登录并触摸每台设备,查找配置的“ip-helper”的所有实例并更改存储的当前 IP 地址。

由于 ip-helper 存在于任意数量的 SVI 下,您不仅必须能够执行 show run 并找到正在配置的 ip-helper 的所有实例,而且您还必须能够识别其配置的 SVI然后生成必须应用的新配置。

有没有人有一个脚本的工作示例可以做到这一点,我可以学习和使用?

我认为在 Cisco 设备配置方面,这样的事情可以应用于很多事情吗?查找任意数量的字符串,检查以确保它是子配置以及在生成和应用新的替换配置之前父配置是什么?

3个回答

欢迎来到 SE!

作为一名网络工程师,我最近也开始使用 Python 进行编程,以更好地支持我自己的内部自动化工作。为了避免处理 CLI 附带的低级 ssh 处理的大量手动工作,我专门关注 SecureCRT 应用程序附带的脚本接口。如果你有 SecureCRT,你可以查看它的脚本功能,它的文档将向你展示如何使用 Python 脚本,如下所示。即使您没有 SecureCRT,您也可以使用以下代码作为如何使用本机 ssh 守护程序将此类脚本组合在一起的一般概述,前提是您单独处理所有低级 SSH/CLI 工作。

我对当前状态的此代码不作任何形式的保证。这是基本测试代码,不用于生产。它可能会破坏您的网络!

你被警告了 :)

# $language = "python"
# $interface = "1.0"
import sys
import re

# Your hosts file should just be a list of IP's, each on its own line
HOSTSFILE = '\path_to\your_hosts_file.txt'
USER = 'username'
PASS = 'password'
ENABLEP = 'enablepassword'
NEW_HELPER_CMD = 'ip helper-address x.x.x.x'

def Wait():
    # Wait for a prompt
    while True:
        if not crt.Screen.WaitForCursor(1):
            break
def Get_prompt():
    row = crt.Screen.CurrentRow
    prompt = crt.Screen.Get(row, 0, row, crt.Screen.CurrentColumn - 1).strip()
    return prompt
def Get_list_from_file(file_path):
    f = open(file_path, 'r')
    hostlist = f.readlines()
    f.close()
    return hostlist
def Log_in(host):
    login_command = '/SSH2 /L {0} /PASSWORD {1} {2}'.format(USER, PASS, host)
    try:
        crt.Session.Connect(login_command)
        crt.Session.Synchronous = True
        Wait()
        return True
    except:
        # More specific except clauses needed
        # For specific error conditions, add corresponding error messages
        return False
def Exec_mode():
    prompt = Get_prompt()
    tries = 0
    # Enter priveleged exec mode if not already there
    while tries > 4:
        if re.search(r'#$', prompt):
            break
        elif re.search(r'[>]$', prompt):
            crt.Screen.Send('enable\n')
            Wait()
            prompt = Get_prompt()
            if re.search(r'Password:', prompt):
                crt.Screen.Send('{0}\n'.format(ENABLEP))
                Wait()
        elif re.search(r'\)$', prompt):
            crt.Screen.Send('end\n')
            Wait()
        tries += 1
    if tries >= 4:
        # Add an error code stating that exec_mode failed
        return False
    else:
        return True
def Config_mode():
    prompt = Get_prompt()
    if re.search(r'#$', prompt):
        crt.Screen.Send('config t\n')
        Wait()
    else:
        b = Exec_mode()
        if b:
            crt.Screen.Send('config t\n')
            Wait()
    prompt = Get_prompt()
    if re.search(r'\)$', prompt):
        return True
    else:
        return False
def Pull_and_parse_config():
    Wait()
    crt.Screen.Send('term len 0\n')
    Wait()
    prompt = Get_prompt()
    # Output the runnning config
    crt.Screen.Send(r'sh running-config\n')
    crt.Screen.WaitForString(r'\n')
    # Capture config output to string var 'config'
    # Note: If there is any duplicate of the 'prompt' string in the config, it will stop the
    # capture prematurely
    config = crt.Screen.ReadString(prompt)
    Wait()
    # Split the captured config into a list, containing the interface string, old ip helper string, and
    # remaining config. Will capture each interface on the device that currently has an ip helper command
    # Note: this only captures the first ip helper command on each interface
    configlist = re.split(r'(interface [^\n]+?)\n[^!]*?(ip helper-address .+?)\n', config, flags=re.DOTALL)
    if len(configlist) > 1:
        configlist.pop(0)
        return configlist
    else:
        # add message stating no interfaces matched
        return False
def Update_config(configlist):
    # For each interface with an ip helper command, remove old command and add new helper command
    while len(configlist) > 2:
        int_id = configlist.pop(0)
        old_helper_cmd = configlist.pop(0)
        if re.search(r'interface .+', int_id) and re.search(r'ip helper-address .+', old_helper_cmd):
            Enter_config_mode()
            # Here is where you actually update the config
            # If you don't want to remove old helper command, remove "no {1}\n" from the following string
            crt.Screen.Send('{0}\n no {1}\n {2}\n'.format(int_id, old_helper_cmd, NEW_HELPER_CMD))
            Wait()
            return True
        elif not re.search(r'interface .+', int_id):
            # add error message stating invalid interface id
            return False
        else:
            # add error message stating invalid ip helper command
            return False
def Main():
    hostfile = Get_list_from_file(HOSTSFLIE)
    for host in hostfile:
        ok = Log_in(host)
        if not ok:
            # add error message stating login failed
            continue
        ok = Exec_mode()
        if not ok:
            # add error message here
            continue
        configlist = Pull_and_parse_config()
        if not configlist:
            # add error message stating config capture failed
        ok = Update_config(configlist)
        if not ok:
            # add error message stating config update failed
        else:
            # add success message stating config on host successfully updated

Main()

您将看到,这涉及大量代码,用于看起来应该很简单的事情,而这仅执行一项特定任务!如果您还是脚本/编程领域的新手,我强烈建议您在尝试在生产网络中实现任何脚本之前咨询更有经验的程序员,尤其是涉及修改配置的任何此类脚本。虽然我从我之前所做的一些工作中改编了这段代码,但我没有对此进行测试,我不能保证其中任何一项都会起作用。尽管如此,这应该为您提供一个良好的起点。祝你好运!

您可以使用 PrettyGoodTerminal 运行以下 Python 脚本来获取帮助程序地址和相关 SVI 名称,并根据需要构建更新配置:

import re

# report status back to PGT if you need    
ActionResult = "ip helper not found"

result = Terminal.ExecCommand("sh ip int brief","#")
commandResult = result[0]
timedout = result[1]
if not timedout:
  for thisLine in commandResult.splitlines():
    ifList = filter(None, thisLine.split(" "))
    if ifList[4] == "up" and ifList[5] == "up":
      ifName = ifList[0]
      result = Terminal.ExecCommand("sh run int {0}".format(ifName), "#")
      ifconfig = result[0]
      timedout = result[1]
      if not timedout:
        helperAddresses = re.findall(r'ip helper-address.*',ifconfig)
        if len(helperAddresses) > 0:
          # report status back to PGT if you need
          ActionResult = "ip helper found"
          # Here you have the SVI name in ifName and the helperAddresses list containing the defined ip helper-addresses

或者,您也可以使用添加到 Command 元素的上述 Python 代码构建一个不错的 Visual Script,然后添加另一个 Command 元素来执行构建的更新脚本。像这样: 用于更新 ip helper-address 的可视化脚本

如果您需要 PGT 方面的帮助,请告诉我;-)

您可以使用一个名为 ciscoconfparse 的库,它理解并可以使用缩进将 cisco ios 解析为父项和子项。我在生产环境中完全做到了这一点,我更新了 600 路由器上的 ip 助手,快速的谷歌搜索应该会让你找到 ciscoconfparse 库的文档