1- from typing import TypeVar , Dict , Callable , List
1+ from typing import TypeVar , Dict , Callable , List , Optional
22from abc import abstractmethod
33from collections import defaultdict
44import time
@@ -119,7 +119,7 @@ async def initialize(self, **kwargs) -> TNode:
119119 self ._conn_lock = asyncio .Lock ()
120120 self ._last_exception = None
121121 self ._exception_timestamp = None
122- self ._current_exception = None
122+ self ._current_exception : Optional [ Exception ] = None
123123 self .api_key = None
124124 self ._fetching_dev_data = False # If we are fetching the dev data
125125 self ._stdin = self ._stdout = self ._long_proc = None
@@ -141,7 +141,7 @@ async def initialize(self, **kwargs) -> TNode:
141141 self .ssh_config_file = kwargs .get ("ssh_config_file" , None )
142142 self .enable_password = kwargs .get ('enable_password' )
143143
144- passphrase = kwargs .get ("passphrase" , None )
144+ passphrase : str = kwargs .get ("passphrase" , None )
145145 jump_host = kwargs .get ("jump_host" , "" )
146146 if jump_host :
147147 jump_result = urlparse (jump_host )
@@ -230,7 +230,7 @@ def last_exception(self) -> Exception:
230230 return self ._last_exception
231231
232232 @property
233- def current_exception (self ) -> Exception :
233+ def current_exception (self ) -> Optional [ Exception ] :
234234 '''The current exception faced on this device'''
235235 return self ._current_exception
236236
@@ -1665,7 +1665,7 @@ def _extract_nos_version(self, data) -> None:
16651665
16661666class IosXENode (Node ):
16671667 '''IOS-XE Node-sepcific telemetry gather implementation'''
1668- WAITFOR = r'.*[>#]\s*$' # devtype specific termination sequence
1668+ IOS_DEFAULT_PROMPT = ( '>' , '#' ) # devtype specific termination sequence
16691669
16701670 async def _rest_connect (self ):
16711671 raise NotImplementedError (
@@ -1678,6 +1678,7 @@ async def _rest_gather(self, service_callback, cmd_list, cb_token,
16781678
16791679 async def _fetch_init_dev_data_devtype (self , reconnect : bool ):
16801680 """Fill in the boot time of the node by executing certain cmds"""
1681+ self .prompt = self .IOS_DEFAULT_PROMPT
16811682 await self ._exec_cmd (self ._parse_init_dev_data ,
16821683 ["show version" ], None , 'text' ,
16831684 reconnect = reconnect )
@@ -1701,6 +1702,8 @@ async def _ssh_connect(self):
17011702 output = await self .wait_for_prompt ()
17021703 if output .strip ().endswith ('>' ):
17031704 if await self ._handle_privilege_escalation () == - 1 :
1705+ self .logger .error (f'{ self .address } :{ self .port } : '
1706+ 'Privilege escalation failed' )
17041707 await self ._close_connection ()
17051708 self ._conn = None
17061709 self ._stdin = None
@@ -1719,7 +1722,7 @@ async def _ssh_connect(self):
17191722
17201723 # Set the terminal length to 0 to avoid paging
17211724 self ._stdin .write ('terminal length 0\n ' )
1722- output = await self ._stdout .readuntil (self .WAITFOR )
1725+ output = await self ._stdout .readuntil (self .prompt )
17231726
17241727 async def _handle_privilege_escalation (self ) -> int :
17251728 '''Escalata privilege if necessary
@@ -1729,7 +1732,13 @@ async def _handle_privilege_escalation(self) -> int:
17291732 self .logger .info (
17301733 f'Privilege escalation required for { self .hostname } ' )
17311734 self ._stdin .write ('enable\n ' )
1732- output = await self .wait_for_prompt (r'Password:\s*' )
1735+ output = await self .wait_for_prompt (('Password:' , '%' ))
1736+ if '%' in output :
1737+ self .logger .error ('Privilege escalation failed, Aborting' )
1738+ self .current_exception = PollingError (
1739+ 'Privilege Escalation Failed' )
1740+ return - 1
1741+
17331742 if self .enable_password :
17341743 self ._stdin .write (self .enable_password + '\n ' )
17351744 else :
@@ -1739,8 +1748,8 @@ async def _handle_privilege_escalation(self) -> int:
17391748 if (output in ['suzieq timeout' , 'Password:' ] or
17401749 output .strip ().endswith ('>' )):
17411750 self .logger .error (
1742- f'Privilege escalation failed for { self . hostname } '
1743- ', Aborting connection' )
1751+ f'{ self . address } : { self . port } Privilege escalation failed, '
1752+ 'Aborting connection' )
17441753 return - 1
17451754
17461755 self .logger .info (f'Privilege escalation succeeded for { self .hostname } ' )
@@ -1762,7 +1771,7 @@ async def wait_for_prompt(self, prompt: str = None,
17621771 the output data or 'timeout'
17631772 """
17641773 if prompt is None :
1765- prompt = self .WAITFOR
1774+ prompt = self .prompt
17661775 coro = self ._stdout .readuntil (prompt )
17671776 try :
17681777 output = await asyncio .wait_for (coro , timeout = timeout )
@@ -1772,6 +1781,8 @@ async def wait_for_prompt(self, prompt: str = None,
17721781 self .logger .error (f'{ self .address } .{ self .port } '
17731782 'Timed out waiting for expected prompt' )
17741783 # Return something that won't ever be in real output
1784+ await self ._close_connection ()
1785+ self .prompt = self .IOS_DEFAULT_PROMPT
17751786 return 'suzieq timeout'
17761787
17771788 async def _parse_init_dev_data_devtype (self , output , cb_token ) -> None :
@@ -1786,6 +1797,9 @@ async def _parse_init_dev_data_devtype(self, output, cb_token) -> None:
17861797 hostupstr = re .search (r'(\S+)\s+uptime is (.*)\n' , data )
17871798 if hostupstr :
17881799 self ._set_hostname (hostupstr .group (1 ))
1800+ self .prompt = tuple (f'{ self .hostname } { x } '
1801+ for x in self .IOS_DEFAULT_PROMPT )
1802+
17891803 timestr = hostupstr .group (2 )
17901804 self .bootupTimestamp = parse_relative_timestamp (
17911805 timestr , output [0 ]['cmd_timestamp' ] / 1000 )
@@ -1832,7 +1846,7 @@ async def _ssh_gather(self, service_callback, cmd_list, cb_token, oformat,
18321846
18331847 cmd_timestamp = time .time ()
18341848 self ._stdin .write (cmd + '\n ' )
1835- output = await self .wait_for_prompt ()
1849+ output = await self .wait_for_prompt (timeout = timeout )
18361850 if 'Invalid input detected' in output :
18371851 status = - 1
18381852 elif 'suzieq timeout' in output :
0 commit comments