SecureCRT python 自动化库及使用示例

# -*- 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)




 

你可能感兴趣的:(SecureCRT python 自动化库及使用示例)