def login(self): logger = logging.getLogger("protocols") try: match = self.terminal.expect([ 'The authenticity of host .* can\'t be established', '[Pp]assword:', self.prompt ]) except pexpect.TIMEOUT: raise exceptions.NetworkException( "Time-out (>%d sec) while connecting to host ('ssh %s@%s')" % (self.timeout, self.username, self.hostname)) if match == 0: # 'The authenticity of host .* can\'t be established' try: self.terminal.expect( 'Are you sure you want to continue connecting') self.terminal.sendline('yes') except pexpect.TIMEOUT, pexpect.EOF: raise exceptions.MalformedIO( "Authenticity of host %s can't be established. Expected question, but got time-out" % (self.hostname)) try: match = 1 + self.terminal.expect(['[Pp]assword:', self.prompt]) except pexpect.TIMEOUT, pexpect.EOF: raise exceptions.MalformedIO( "Unexpected time-out (>%d sec) while waiting for prompt from %s" % (self.timeout, self.hostname))
def ParseCLILines(lines, skipStartLines=0, lastSkipLineRe=None, skipEndLines=0): """Delete first few and last few lines in an array""" if skipStartLines > 0: if lastSkipLineRe != None: # sanity check. Make sure last line to skip matches the given regexp if None == re.match(lastSkipLineRe, lines[(skipStartLines - 1)]): raise exceptions.MalformedIO( "Expected '%s' at line %d of result, but found '%s'." % (lastSkipLineRe, skipStartLines, lines[(skipStartLines - 1)].strip())) if len(lines) < skipStartLines: raise exceptions.MalformedIO( "Can't skip first %d lines of result %s. It only contains %d lines." % (skipStartLines, repr(lines), len(lines))) del lines[0:skipStartLines] if skipEndLines > 0: if len(lines) < skipEndLines: raise exceptions.MalformedIO( "Can't skip last %d lines of result %s. It only contains %d lines." % (skipEndLines, repr(lines), len(lines))) del lines[-skipEndLines:] return lines
def login(self): logger = logging.getLogger("protocols") try: match = self.terminal.expect( ['[Ll]ogin:', '[Pp]assword:', self.prompt]) except pexpect.TIMEOUT: raise exceptions.NetworkException( "Time-out while connecting to host ('telnet %s %d')" % (self.hostname, self.port)) if match == 0: # Login prompt try: self.terminal.sendline(self.username) match = 1 + self.terminal.expect(['[Pp]assword:', self.prompt]) except pexpect.TIMEOUT: raise exceptions.MalformedIO( "Unexpected time-out while waiting for prompt from %s" % (self.hostname)) if match == 1: # Password prompt try: self.terminal.sendline(self.password) match = 1 + self.terminal.expect( ['Permission denied', self.prompt]) if match == 1: # permission denied if self.password: raise exceptions.NetworkException( "Password failed when connecting to %s@%s" % (self.username, self.hostname)) else: raise exceptions.NetworkException( "No password given for %s@%s. Unable to connect" % (self.username, self.hostname)) except pexpect.TIMEOUT, pexpect.EOF: raise exceptions.MalformedIO( "Unexpected time-out (>%d sec) while authenticating to %s" % (self.timeout, self.hostname))
def readmessage(self, timeout): """Reads text from the terminal up to the next delimiter. Does return the string as-is, without checking validity. The result MUST be an UTF-8 encoded string. Should raise an TimeOut in case more then timeout seconds have been passed.""" logger = logging.getLogger("protocols") try: self.terminal.expect(self.delimiter, timeout=timeout) # Store the result in a variable resultString = self.terminal.before except pexpect.TIMEOUT: raise exceptions.MalformedIO( "No delimiter %s after in data %s from %s" % (repr(self.delimiter), self.terminal.before, self.hostname)) instring = resultString + self.delimiter self.writetolog(instring.replace("\r\n", "\n"), output=True) logger.debug("Received %d bytes of data" % len(resultString)) return resultString
def sendcommand(self, string): self.writetolog(string, input=True) logger = logging.getLogger("protocols") logger.debug("Sending command %s" % (repr(string))) try: self.terminal.sendline(string) if self.hasecho: # if the command is 'command\n', the echo is 'command\r\n' expectstring = string.replace("\n", "\r\n") self.terminal.expect(expectstring, timeout=self.timeout) echostring = self.terminal.before + expectstring self.writetolog(echostring.replace("\r\n", "\n"), output=True) except pexpect.TIMEOUT: raise exceptions.MalformedIO( "No echo response %s in data %s from %s" % (repr(echostring), repr(self.terminal.before), self.hostname)) self.lastcommand = string.strip()
def makeCommand(self, command): """TL1 emulation: removes the ctag. The identifier is None""" if command[-1] == ";": command = command[:-1] command = command.split(":") try: command[3] = "" # set ctag to empty except IndexError: raise exceptions.MalformedIO( "Invalid TL1 command given. The fourth (ctag) parameter MUST be present. E.g.: ACT-USER:::ctag;" ) if self.ignorecase: command[0] = command[0].upper() if self.ignorecredentials and (command[0] in ["ACT-USER", "CANC-USER" ]): # e.g. "act-user::username:ctag::password" # if len(command) > 2: # command[2] = "" # remove username if len(command) > 5: command[5] = "" # remove password command = ":".join(command) + ";" return (None, command + "\n")
def makeCommand(self, command): """Takes a command, and turns it into a string read to send to the device. It may add a line break (I/O specific), or identifier in the command (language-specific). Returns a tuple (identifier, commandstring). The identifier is the ctag.""" self.acquireMemLock() ctag = self.ctag self.ctag += 1 self.releaseMemLock() if (len(command) > 0) and (command[-1] == ";"): command = command[:-1] command = command.split(":") if self.ignorecase: command[0] = command[0].upper() try: command[3] = str(ctag) # set ctag to empty command = ":".join(command) + ";" except IndexError: raise exceptions.MalformedIO( "Invalid TL1 command given. The fourth (ctag) parameter MUST be present. E.g.: ACT-USER:::ctag;" ) command = ":".join(command) + ";" return (str(ctag), command + "\n")
def isAutonomousType(self, identifier, status): """Given the identifier and status, decide if the message is autonomous, and if so, if it is of a certain type. For regular (non-autonomous), return None.""" responsetype = status[0] # (responsetype, status, comment) = status if responsetype == 'M': return False # regular message elif (responsetype[0] == "A"): return "auto" # autonomous message or no alarm elif (responsetype == "*C"): return "critical" # critical alarm elif (responsetype == "**"): return "major" # major alarm elif (responsetype == "*^"): return "minor" # minor alarm else: raise exceptions.MalformedIO( "Received an unknown message type '%s' (only understand M, A and *) with identifier %s from %s" % (responsetype, identifier, self.getTarget()))
def parseMessage(self, resultString): """Takes a message, and parses it into a tripley (resultlines, identifier, status) The resultline is an array of result line (e.g. ['10.1a.3:NOP,NONE,NONE:INOPTDEGR=-15.00,INOPTCRIT=-18.0']), the identifier is the ctag. The status is a 3-item list [type, status, comment] with type 'M' or 'A', status "DENY" or "COMPLD", and comment whatever string was found between /* and */. May raise a ParsingError in case the output can't be parsed, but does not raise an exception if the status is unsuccessful.""" logger = logging.getLogger("protocols") # The result should start with a header line (2,3,4), and result lines (5,6): # 1 # BeautyCees 07-03-13 14:52:28 2 # M 123 COMPLD (normal response) 3 # A 123 REPT DBCHG EVT SECU IDVN (automatic message) 3 # *C 123 REPT ALM CRS (critical alarm) 3 # ** 123 REPT ALM ENV DBCHG EVT SECU (major alarm) 3 # *^ 123 REPT ALM (minor alarm) 3 # PLNA (error code) 4 # /* Here is a comment. */ 5 # "10.3a.1-10.3a.2:SRCPORT=10.3a.1,DSTPORT=10.3a.2" 6 # "10.2a.7-10.3a.5:SRCPORT=10.2a.7,DSTPORT=10.3a.5" 6 # return lines formated as: # "resultstring, possible with \"quotes\"" (line type 5) # note that the header lines (1,2,3) can be repeated in the list of resultsline (type 5) identifierRE = re.compile( r'^\s+(\w+) (\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d)$') statusRE = re.compile(r'^([MA\*][\*C\^]?)\s+(\S+)\s([\w ]+)$') moreStatusRE = re.compile(r'^\s+([\w ]+)$') commentRE = re.compile(r'^\s+/\*(.*)\*/') resultRE = re.compile(r'^\s+"{0,1}([^\n]*)[,"]$') resultLines = resultString.split('\r\n') commentlines = [] resultlines = [] responsetype = None status = None ctag = None skiplines = True # only store result and comment lines after a valid status line # line types for line in resultLines: statusmatch = statusRE.match(line) morestatusmatch = moreStatusRE.match(line) identifiermatch = identifierRE.match(line) commentmatch = commentRE.match(line) resultmatch = resultRE.match(line) if statusmatch: if ctag == None: responsetype = statusmatch.group(1) ctag = statusmatch.group(2) status = statusmatch.group( 3 ) # warning: may be more then one word! (e.g. "COMPLD" or "DENY" or "REPT ALM CRS") skiplines = False elif ctag == statusmatch.group(2): skiplines = False else: logger.warning( "Ignoring TL1 output with ctag %s, since the output of ctag %s is not finished (we can only handle output one-by-one)." % (statusmatch.group(2), ctag)) skiplines = True elif morestatusmatch: status = status + " " + morestatusmatch.group( 1) # warning: may be more then one word! elif resultmatch: match = resultmatch.group(1) if skiplines: if ctag == None: logger.error( "Haven't receive a valid status line yet. Thus skip TL1 result line %s" % repr(match)) else: logger.warning("Skip TL1 result line %s" % repr(match)) else: resultlines.append(match) elif commentmatch: match = commentmatch.group(1) if skiplines: logger.warning("Skip TL1 comment line %s" % repr(match)) else: commentlines.append(match.strip()) elif identifiermatch: pass elif line == "": # this instruction must come before the line[0] == ">" checks pass elif line[0] == ">": pass elif line[0] == "<": pass elif line == ";": skiplines = True # termination line else: logger.error("Skip uknown TL1 line %s" % repr(line)) if ctag == None: raise exceptions.MalformedIO( "Could not find valid response header (e.g. 'M 123 COMPLD') in response %s" % repr(resultString)) # NOTE: we actually like to include the associated command that was send out, but we don't have that information. # However, experience showed that command is often unrelated to the alarm. So we leave it as it is. comment = " ".join(commentlines) # paste them together # The comment line typically contains the error message status = [responsetype, status, comment] logger.debug("Received %d lines of data, identifier=%s, status=%s" % (len(resultlines), ctag, status)) return (resultlines, ctag, status)
class TelnetInput(CLIIOInput, CLILangInput, base.BaseSyncInput): """Telnet input, based on the expect class. This opens a telnet subprocess, and most likely only works for UNIX.""" port = 23 # I/O commands def connect(self): try: if not self.username: raise AttributeError( "username is not set for %s. Please call setLoginCredentials() before getSubject()." % self.hostname) self.terminal = pexpect.spawn('telnet %s %d' % (self.hostname, self.port)) self.terminal.timeout = int(self.timeout) except pexpect.ExceptionPexpect: raise exceptions.NetworkException( "Problem spawning a new process ('telnet %s %d')" % (self.hostname, self.port)) def login(self): logger = logging.getLogger("protocols") try: match = self.terminal.expect( ['[Ll]ogin:', '[Pp]assword:', self.prompt]) except pexpect.TIMEOUT: raise exceptions.NetworkException( "Time-out while connecting to host ('telnet %s %d')" % (self.hostname, self.port)) if match == 0: # Login prompt try: self.terminal.sendline(self.username) match = 1 + self.terminal.expect(['[Pp]assword:', self.prompt]) except pexpect.TIMEOUT: raise exceptions.MalformedIO( "Unexpected time-out while waiting for prompt from %s" % (self.hostname)) if match == 1: # Password prompt try: self.terminal.sendline(self.password) match = 1 + self.terminal.expect( ['Permission denied', self.prompt]) if match == 1: # permission denied if self.password: raise exceptions.NetworkException( "Password failed when connecting to %s@%s" % (self.username, self.hostname)) else: raise exceptions.NetworkException( "No password given for %s@%s. Unable to connect" % (self.username, self.hostname)) except pexpect.TIMEOUT, pexpect.EOF: raise exceptions.MalformedIO( "Unexpected time-out (>%d sec) while authenticating to %s" % (self.timeout, self.hostname)) if match != 2: # haven't gotten a prompt yet try: self.terminal.expect(self.prompt) except pexpect.TIMEOUT, pexpect.EOF: raise exceptions.MalformedIO( "Unexpected time-out (>%d sec) while waiting for prompt from %s" % (self.timeout, self.hostname))
try: self.terminal.sendline(self.password) match = 1 + self.terminal.expect( ['Permission denied', self.prompt]) if match == 1: # permission denied if self.password: raise exceptions.NetworkException( "Password failed when connecting to %s@%s" % (self.username, self.hostname)) else: raise exceptions.NetworkException( "No password given for %s@%s. Unable to connect" % (self.username, self.hostname)) except pexpect.TIMEOUT, pexpect.EOF: raise exceptions.MalformedIO( "Unexpected time-out (>%d sec) while authenticating to %s" % (self.timeout, self.hostname)) if match != 2: # haven't gotten a prompt yet try: self.terminal.expect(self.prompt) except pexpect.TIMEOUT, pexpect.EOF: raise exceptions.MalformedIO( "Unexpected time-out (>%d sec) while waiting for prompt from %s" % (self.timeout, self.hostname)) # raise exceptions.MalformedIO("Expected 'permission denied' or '%s', but got: '%s'" % (self.prompt, self.terminal.before)) logger.debug("Succesfully logged in to %s" % (self.hostname)) class CLIEmulatorInput(emulate.FileIOInput, CLILangInput, base.BaseSyncInput): """Emulates a CLI input, but in reality, reads data from a file""" pass