asyncio.wait_for#
У метода readuntil есть одна проблема - у него нет параметра timeout, в итоге,
если указанная строка не найдена, метод зависает, пока соединение не прервется.
Исправить это можно с помощью функции asyncio.wait_for
:
asyncio.wait_for(aw, timeout)
Функция wait_for запускает awaitable и ждет его выполнение указанный timeout. Если сопрограмма не выполнилась за timeout, генерируется исключение asyncio.TimeoutError.
С использованием asyncio.wait_for
функция send_show будет выглядеть так:
async def send_show(host, username, password, enable_password, command):
print(f"Подключение к {host}")
ssh = await asyncssh.connect(
host=host,
username=username,
password=password,
encryption_algs="+aes128-cbc,aes256-cbc",
)
writer, reader, stderr = await ssh.open_session(
term_type="Dumb", term_size=(200, 24)
)
try:
await asyncio.wait_for(reader.readuntil(">"), timeout=3)
writer.write("enable\n")
await asyncio.wait_for(reader.readuntil("Password"), timeout=3)
writer.write(f"{enable_password}\n")
await asyncio.wait_for(reader.readuntil([">", "#"]), timeout=3)
writer.write("terminal length 0\n")
await asyncio.wait_for(reader.readuntil("#"), timeout=3)
print(f"Отправка команды {command} на {host}")
writer.write(f"{command}\n")
output = await asyncio.wait_for(reader.readuntil("#"), timeout=3)
ssh.close()
return output
except asyncio.TimeoutError as error:
print("TimeoutError при выполнении reader.readuntil")
Так как писать asyncio.wait_for
для каждого вызова reader.readuntil
не очень удобно, можно сделать отдельную сопрограмму, которая выполняет эти
операции, а также выводит последний вывод, который был получен с устройства,
если readuntil не дождался нужного символа:
async def read_until(reader, line, timeout=3):
try:
return await asyncio.wait_for(reader.readuntil(line), timeout)
except asyncio.TimeoutError as error:
output = ""
while True:
try:
output += await asyncio.wait_for(reader.read(1000), 0.1)
except asyncio.TimeoutError as error:
break
print(
f"TimeoutError при выполнении reader.readuntil('{line}')\n"
f"Последний вывод:"
)
print(output)
Функция read_until сначала пытается выполнить reader.readuntil
с указанным
разделителем, при этом asyncio.wait_for
ждет выполнения reader.readuntil
только указанный timeout. Если разделитель line не найден, функция read_until
пытается считать доступный вывод с помощью метода reader.read
.
Теперь функция send_show выглядит так:
async def send_show(host, username, password, enable_password, command):
print(f"Подключение к {host}")
ssh = await asyncssh.connect(
host=host,
username=username,
password=password,
encryption_algs="+aes128-cbc,aes256-cbc",
)
writer, reader, stderr = await ssh.open_session(
term_type="Dumb", term_size=(200, 24)
)
await read_until(reader, ">")
writer.write("enable\n")
await read_until(reader, "Password")
writer.write(f"{enable_password}\n")
await read_until(reader, [">", "#"])
writer.write("terminal length 0\n")
await read_until(reader, "#")
print(f"Отправка команды {command} на {host}")
writer.write(f"{command}\n")
output = await read_until(reader, "#")
ssh.close()
return output
И если указанная строка не была найдена в выводе, функция read_until выведет такую информацию на stdout:
TimeoutError при выполнении reader.readuntil('>')
Последний вывод:
sh ip int br
Interface IP-Address OK? Method Status Protocol
Ethernet0/0 192.168.100.3 YES NVRAM up up
Ethernet0/1 10.100.23.3 YES NVRAM up up
Ethernet0/2 unassigned YES NVRAM administratively down down
Ethernet0/3 unassigned YES NVRAM administratively down down
Loopback9 unassigned YES unset up up
R3#
Соответственно будет видно при выполнении какой команды возникло исключение
asyncio.TimeoutError
и какой вывод был получен с устройства.