Package DataCollector :: Module TelnetClient
[hide private]
[frames] | no frames]

Source Code for Module DataCollector.TelnetClient

  1  ########################################################################### 
  2  # 
  3  # This program is part of Zenoss Core, an open source monitoring platform. 
  4  # Copyright (C) 2007, Zenoss Inc. 
  5  # 
  6  # This program is free software; you can redistribute it and/or modify it 
  7  # under the terms of the GNU General Public License version 2 as published by 
  8  # the Free Software Foundation. 
  9  # 
 10  # For complete information please visit: http://www.zenoss.com/oss/ 
 11  # 
 12  ########################################################################### 
 13   
 14  __doc__="""TelnetSession 
 15   
 16  TelnetSession is used by TelnetSession to issue commands to a machine 
 17  and return their output over the telnet protocol. 
 18   
 19  Device Tree Parameters are: 
 20   
 21  zTelnetLoginTries - number of times to try login default: 1 
 22  zTelnetLoginTimeout - timeout for expect statements during login default: 2 
 23  zTelnetPromptTimeout - pause used during prompt discovery default: 0.2 
 24  zTelnetCommandTimeout - default timeout when executing a command default: 5 
 25  zTelnetLoginRegex - regex to match the login prompt default: 'ogin:.$' 
 26  zTelnetPasswordRegex - regext to match the password prompt default: 'assword:.$' 
 27   
 28  Other Parameters that are used by both TelnetSession and SshTransport: 
 29  zCommandPathList - list of path to check for a command 
 30  zCommandExistanceCheck - shell command issued to look for executible 
 31                          must echo succ if executible is found 
 32                          default: test -f executible 
 33   
 34  $Id: TelnetClient.py,v 1.15 2004/04/05 02:05:30 edahl Exp $""" 
 35   
 36  __version__ = "$Revision: 1.15 $"[11:-2] 
 37   
 38  import Globals 
 39   
 40  #FIXME take away except when we are totally migrated 
 41  try: 
 42      from twisted.conch import telnet 
 43  except: 
 44      from twisted.protocols import telnet 
 45   
 46  from twisted.internet import protocol, reactor 
 47   
 48  import re 
 49  import logging 
 50  log = logging.getLogger("zen.TelnetClient") 
 51   
 52  import CollectorClient 
 53  from Exceptions import * 
 54   
 55  defaultPromptTimeout = 10  
 56  defaultCommandTimeout = 20 
 57  defaultLoginRegex = 'ogin:.$' 
 58  defaultPasswordRegex = 'assword:' 
 59  defaultEnable = False 
 60  defaultTermLength = False 
 61   
 62   
 63  responceMap = ("WILL", "WONT", "DO", "DONT") 
 64   
