1515from ..factory import target_factory
1616from ..protocol import CommandProtocol , ConsoleProtocol , FileTransferProtocol
1717from ..step import step
18- from ..util import gen_marker
18+ from ..util import gen_marker , Timeout
1919from .commandmixin import CommandMixin
2020from .common import Driver
2121from .exception import ExecutionError
@@ -37,6 +37,9 @@ class ShellDriver(CommandMixin, Driver, CommandProtocol, FileTransferProtocol):
3737 password (str): password to login with
3838 keyfile (str): keyfile to bind mount over users authorized keys
3939 login_timeout (int): optional, timeout for login prompt detection
40+ post_login_settle_time (int): optional, seconds of silence after logging in
41+ before check for a prompt. Useful when the console is interleaved with boot
42+ output which may interrupt prompt detection
4043 """
4144 bindings = {"console" : ConsoleProtocol , }
4245 prompt = attr .ib (validator = attr .validators .instance_of (str ))
@@ -47,6 +50,7 @@ class ShellDriver(CommandMixin, Driver, CommandProtocol, FileTransferProtocol):
4750 login_timeout = attr .ib (default = 60 , validator = attr .validators .instance_of (int ))
4851 console_ready = attr .ib (default = "" , validator = attr .validators .instance_of (str ))
4952 await_login_timeout = attr .ib (default = 2 , validator = attr .validators .instance_of (int ))
53+ post_login_settle_time = attr .ib (default = 0 , validator = attr .validators .instance_of (int ))
5054
5155
5256 def __attrs_post_init__ (self ):
@@ -114,9 +118,9 @@ def run(self, cmd, timeout=30.0, codec="utf-8", decodeerrors="strict"):
114118 def _await_login (self ):
115119 """Awaits the login prompt and logs the user in"""
116120
117- start = time . time ( )
121+ timeout = Timeout ( float ( self . login_timeout ) )
118122
119- expectations = [self .prompt , self .login_prompt , TIMEOUT ]
123+ expectations = [self .prompt , self .login_prompt , "Password: " , TIMEOUT ]
120124 if self .console_ready != "" :
121125 expectations .append (self .console_ready )
122126
@@ -126,6 +130,7 @@ def _await_login(self):
126130 # Because pexpect keeps any read data in it's buffer when a timeout
127131 # occours, we can't lose any data this way.
128132 last_before = b''
133+ did_login = False
129134
130135 while True :
131136 index , before , _ , _ = self .console .expect (
@@ -142,18 +147,15 @@ def _await_login(self):
142147 elif index == 1 :
143148 # we need to login
144149 self .console .sendline (self .username )
145- index , _ , _ , _ = self .console .expect ([self .prompt , "Password: " ], timeout = 10 )
146- if index == 1 :
147- if self .password :
148- self .console .sendline (self .password )
149- remaining_time = (start + self .login_timeout ) - time .time ()
150- self .console .expect (self .prompt , timeout = remaining_time )
151- else :
152- raise Exception ("Password entry needed but no password set" )
153- self ._check_prompt ()
154- break
150+ did_login = True
155151
156152 elif index == 2 :
153+ if self .password :
154+ self .console .sendline (self .password )
155+ else :
156+ raise Exception ("Password entry needed but no password set" )
157+
158+ elif index == 3 :
157159 # expect hit a timeout while waiting for a match
158160 if before == last_before :
159161 # we did not receive anything during
@@ -162,17 +164,22 @@ def _await_login(self):
162164 # newline to check the state
163165 self .console .sendline ("" )
164166
165- elif index == 3 :
167+ elif index == 4 :
166168 # we have just activated a console here
167169 # lets start over again and see if login or prompt will appear
168170 # now.
169171 self .console .sendline ("" )
170172
171173 last_before = before
172174
173- if time . time () > start + self . login_timeout :
175+ if timeout . expired :
174176 raise TIMEOUT ("Timeout of {} seconds exceeded during waiting for login" .format (self .login_timeout )) # pylint: disable=line-too-long
175177
178+ if did_login :
179+ if self .post_login_settle_time > 0 :
180+ self .console .settle (self .post_login_settle_time , timeout = timeout .remaining )
181+ self ._check_prompt ()
182+
176183 @step ()
177184 def get_status (self ):
178185 """Returns the status of the shell-driver.
0 commit comments