# -*- coding: UTF-8 -*-
# notepad++ 快速执行脚本方法
# cmd /k cd /d "$(CURRENT_DIRECTORY)" & python "$(FULL_CURRENT_PATH)" & ECHO & PAUSE & EXIT
# cmd /k cd /d "$(CURRENT_DIRECTORY)" & tclsh "$(FULL_CURRENT_PATH)" & ECHO & PAUSE & EXIT
r'''
本库是基于 python2.7 和 SecureCRT api 实现。
脚本必须在SecureCRT上运行.
'''
import logging,time,datetime,sys,re,os
__version__='2018.11.15'
######################### 初始化日志模块 #########################
current_dir = os.getcwd()
syslog_file = os.path.join(current_dir, 'syslog.log')
#current_dir = os.path.dirname(os.path.realpath(__file__))
#os.chdir(current_dir)
filelog_logger = logging.getLogger('filelog')
if filelog_logger.handlers == [] :
reload(sys)
sys.setdefaultencoding('UTF-8')
filelog_handler = logging.FileHandler(syslog_file,mode='ab',encoding='UTF-8')
filelog_formatter = logging.Formatter('%(asctime)s-->%(message)s\r')
filelog_handler.setFormatter(filelog_formatter)
filelog_logger.addHandler(filelog_handler)
filelog_logger.setLevel(logging.DEBUG)
############################## 全局变量 ###########################
debug = True
basemac = '44:00:32:1e:11:22'
ip_firstbyte = 10
# 1 : 所有接口共用一个mac地址;2 : 每个接口mac地址不同
mactype = 2
############################## 全局函数 ###########################
def logger(szString):
'''日志文件名称固定,适合做系统日志记录'''
if debug == True:
filelog_logger.info(szString)
def sleep(ms):
time.sleep(ms/1000.0)
def timestamp():
return time.time()
############################# 定义类 ###############################
class DUT():
'''设备类,其中包含了所有需要用到的命令'''
def __init__(self,crt):
self.crt = crt
self.DefaultPrompt = '#'
self.Tab = self.crt.GetScriptTab()
self.Tab.Screen.Synchronous = True
self.Tab.Screen.IgnoreEscape = True
self.logfilename = time.strftime('%Y.%m.%d.%H.%M.%S')+'.log'
self.sendwithlogfilename = 'sendwithlog_'+time.strftime('%Y.%m.%d.%H.%M.%S')+'.txt'
self.sendwithlogfileobj = None
def send(self,szCommand, Prompt = None, delay = 0):
'''遇到错误就会停止,方便查看,主要用于配置设备'''
if Prompt == None:
Prompt = self.DefaultPrompt
if (szCommand == ' ' or szCommand == '\r'):
self.Tab.Screen.Send(szCommand)
else :
self.Tab.Screen.Send(szCommand + '\r')
szResult = self.Tab.Screen.WaitForStrings([Prompt,'--More--','%Error '])
if (szResult == 1):
pass
elif (szResult == 2):
whitespace = True
while whitespace :
self.Tab.Screen.Send(' ')
szResult = self.Tab.Screen.WaitForStrings([Prompt,'--More--'])
if (szResult == 1):
whitespace = False
elif (szResult == 3):
szResult = self.Tab.Screen.WaitForStrings(['again','Invalid'],2)
if (szResult == 1):
for i in range(1,51):
self.Tab.Screen.WaitForString(Prompt)
self.crt.Sleep(i*500)
self.Tab.Screen.Send(szCommand + '\r')
szResult = self.Tab.Screen.WaitForStrings(['again',Prompt])
if (szResult == 2):
break
else :
raise IOError
if (delay != 0):
self.crt.Sleep(delay)
def send2(self,szCommand,Prompt = None, delay = 0):
'''和send的主要区别是:遇到Error时,不会停止,继续执行 '''
if Prompt == None:
Prompt = self.DefaultPrompt
if (szCommand == ' ' or szCommand == '\r'):
self.Tab.Screen.Send(szCommand)
else :
self.Tab.Screen.Send(szCommand + '\r')
szResult = self.Tab.Screen.WaitForStrings([Prompt,'--More--','%Error '])
if (szResult == 1):
pass
elif (szResult == 2):
whitespace = True
while whitespace :
self.Tab.Screen.Send(' ')
szResult = self.Tab.Screen.WaitForStrings([Prompt,'--More--'])
if (szResult == 1):
whitespace = False
elif (szResult == 3):
szResult = self.Tab.Screen.WaitForStrings(['again',Prompt],2)
if (szResult == 1):
for i in range(1,51):
self.Tab.Screen.WaitForString(Prompt)
self.crt.Sleep(i*500)
self.Tab.Screen.Send(szCommand + '\r')
szResult = self.Tab.Screen.WaitForStrings(['again',Prompt])
if (szResult == 2):
break
if (delay != 0):
self.crt.Sleep(delay)
def sendwithlog(self,szCommand):
'''发送命令的同时,把命令写入文本文件'''
self.send(szCommand)
if not self.sendwithlogfileobj :
self.sendwithlogfileobj = open(self.sendwithlogfilename,'ab')
self.sendwithlogfileobj.write(szCommand+'\n')
def sendctrl(self,char, Prompt = None):
if Prompt == None:
Prompt = self.DefaultPrompt
if (char == 'c'):
self.Tab.Screen.Send(chr(3))
self.Tab.Screen.WaitForString(Prompt)
elif (char == 'x'):
self.Tab.Screen.Send(chr(24))
def sendexpect(self,send, expect, delay = 10):
if not self.Tab.Session.Connected:
return False
self.Tab.Screen.Send(send + '\r')
if self.Tab.Screen.WaitForString(expect,delay):
return True
else :
raise IOError
def con_t(self):
'''进入 config terminal 模式'''
self.sendctrl('c')
self.sendctrl('c')
self.send('configure terminal')
def rec(self,szCommand, Prompt = None):
'''不包含开始的命令行和结尾的系统提示符,如果没有返回内容,则为空'''
if Prompt == None:
Prompt = self.DefaultPrompt
self.Tab.Screen.Send(szCommand + '\r')
if not self.Tab.Screen.WaitForString('\r\n',15):
self.crt.Dialog.MessageBox('WaitForString \r\n is timeout!')
raise IOError
szResult = self.Tab.Screen.ReadString([Prompt,' --More--','%Error'])
index = self.Tab.Screen.MatchIndex
if (index == 3):
raise IOError
while (index == 2):
self.Tab.Screen.Send(' ')
szResult = szResult + self.Tab.Screen.ReadString([Prompt,' --More--'])
index = self.Tab.Screen.MatchIndex
szResult = szResult.replace('\b \b','')
if (szResult.rfind('\r\n') == -1):
return ''
else:
szResult = szResult.rsplit('\r\n',1)
return szResult[0]
def sessionlog(self,flag,LogFileName = None):
'''开启securecrt的会话日志功能'''
if flag == True :
if LogFileName == None :
self.Tab.Session.LogFileName = self.Tab.Caption + '_' + time.strftime('%Y.%m.%d.%H.%M.%S') + '.log'
else :
self.Tab.Session.LogFileName = LogFileName + '_' + time.strftime('%Y.%m.%d.%H.%M.%S') + '.log'
self.Tab.Session.Log(True)
elif flag == False :
self.Tab.Session.Log(flag)
self.Tab.Session.LogFileName = ''
def log(self,szString):
'''每次的日志文件名都不同,以脚本运行的时间命名日志文件名称 '''
with open(self.logfilename,'ab') as f:
datefmt = datetime.datetime.now().strftime('%Y-%m-%d %H:%M: %S,%f')[0:-3]
f.write(datefmt+'-->'+szString+'\r\n')
def expectsend(self,expect, send):
if not self.Tab.Session.Connected:
return False
if self.Tab.Screen.WaitForString(expect):
self.Tab.Screen.Send(send + '\r')
return True
def reconnect(self,username = 'who',password = 'who',enablepassword = 'zxr10'):
self.Tab.Session.Disconnect()
self.Tab.Session.Connect()
if self.Tab.Session.Connected :
if not self.Tab.Screen.WaitForString('#',5):
self.sendexpect('\r','Username:')
self.sendexpect(username,'assword:')
if self.sendexpect(password,'>',3):
self.sendexpect('enable','assword:')
self.sendexpect(enablepassword,'#')
return True
else :
return True
else:
DUT.logger('重新登陆设备失败!')
raise IOError
def isSynchronized(self):
szResult = self.rec('show synchronization | include MPU')
for row in range(self.getrowcnt(szResult)):
if (self.getstring(szResult,row,3) != 'Synchronized'):
DUT.logger('系统当前不是Synchronized状态!')
DUT.logger('\r\n'+szResult)
return False
return True
def redundancyswitch(self,le='sc',interval=10,switch_cnt=10,config_check=True,ospf_check=True,isis_check=True,bgp_check=True,ldp_check=True,pim_check=True,retry_cnt=10):
DUT.logger('\r\n'+'================================ RESTART ================================')
DUT.logger('系统开始主备倒换测试,当前倒换的逻辑实体是:' + le)
#用ctrl c 确保在特权模式下
self.sendctrl('c')
DUT.logger('开始判断系统倒换前的同步状态')
#给10次判断同步的机会
for retry in xrange(0,retry_cnt):
if not self.isSynchronized() :
DUT.logger('系统当前不是Synchronized状态,不能进行主备倒换!')
DUT.logger('30秒后,重新进行一次主备同步状态判断')
if retry == retry_cnt - 1 :
DUT.logger('10次机会用完了,依然未同步,倒换测试失败!')
return False
else :
self.crt.Sleep(30000)
continue
else :
break
DUT.logger('系统处于Synchronized状态,具备倒换的条件,可以开始倒换了!')
if (config_check == True):
DUT.logger('开始获取倒换前,系统的配置文件!')
running_config_old = self.rec('sho running-config')
DUT.logger('开始清除系统告警日志!')
self.send('clear logging')
DUT.logger('倒换测试正式开始!')
for i in range(1,switch_cnt+1):
DUT.logger('开始第'+str(i)+'次倒换,当前倒换的逻辑实体是: '+ le)
if (le == 'sc' or le == 'rp-router'):
self.sendexpect('redundancy switch '+le+' grace','[yes/no]')
elif (le == 'mpls'):
self.sendexpect('redundancy switch '+le,'[yes/no]')
self.sendexpect('yes','yes')
DUT.logger(str(interval)+'分钟后,会重新登陆设备,请耐心等待!')
self.Tab.Session.Disconnect()
self.crt.Sleep(interval*60*1000)
self.reconnect()
if (config_check == True):
DUT.logger('开始获取倒换后,系统的配置文件!')
running_config_new = self.rec('sho running-config')
if (running_config_new != running_config_old):
DUT.logger('系统倒换前后的配置不一致,请用比较软件比较保存在脚本目录的配置文件!')
with open('running_config_new.txt','wb') as f:
f.write(running_config_new)
with open('running_config_old.txt','wb') as f:
f.write(running_config_old)
return False
#判断系统同步状态
DUT.logger('开始判断倒换后,系统的主备同步状态!')
#给10次判断同步的机会
for retry in xrange(0,retry_cnt):
if not self.isSynchronized() :
DUT.logger('倒换后,系统不是Synchronized状态!')
DUT.logger('30秒后,重新进行一次主备同步状态判断')
if retry == retry_cnt - 1 :
DUT.logger('10次机会用完了,依然未同步,倒换测试失败!')
return False
else :
self.crt.Sleep(30000)
continue
else :
DUT.logger('系统当前是Synchronized状态!')
break
#判断路由是否发生震荡
DUT.logger('开始判断倒换后,系统的路由震荡情况!')
if (ospf_check == True):
szResult = self.rec('show logging alarm typeid ospf | include [Dd][Oo][Ww][Nn]')
if ( szResult != ''):
DUT.logger('倒换后,ospf路由出现震荡!')
DUT.logger('\r\n'+szResult)
return False
if (isis_check == True):
szResult = self.rec('show logging alarm typeid isis | include [Dd][Oo][Ww][Nn]')
if ( szResult != ''):
DUT.logger('倒换后,isis路由出现震荡!')
DUT.logger('\r\n'+szResult)
return False
if (bgp_check == True):
szResult = self.rec('show logging alarm typeid bgp | include [Dd][Oo][Ww][Nn]')
if ( szResult != ''):
DUT.logger('倒换后,bgp路由出现震荡!')
DUT.logger('\r\n'+szResult)
return False
if (ldp_check == True):
szResult = self.rec('show logging alarm typeid ldp | include [Dd][Oo][Ww][Nn]')
if ( szResult != ''):
DUT.logger(u'倒换过程中,ldp协议出现震荡!')
DUT.logger('\r\n'+szResult)
return False
if (pim_check == True):
szResult = self.rec('show logging alarm typeid pim | include [Dd][Oo][Ww][Nn]')
if ( szResult != ''):
DUT.logger(u'倒换过程中,pim协议出现震荡!')
DUT.logger('\r\n'+szResult)
return False
DUT.logger('第'+str(i)+'次倒换顺利结束,当前倒换的逻辑实体是: '+ le)
DUT.logger(str(switch_cnt)+'次倒换全部顺利结束,当前倒换的逻辑实体是: '+ le)
return True
# ============= 以下是静态方法 =================
@staticmethod
def logger(szString):
'''日志文件名称固定,适合做系统日志记录'''
if debug == True:
filelog_logger.info(szString)
@staticmethod
def listfind(szString,list):
index = 0
while index < len(list) :
if szString in list[index] :
return index
index = index + 1
return None
@staticmethod
def sleep(ms):
time.sleep(ms/1000.0)
@staticmethod
def dec2hex(num):
num_temp = hex(num)
return num_temp[2:]
@staticmethod
def getrowcnt(szString):
if (szString == ''):
return 0
else:
szString_temp = szString.split('\r\n')
return len(szString_temp)
@staticmethod
def getrow(szString,row):
if (szString == ''):
return 0
else:
szString_temp = szString.split('\r\n')
return szString_temp[row]
@staticmethod
def getstring(szString,row,column):
'''返回指定的某行某列的字符串'''
if (szString == ''):
return ''
else:
szString_temp = szString.split('\r\n')
szString_temp = szString_temp[row]
szString_temp = szString_temp.split()
return szString_temp[column]
@staticmethod
def getcolumn(szString,column):
'''返回一个列表'''
columnlist = []
if (szString == ''):
return columnlist
else:
for row in range(0,DUT.getrowcnt(szString)) :
columnlist.append(DUT.getstring(szString,row,column))
return columnlist
@staticmethod
def readportlist(filename='portlist.py'):
'''返回一个端口列表'''
with open(filename,'rb') as f:
portlist = f.readlines()
for index in range(0,len(portlist)):
portlist[index] = portlist[index].strip()
#支持注释功能
if '#' in portlist[index] :
portlist[index] = ''
while '' in portlist:
portlist.remove('')
return portlist
@staticmethod
def readtxt(filename):
'''读取一个文本文件,返回列表,每行数据作为一个元素'''
with open(filename,'rb') as f:
szlist = f.readlines()
for index in range(0,len(szlist)):
szlist[index] = szlist[index].strip()
#支持注释功能
if '#' in szlist[index] :
szlist[index] = ''
while '' in szlist:
szlist.remove('')
return szlist
@staticmethod
def nextipv4addr(startipv4addr , step , netmask = 24):
szipv4addrlist = startipv4addr.split('.')
intValue = int(szipv4addrlist[0])*256*256*256 + int(szipv4addrlist[1])*256*256 + int(szipv4addrlist[2])*256 + int(szipv4addrlist[3])
stepValue = step << (32 - netmask)
intValue_new = intValue + stepValue
if intValue_new > 4294967295 :
raise Exception('Value Error', 'exceed the max value !!')
ipv4addr1 = intValue_new / 16777216
temp = intValue_new % 16777216
ipv4addr2 = temp / 65536
ipv4addr3 = (temp % 65536) / 256
ipv4addr4 = intValue_new % 256
return str(ipv4addr1)+'.'+str(ipv4addr2)+'.'+str(ipv4addr3)+'.'+str(ipv4addr4)
@staticmethod
def nextipv6addr(startipv6addr , step , netmask = 48):
szipv6addrlist = startipv6addr.split(':')
length = len(szipv6addrlist)
if startipv6addr.startswith('::'):
szipv6addrlist.remove('')
szipv6addrlist[0] = ('0:'*(10-length))[:-1]
elif startipv6addr.endswith('::'):
szipv6addrlist.remove('')
szipv6addrlist[-1] = ('0:'*(10-length))[:-1]
elif length < 8 :
for index in range(length) :
if szipv6addrlist[index] == '' :
szipv6addrlist[index] = ('0:'*(9-length))[:-1]
break
startipv6addr = ':'.join(szipv6addrlist)
szipv6addrlist = startipv6addr.split(':')
for index in range(8) :
szipv6addrlist[index] = int(szipv6addrlist[index],16)
index = (netmask / 16) - 1
szipv6addrlist[index] = szipv6addrlist[index] + step
while index > -1 :
if szipv6addrlist[index] > 65535 :
szipv6addrlist[index-1] = szipv6addrlist[index-1] + (szipv6addrlist[index] / 65536)
szipv6addrlist[index] = szipv6addrlist[index] % 65536
index = index - 1
else :
break
for index in range(8) :
szipv6addrlist[index] = hex(szipv6addrlist[index])[2:]
szResult = ':'.join(szipv6addrlist)
if szResult.startswith('0:0:'):
return re.sub(r'(0:){2,}','::',szResult,count=1)
else :
szResult = re.sub(r'(:0){2,}',':',szResult,count=1)
if szResult.endswith(':'):
return szResult + ':'
return szResult
@staticmethod
def mac_address_400g(port):
shelf,slot,np,portid = port.split('-')[1].split('/')
if (mactype == 1):
delta = 0
elif (mactype == 2):
delta = int(shelf)*720 + int(slot)*40 + int(np)*10 + int(portid) + 256
basemac_temp = basemac.split(':')[::-1]
for i in range(0,5):
basemac_temp[i] = '0x' + basemac_temp[i]
mac = eval(basemac_temp[i]) + delta
if ( mac > 255 ):
basemac_temp[i] = '%02x'%(mac%256)
delta = mac/256
else:
basemac_temp[i] = '%02x'%mac
break
basemac_temp = basemac_temp[::-1]
return basemac_temp[0]+basemac_temp[1]+'.'+basemac_temp[2]+basemac_temp[3]+'.'+basemac_temp[4]+basemac_temp[5]
@staticmethod
def mac_address_1t(port):
shelf,slot,np,portid,portid2 = DUT.portdecode(port)
if (mactype == 1):
delta = 0
elif (mactype == 2):
if (portid2 == '0'):
delta = int(shelf)*1728 + int(slot)*192 + int(np)*48 + int(portid) + 256
else :
delta = int(shelf)*1728 + int(slot)*192 + int(np)*48 + int(portid)*256 + int(portid2) + 256
basemac_temp = basemac.split(':')[::-1]
for i in range(0,5):
basemac_temp[i] = '0x' + basemac_temp[i]
mac = eval(basemac_temp[i]) + delta
if ( mac > 255 ):
basemac_temp[i] = '%02x'%(mac%256)
delta = mac/256
else:
basemac_temp[i] = '%02x'%mac
break
basemac_temp = basemac_temp[::-1]
return basemac_temp[0]+basemac_temp[1]+'.'+basemac_temp[2]+basemac_temp[3]+'.'+basemac_temp[4]+basemac_temp[5]
@staticmethod
def portdecodetostr(port,prefix='1'):
shelf,slot,np,portid = port.split('-')[1].split('/')
#对4.00.10扇出口的支持
portid = portid.replace(':','')
return prefix + shelf + slot + np + portid
@staticmethod
def portdecode(port):
portid2 = '0'
shelf,slot,np,portid = port.split('-')[1].split('/')
if (':' in portid):
portid,portid2 = portid.split(':')
return (shelf,slot,np,portid,portid2)
@staticmethod
def shelf_slot_decode(board):
shelf,slot,cpu = board.split('-')[1].split('/')
return (shelf,slot)
@staticmethod
def board_decode(board):
shelf,slot,cpu = board.split('-')[1].split('/')
return (shelf,slot)
@staticmethod
def ipv4_addr(port, subcardportnum):
shelf,slot,subcard,portid = port.split('-')[1].split('/')
return str(ip_firstbyte+int(shelf))+'.'+ slot + '.'+ str(int(subcard)*subcardportnum+int(portid))+'.1'
@staticmethod
def ipv6_addr(port, subcardportnum):
shelf,slot,subcard,portid = port.split('-')[1].split('/')
return str(ip_firstbyte+int(shelf))+':'+ slot + ':'+ hex(int(subcard)*subcardportnum+int(portid))[2:]+'::1'
@staticmethod
def ipv4_addr_v4(port):
shelf,slot,np,portid,portid2 = DUT.portdecode(port)
return str(ip_firstbyte+int(shelf))+'.'+ slot + '.'+ str(int(np)*48+int(portid)*4 +int(portid2))+'.1'
@staticmethod
def ipv6_addr_v4(port):
shelf,slot,np,portid,portid2 = DUT.portdecode(port)
return str(ip_firstbyte+int(shelf))+':'+ slot + ':'+ str(int(np)*48+int(portid)*4 + int(portid2))+'::1'
def __del__(self):
if self.sendwithlogfileobj :
self.sendwithlogfileobj.close()
if self.Tab.Session.Logging :
self.Tab.Session.Log(False)
self.Tab.Session.LogFileName = ''
self.Tab.Screen.Synchronous = False
加载此库,可以在SecureCRT上运行python自动化脚本,示例如下:
# -*- coding: UTF-8 -*-
import sys,os
current_dir = os.path.dirname(os.path.realpath(__file__))
os.chdir(current_dir)
if current_dir not in sys.path :
sys.path.append(current_dir)
import SecureCRTlib as my
t = my.DUT(crt)
while 1:
t.send('who')
t.sleep(2*1000)