65 -def check(hostname):
66 "check to see if a device supports telnet" 67 from telnetlib import Telnet 68 import socket 69 try: 70 tn = Telnet(hostname) 71 tn.close() 72 return 1 73 except socket.error: 74 return 0
75 76
77 -class TelnetClientProtocol(telnet.Telnet):
78 mode = 'Login' 79 80 timeout = 0 81 timeoutID = None 82 p1 = "" 83 p2 = "" 84 commandPrompt = "" 85 command = '' 86 enabled = -1 87 scCallLater = None 88 bytes = '' 89 lastwrite = '' 90 result = '' 91 buffer = "" 92
93 - def connectionMade(self):
94 self.factory.myprotocol = self #bogus hack 95 self.hostname = self.factory.hostname 96 log.info("connected to device %s" % self.hostname) 97 self.startTimeout(self.factory.loginTimeout, self.loginTimeout) 98 self.protocol = telnet.TelnetProtocol()
99 100 # the following functions turn off all telnet options
101 - def iac_DO(self, feature):
102 log.debug("received telnet DO feature %s" % ord(feature)) 103 if ord(feature) == 1: 104 self._iac_responce(telnet.WILL, feature) 105 else: 106 self._iac_responce(telnet.WONT, feature)
107
108 - def iac_DONT(self, feature):
109 log.debug("received telnet DONT feature %s" % ord(feature)) 110 self._iac_responce(telnet.WONT, feature)
111
112 - def iac_WILL(self, feature):
113 log.debug("received telnet WILL feature %s" % ord(feature)) 114 self._iac_responce(telnet.DONT, feature)
115
116 - def iac_WONT(self, feature):
117 log.debug("received telnet WONT feature %s" % ord(feature)) 118 self._iac_responce(telnet.DONT, feature)
119
120 - def _iac_responce(self, action, feature):
121 log.debug("sending telnet action %s feature %s" % 122 (responceMap[ord(action)-251], ord(feature))) 123 self.write(telnet.IAC+action+feature)
124 125
126 - def write(self, data):
127 "save the last bit of data that we wrote" 128 self.lastwrite = data 129 self.transport.write(data)
130
131 - def processChunk(self, chunk):
132 self.buffer = self.buffer + chunk 133 regex = None 134 if self.factory.modeRegex.has_key(self.mode): 135 regex = self.factory.modeRegex[self.mode] 136 log.debug("mode '%s' regex = %s" % (self.mode, regex)) 137 log.debug("chunk received = '%s'" % chunk) 138 if regex and re.search(regex, chunk): 139 self.processLine(self.buffer) 140 self.buffer = ""
141
142 - def processLine(self, line):
143 """I call a method that looks like 'telnet_*' where '*' is filled 144 in by the current mode. telnet_* methods should return a string which 145 will become the new mode.""" 146 line = re.sub("\r\n|\r", "\n", line) #convert \r\n to \n 147 #if server is echoing take it out 148 if self.lastwrite.startswith(line): 149 self.lastwrite = self.lastwrite[len(line):] 150 line = '' 151 elif line.find(self.lastwrite) == 0: 152 line = line[len(self.lastwrite):] 153 log.debug("mode = %s", self.mode) 154 self.mode = getattr(self, "telnet_"+self.mode)(line)
155
156 - def dataReceived(self, data):
157 telnet.Telnet.dataReceived(self, data) 158 log.debug('line %r', self.bytes) 159 if self.bytes: 160 self.processLine(self.bytes) 161 self.bytes = ''
162
163 - def applicationDataReceived(self, bytes):
164 self.bytes += bytes
165 166
167 - def startTimeout(self, timeout=1, timeoutfunc=None):
168 self.cancelTimeout() 169 if timeoutfunc is None: timeoutfunc = self.defaultTimeout 170 self.timeoutID = reactor.callLater(timeout, timeoutfunc)
171 172
173 - def cancelTimeout(self):
174 if self.timeoutID: self.timeoutID.cancel() 175 self.timeoutID = None
176 177
178 - def defaultTimeout(self):
179 self.transport.loseConnection() 180 if self.factory.commandsFinished(): 181 self.factory.clientFinished() 182 regex = self.factory.modeRegex.get(self.mode, "") 183 log.warn("dropping connection to %s: " 184 "state '%s' timeout %.1f seconds regex '%s' buffer '%s'", 185 self.factory.hostname, self.mode, self.timeout,regex,self.buffer)
186 187 188
189 - def loginTimeout(self, loginTries=0):
190 if loginTries == 0: 191 loginTries = self.factory.loginTries 192 elif loginTries == 1: 193 self.transport.loseConnection() 194 self.factory.clientFinished() 195 log.warn("login to device %s failed" % self.hostname) 196 return "Done" 197 else: 198 self.factory.loginTries -= 1 199 return "Login"
200 201
202 - def telnet_Login(self, data):
203 "Called when login prompt is received" 204 log.debug('Search finds: %r', re.search(self.factory.loginRegex, data)) 205 if not re.search(self.factory.loginRegex, data): # login failed 206 return 'Login' 207 log.debug("login tries=%s" % self.factory.loginTries) 208 if not self.factory.loginTries: 209 self.transport.loseConnection() 210 log.warn("login to %s with username %s failed" % ( 211 self.factory.hostname, self.factory.username)) 212 else: 213 self.factory.loginTries -= 1 214 log.debug("sending username %s" % self.factory.username) 215 self.write(self.factory.username + '\n') 216 return 'Password'
217 218
219 - def telnet_Password(self, data):
220 "Called when the password prompt is received" 221 if not re.search(self.factory.passwordRegex, data): # look for pw prompt 222 return 'Password' 223 log.debug("sending password %s" % self.factory.password) 224 self.write(self.factory.password + '\n') 225 self.startTimeout(self.factory.promptTimeout) 226 return 'FindPrompt'
227 228
229 - def telnet_Enable(self, data):
230 "change to enable mode on cisco" 231 self.write('enable\n') 232 self.startTimeout(self.factory.loginTimeout, self.loginTimeout) 233 return "Password"
234 235
236 - def telnet_FindPrompt(self, data):
237 "Called after login to figure out the command prompt" 238 if not data.strip(): return 'FindPrompt' 239 if re.search(self.factory.loginRegex, data): # login failed 240 return self.telnet_Login(data) 241 self.p1 = data 242 if self.p1 == self.p2: 243 self.cancelTimeout() # promptTimeout 244 self.commandPrompt = self.p1 245 log.debug("found command prompt '%s'" % self.p1) 246 self.factory.modeRegex['Command'] = re.escape(self.p1) + "$" 247 self.factory.modeRegex['SendCommand'] = re.escape(self.p1) + "$" 248 if self.factory.enable: 249 self.factory.enable = False 250 return self.telnet_Enable("") 251 else: 252 self.scCallLater = reactor.callLater(1.0, 253 self.telnet_SendCommand, "") 254 return "ClearPromptData" 255 self.p2 = self.p1 256 self.p1 = "" 257 log.debug("sending \\n") 258 reactor.callLater(.1, self.write, "\n") 259 return 'FindPrompt'
260
261 - def telnet_ClearPromptData(self, data):
262 if self.scCallLater: self.scCallLater.cancel() 263 self.scCallLater = reactor.callLater(1.0, self.telnet_SendCommand, "") 264 return "ClearPromptData"
265
266 - def telnet_SendCommand(self, data):
267 "Get a command of the command stack and send it" 268 if self.scCallLater and self.scCallLater.active(): 269 self.scCallLater.cancel() 270 log.debug("sending command '%s'" % self.curCommand()) 271 self.write(self.curCommand() + '\n') 272 self.startTimeout(self.factory.commandTimeout) 273 self.mode = 'Command' 274 return 'Command'
275 276
277 - def telnet_Command(self, data):
278 """process the data from a sent command 279 if there are no more commands move to final state""" 280 self.result += data 281 if not self.result.endswith(self.commandPrompt): 282 log.debug("prompt '%s' not found", self.commandPrompt) 283 log.debug("line ends wth '%s'", data[-5:]) 284 return 'Command' 285 self.cancelTimeout() 286 data, self.result = self.result, '' 287 log.debug("command = %s" % self.curCommand()) 288 log.debug("data=%s" % data) 289 self.factory.addResult(self.curCommand(), data[0:-len(self.p1)], None) 290 self.factory.cmdindex += 1 291 if self.factory.commandsFinished(): 292 self.factory.clientFinished() 293 if not self.factory.maintainConnection: 294 self.transport.loseConnection() 295 return 'Done' 296 else: 297 return self.telnet_SendCommand("")
298 299
300 - def curCommand(self):
301 return self.factory._commands[self.factory.cmdindex]
302 303
304 -class TelnetClient(CollectorClient.CollectorClient):
305
306 - def __init__(self, hostname, ip, port, commands=[], options=None, 307 device=None, datacollector=None):
308 CollectorClient.CollectorClient.__init__(self, hostname, ip, port, 309 commands, options, device, datacollector) 310 self.protocol = TelnetClientProtocol 311 self.modeRegex = { 312 'FindPrompt' : '.*', 313 'WasteTime' : '.*', 314 'Done' : '', 315 } 316 self.promptPause = 1 317 318 if options: 319 defaultPromptTimeout = options.promptTimeout 320 defaultCommandTimeout = options.commandTimeout 321 defaultLoginRegex = options.loginRegex 322 defaultPasswordRegex = options.passwordRegex 323 defaultEnable = options.enable 324 defaultTermLength = options.termlen 325 326 if device: # if we are in zope look for parameters in aq path 327 self.promptTimeout = getattr(device, 328 'zTelnetPromptTimeout', defaultPromptTimeout) 329 self.loginRegex = getattr(device, 330 'zTelnetLoginRegex', defaultLoginRegex) 331 self.passwordRegex = getattr(device, 332 'zTelnetPasswordRegex', defaultPasswordRegex) 333 self.enable = getattr(device, 334 'zTelnetEnable', defaultEnable) 335 self.termlen = getattr(device, 336 'zTelnetTermLength', defaultTermLength) 337 else: 338 self.promptTimeout = defaultPromptTimeout 339 self.loginRegex = defaultLoginRegex 340 self.passwordRegex = defaultPasswordRegex 341 self.enable = defaultEnable 342 self.termlen = defaultTermLength 343 344 self.modeRegex['Login'] = self.loginRegex 345 self.modeRegex['Password'] = self.passwordRegex
346 347
348 - def run(self):
349 """Start telnet collection. 350 """ 351 if self.termlen: 352 self._commands.insert(0, "terminal length 0") 353 reactor.connectTCP(self.ip, self.port, self)
354 355
356 - def Command(self, commands):
357 """add new commands to be run reset cmdindex to 0""" 358 CollectorClient.CollectorClient.addCommand(self, commands) 359 if self.myprotocol.mode != "Command": 360 self.myprotocol.telnet_SendCommand("")
361 362
363 - def clientConnectionFailed(self, connector, reason):
364 """if we don't connect let the modeler know""" 365 log.warn(reason.getErrorMessage()) 366 self.clientFinished()
367 368
369 - def clientFinished(self):
370 CollectorClient.CollectorClient.clientFinished(self) 371 if __name__ == "__main__": 372 reactor.stop()
373 374
375 -def buildOptions(parser=None, usage=None):
376 parser = CollectorClient.buildOptions(parser,usage) 377 parser.add_option('-r', '--promptTimeout', 378 dest='promptTimeout', 379 type = 'float', 380 default = defaultPromptTimeout, 381 help='timeout when discovering prompt') 382 parser.add_option('-x', '--loginRegex', 383 dest='loginRegex', 384 default = defaultLoginRegex, 385 help='regex that will find the login prompt') 386 parser.add_option('-w', '--passwordRegex', 387 dest='passwordRegex', 388 default = defaultPasswordRegex, 389 help='regex that will find the password prompt') 390 parser.add_option('--enable', 391 dest='enable', action='store_true', default=False, 392 help='enter enable mode on a cisco device') 393 parser.add_option('--termlen', 394 dest='termlen', action='store_true', default=False, 395 help='enter send terminal length 0 on a cisco device') 396 return parser
397 398
399 -def main():
400 import socket 401 import getpass 402 parser = buildOptions() 403 options = CollectorClient.parseOptions(parser, 23) 404 if not options.password: 405 options.password = getpass.getpass("%s@%s's password: " % 406 (options.username, options.hostname)) 407 logging.basicConfig(level=10) 408 commands = [("test", c) for c in options.commands] 409 client = TelnetClient(options.hostname, 410 socket.gethostbyname(options.hostname), 411 options.port, 412 commands=commands, options=options) 413 client.run() 414 def stop(): 415 if client.commandsFinished(): 416 reactor.stop() 417 else: 418 reactor.callLater(1, stop)
419 stop() 420 reactor.run() 421 import pprint 422 pprint.pprint(client.getResults()) 423 424 if __name__ == '__main__': 425 main() 426