Skip to content

Commit ccf952d

Browse files
ssh: Fix connection timeout handling
Pass the timeout also as paramiko.SshClient(banner_timeout=...) parameter so that we don't get the following exception too early: SSH logic error: Error reading SSH protocol banner Also fix the case where the target hostname is not even available (e.g. very early boot before the network stack has been started).
1 parent 9c4f09b commit ccf952d

File tree

1 file changed

+49
-10
lines changed

1 file changed

+49
-10
lines changed

devlib/utils/ssh.py

Lines changed: 49 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -425,16 +425,55 @@ def _make_client(self):
425425
self.strict_host_check
426426
))
427427
client.set_missing_host_key_policy(policy)
428-
client.connect(
429-
hostname=self.host,
430-
port=self.port,
431-
username=self.username,
432-
password=self.password,
433-
key_filename=self.keyfile,
434-
timeout=self.timeout,
435-
look_for_keys=check_ssh_keys,
436-
allow_agent=check_ssh_keys
437-
)
428+
429+
get_time = time.monotonic
430+
start = get_time()
431+
timeout = self.timeout
432+
while True:
433+
try:
434+
client.connect(
435+
hostname=self.host,
436+
port=self.port,
437+
username=self.username,
438+
password=self.password,
439+
key_filename=self.keyfile,
440+
timeout=timeout,
441+
banner_timeout=timeout,
442+
channel_timeout=timeout,
443+
auth_timeout=timeout,
444+
look_for_keys=check_ssh_keys,
445+
allow_agent=check_ssh_keys
446+
)
447+
# There does not seem to be any *_timeout parameter that waits
448+
# until the hostname is available, so we implement it manually
449+
# here.
450+
except paramiko.ssh_exception.NoValidConnectionsError as e:
451+
elapsed = get_time() - start
452+
timeout = self.timeout - elapsed
453+
if timeout > 0:
454+
sleep = min(
455+
# Wait a good chunk of time before retrying.
456+
self.timeout / 10,
457+
# We still want to be able to connect ASAP, so we
458+
# don't wait too long either until we retry. If the
459+
# user timeout is very large, we don't want to end
460+
# up waiting e.g. 10s before retrying.
461+
1,
462+
# Never sleep for longer than the remaining duration
463+
max(
464+
timeout / 2,
465+
# Do not sleep less than 10ms, otherwise we
466+
# would end up splitting the remaining time
467+
# into ever-thinner sleeps
468+
10e-3,
469+
)
470+
)
471+
time.sleep(sleep)
472+
continue
473+
else:
474+
raise e
475+
else:
476+
break
438477

439478
return client
440479

0 commit comments

Comments
 (0)