Python telnetlib与Cisco节点的控制台连接
我正在尝试通过一个思科终端/通信服务器连接到思科设备的控制台。为此,我在特定的端口上通过telnet连接到思科终端/通信服务器的IP地址,比如说X.X.X.X - 端口2068。当我在我的电脑上通过命令行界面(CLI)这样做时,情况如下:
[user@computer]$ telnet X.X.X.X 2068
Trying X.X.X.X...
Connected to X.X.X.X (X.X.X.X).
Escape character is '^]'.
Username: <user>
Password:
console-cisco-node>
在我的电脑上通过命令行界面没有问题。但是,当我在我的电脑上运行下面的Python代码时,它似乎不太好使……
#! /usr/bin/env python
import telnetlib
tn = telnetlib.Telnet("X.X.X.X",2068)
tn.set_debuglevel(8)
data = tn.read_some()
tn.close()
if data == '':
print 'variable data is EMPTY'
else:
print data
print "variable data is FILLED !!!"
运行这段代码时,我只看到这个,感觉像是'tn.read_some()'一直在等待,因为从思科终端/通信服务器没有任何数据过来?('tn.read_all()'也是一样)
附注:我通过按CTRL-C停止了正在运行的代码。
[user@computer]$ ./test.py
Telnet(X.X.X.X,2068): recv '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f'
Telnet(X.X.X.X,2068): IAC WILL 1
Telnet(X.X.X.X,2068): IAC WILL 3
Telnet(X.X.X.X,2068): IAC DO 24
Telnet(X.X.X.X,2068): IAC DO 31
Telnet(X.X.X.X,2068): recv '\xff\xfc\x01'
Telnet(X.X.X.X,2068): IAC WONT 1
Telnet(X.X.X.X,2068): recv '\xff\xfc\x03'
Telnet(X.X.X.X,2068): IAC WONT 3
Telnet(X.X.X.X,2068): recv '\xff\xfe\x18'
Telnet(X.X.X.X,2068): IAC DONT 24
Telnet(X.X.X.X,2068): recv '\xff\xfe\x1f'
Telnet(X.X.X.X,2068): IAC DONT 31
Traceback (most recent call last):
File "./test.py", line 7, in ?
data = tn.read_some()
File "/usr/lib64/python2.4/telnetlib.py", line 345, in read_some
self.fill_rawq()
File "/usr/lib64/python2.4/telnetlib.py", line 521, in fill_rawq
buf = self.sock.recv(50)
KeyboardInterrupt
当我把代码中的'tn.read_some()'改成'tn.read_eager()'、'tn.read_very_eager()'、'tn.read_lazy()'或'tn.read_very_lazy()',然后再次运行代码时,它给我显示了这个:
[user@computer]$ ./test.py
variable data is EMPTY
当我把Python代码改为连接思科设备的管理连接(另一个IP地址Y.Y.Y.Y,正常端口23),如下所示,它就正常工作了,我看到了这个输出:
[user@computer]$ ./test1.py
Telnet(Y.Y.Y.Y,23): recv '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f'
Telnet(Y.Y.Y.Y,23): IAC WILL 1
Telnet(Y.Y.Y.Y,23): IAC WILL 3
Telnet(Y.Y.Y.Y,23): IAC DO 24
Telnet(Y.Y.Y.Y,23): IAC DO 31
Telnet(Y.Y.Y.Y,23): recv '\r\n************************************************'
************************************************
variable data is FILLED !!!
所以我觉得Python代码是没问题的。只是思科终端/通信服务器(X.X.X.X)的反应和正常情况差别太大,导致Python的telnetlib感到困惑。
有没有人遇到过类似的情况?
3 个回答
我也在和思科(Cisco)以及其他路由器进行交互。为了实现telnet通信,我扩展了Telnet类,并添加了一个叫做read
的方法,代码如下:
def read(self, timeout):
s = time.time()
resp = ""
while True:
res = self.read_eager()
if not res and resp:
return resp
resp += res
if time.time() - s >= timeout:
return resp
time.sleep(0.001)
简单来说,"问题"在于各种read_*
方法并不能返回“所有”内容,主要是因为“所有”的定义并不明确。它们会返回缓冲区中已有的内容,或者先填充缓冲区再返回内容,这取决于你调用的是哪个方法。我的做法是“在还有东西可以读取的时候继续读取(当我读取到空字符串时停止)或者直到超过timeout
秒为止”。另外,你应该看看read_until
方法,它的工作原理和expect差不多。
def read_until(self, match, timeout=None):
"""Read until a given string is encountered or until timeout.
When no match is found, return whatever is available instead,
possibly the empty string. Raise EOFError if the connection
is closed and no cooked data is available.
"""
我找到了另一种方法(感谢 Serge Ballesta)
在我的情况下,telnetlib这个对象无法使用,所以我不得不使用socket对象,通过思科的终端/通信服务器连接到思科节点的控制台连接。下面是一些基本的原始代码,这让我可以开始编写我需要的程序。
#! /usr/bin/env python
import socket
import time
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect(("X.X.X.X",2068))
data1 = s.recv(1024)
#
# when the connection with the server is made the server sends some (IAC) commands
#
# '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f'
#
# as far as I know this means the following:
#
# IAC WILL ECHO
# IAC WILL SUPRESS GO AHEAD
# IAC DO TERMINAL TYPE
# IAC DO WINDOW SIZE
#
# WILL means the server wants to use the indicated facility if the client has the possibilty to use this facility
# (client can reply with DO or DONT)
#
# DO means the server has the possibility for the indicated facility and asks the client if he wants to use this facility
# (client can reply with WILL or WONT)
#
#
# PS.
# WILL = xFB = 251
# WONT = xFC = 252
# DO = xFD = 253
# DONT = xFE = 254
#
#
#
# Python is going to answer as follwos:
#
# IAC DONT ECHO
# IAC DO SUPRESS GO AHEAD
# IAC WONT TERMINAL TYPE
# IAC WONT WINDOW SIZE
#
s.sendall('\xff\xfe\x01\xff\xfd\x03\xff\xfc\x18\xff\xfc\x1f')
data2 = s.recv(1024) # server sends '\xff\xfc\x01' = IAC WONT ECHO as a reply back
# send an enter to make the hop from the terminal server to the console connection of the node we want to be on
s.send('\r')
data3 = ''
while not 'Username: ' in data3:
data3 = s.recv(2048)
s.send('<USERNAME>\r')
time.sleep(0.1)
s.recv(1024)
s.send('<PASSWORD>\r')
time.sleep(0.1)
data5 = s.recv(1024)
s.send('exit\r')
s.close()
#print repr(data1) # '\xff\xfb\x01\xff\xfb\x03\xff\xfd\x18\xff\xfd\x1f' from the server
#print repr(data2) # '\xff\xfc\x01' from the server
#print data3 # banner and Username: from the console connection of the node we want to be on
prompt_console = data5.split()[-1]
print 'prompt van de console = %s' %prompt_console[:len(prompt_console)-1]
except socket.error, e:
print e
except socket.timeout, e:
print e
看起来在2068端口上,你并没有真正的telnet协议。根据telnet的手册,当连接到一个非标准端口时,telnet不会自动启动TELNET选项。如果端口号前面有一个负号,初始的选项协商就会进行。但是telnetlib假设如果你使用它,你就想要一个telnet协议。正如你所说,使用wireshark监控这个连接并不容易,你可以尝试用命令行telnet连接到-2068端口,看看是否会遇到和telnetlib一样的问题。
使用一个简单的socket会不会是一个选择呢?
s = socket.create_connection(('X.X.X.X', 2068))
data = s.recv(1024)
...