fortinet (Press 'a' to accept) OSError: Socket is closed
when connecting to a fortinet device and it has banner to accept sending RETURN char before sending 'a' to accept the disclaimer cause the device to close the connection, when I disabled force_data in _try_session_preparation this solved the issue
https://github.com/ktbyers/netmiko/blob/8f30aa98c03ab41eebee441017bd947b7cd415fa/netmiko/base_connection.py#L902
@ktbyers is this applied for every connection?
def _open(self) -> None:
"""Decouple connection creation from __init__ for mocking."""
self._modify_connection_params()
self.establish_connection()
self._try_session_preparation()
If so, we could potentially move the data and if "to accept" in data from the "session_preparation" method to the "_try_sessions_preparation" method
def session_preparation(self) -> None:
"""Prepare the session after the connection has been established."""
data = self._test_channel_read(pattern=f"to accept|{self.prompt_pattern}")
# If "set post-login-banner enable" is set it will require you to press 'a'
# to accept the banner before you login. This will accept if it occurs
if "to accept" in data:
self.write_channel("a\r")
self._test_channel_read(pattern=r"[#$]")
self.set_base_prompt()
self.disable_paging()
self.clear_buffer()
So end results would be this:
def _try_session_preparation(self, force_data: bool = True) -> None:
"""
In case of an exception happening during `session_preparation()` Netmiko should
gracefully clean-up after itself. This might be challenging for library users
to do since they do not have a reference to the object. This is possibly related
to threads used in Paramiko.
"""
try:
# Netmiko needs there to be data for session_preparation to work.
if force_data:
data = self._test_channel_read(pattern=f"to accept|{self.prompt_pattern}")
# If "set post-login-banner enable" is set it will require you to press 'a'
# to accept the banner before you login. This will accept if it occurs
if "to accept" in data:
self.write_channel("a\r")
self._test_channel_read(pattern=r"[#$]")
self.write_channel(self.RETURN)
time.sleep(0.1)
self.session_preparation()
except Exception:
self.disconnect()
raise
def session_preparation(self) -> None:
"""Prepare the session after the connection has been established."""
self.set_base_prompt()
self.disable_paging()
self.clear_buffer()
What do you think?
Yes, _open() should always be called one way or another.
How about the following (both of these would be inside the fortinet driver).
def session_preparation(self) -> None:
"""Prepare the session after the connection has been established."""
data = self._test_channel_read(pattern=f"to accept|{self.prompt_pattern}")
# If "set post-login-banner enable" is set it will require you to press 'a'
# to accept the banner before you login. This will accept if it occurs
if "to accept" in data:
self.write_channel("a\r")
self._test_channel_read(pattern=r"[#$]")
self.set_base_prompt()
self.disable_paging()
self.clear_buffer()
def _try_session_preparation(self, force_data: bool = False) -> None:
super._try_session_preparation(force_data=force_data)
This is similar to what you proposed, but just keeps relevant code in the method where I would more typically expect to find them (from a Netmiko perspective).
I think the force_data=False is okay for the Fortinet driver as it is a pretty edge case where it can come up (and there are probably other workarounds you could do--if that edge case arises).
Hi, All have the same issues when I try to log in Fortinet device I would like to know which files I need to update the above code. is this code already published to master? Can you share the link, please? Thanks
@sureshwin006
This file:
https://github.com/ktbyers/netmiko/blob/develop/netmiko/fortinet/fortinet_ssh.py
You can probably just make this change here:
def _try_session_preparation(self, force_data: bool = False) -> None:
super._try_session_preparation(force_data=force_data)
Nope, there are no updates that have been published to the develop (for this).
Thanks for the quick reply, still i have no luck; maybe am I missing something here
here is my sample code to connect a device
from netmiko import ConnectHandler import time
devices = []
ips = ["192.168.26.200"]
for ip in ips:
fortinet = {
'device_type' :'fortinet',
'host':ip,
'username':'admin',
'password':'admin',
'port' : 22,
}
devices.append(fortinet)
for device in devices:
print("Connecting to Device...")
net_connect = ConnectHandler(**device)
time.sleep(1)
banner = net_connect.find_prompt()
print(banner)
print(f'Device is Connected: {ip}')
============================================ Connecting to Device... Traceback (most recent call last):
File "/usr/lib/python3/dist-packages/spyder_kernels/py3compat.py", line 356, in compat_exec exec(code, globals, locals)
File "/home/kali/untitled0.py", line 28, in
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/ssh_dispatcher.py", line 365, in ConnectHandler return ConnectionClass(*args, **kwargs)
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/base_connection.py", line 439, in init self._open()
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/base_connection.py", line 445, in _open self._try_session_preparation()
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/base_connection.py", line 904, in _try_session_preparation self.session_preparation()
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/fortinet/fortinet_ssh.py", line 31, in session_preparation self.write_channel("a\r")
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/base_connection.py", line 97, in wrapper_decorator return_val = func(self, *args, **kwargs)
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/base_connection.py", line 111, in wrapper_decorator func(self, out_data)
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/base_connection.py", line 537, in write_channel self.channel.write_channel(out_data)
File "/home/kali/.local/lib/python3.10/site-packages/netmiko/channel.py", line 74, in write_channel self.remote_conn.sendall(write_bytes(out_data, encoding=self.encoding))
File "/usr/lib/python3/dist-packages/paramiko/channel.py", line 846, in sendall sent = self.send(s)
File "/usr/lib/python3/dist-packages/paramiko/channel.py", line 801, in send return self._send(s, m)
File "/usr/lib/python3/dist-packages/paramiko/channel.py", line 1198, in _send raise socket.error("Socket is closed")
OSError: Socket is closed
@Amr-Es Did you make this change:
def _try_session_preparation(self, force_data: bool = False) -> None:
super._try_session_preparation(force_data=force_data)
In your file named?
/home/kali/.local/lib/python3.10/site-packages/netmiko/fortinet/fortinet_ssh.py
It doesn't look like it from your stack trace?
@ktbyers, yes this is similar to what i did, I just didn't do it in original code
from netmiko.fortinet import FortinetSSH as ForitnetSSHBase
class FortinetSSH(ForitnetSSHBase):
def _try_session_preparation(self, force_data: bool = False):
super()._try_session_preparation(force_data=force_data)
@ktbyers Sounds good, I've updated my test code. Perhaps @Amr-Es can test it out when we've completed it as I've not come across this specific issue.
@Amr-Es If you do what you did, you can't use ConnectHandler as ConnectHandler would use its internal Fortinet Class and not yours.
Instead you would need to do:
net_connect = FortinetSSH(**device)
This is also consistent with the stack trace i.e. the stack trace should show your force_data=False in the stack trace (and it doesn't).
Regards, Kirk
@ktbyers needed a small correction in the code:
def _try_session_preparation(self, force_data: bool = False) -> None:
super()._try_session_preparation(force_data=force_data)
def session_preparation(self) -> None:
"""Prepare the session after the connection has been established."""
data = self._test_channel_read(pattern=f"to accept|{self.prompt_pattern}")
# If "set post-login-banner enable" is set it will require you to press 'a'
# to accept the banner before you login. This will accept if it occurs
if "to accept" in data:
self.write_channel("a\r")
self._test_channel_read(pattern=r"[#$]")
self.set_base_prompt()
self.disable_paging()
@Gatorjosh14 Yeah, I definitely could have messed something up there...as I did it pretty quickly.
@ktbyers, Yes right, I'm not using ConnectHandler. I don't understand what stack trace that should show force_data=False? below is the exception with debug logs starting from \n sent when force_data=True
`DEBUG:paramiko.transport:[chan 0] Sesch channel 0 request ok DEBUG:netmiko:write_channel: b'\n' DEBUG:netmiko:read_channel: (Press 'a' to accept):
DEBUG:paramiko.transport:[chan 0] EOF received (0)
DEBUG:netmiko:Pattern found: (to accept|[#$])
(Press 'a' to accept
DEBUG:paramiko.transport:[chan 0] EOF sent (0)
DEBUG:paramiko.transport:Dropping user packet because connection is dead.
Traceback (most recent call last):
File "test.py", line 8, in
@Amr-Es Your code shows this (from the Stack Trace)
File "test.py", line 8, in
conn = ConnectHandler(**data)
File "\Python310\lib\site-packages\netmiko\ssh_dispatcher.py", line 365, in ConnectHandler
return ConnectionClass(*args, **kwargs)
So this code that you are calling is still using ConnectHandler?
@ktbyers Yes, the code I'm calling is just a test to show the error when using ConnectHandler
@Amr-Es Okay, and what error do you get when you execute your modified code (that is not using ConnectHandler)?
@ktbyers my modified code produce no error, it is the fix I made for this issue "force_data=True send RETURN char before sending 'a' to accept the disclaimer which cause the connection to close", when i set force_data=False this fix the issue as no RETURN char is being sent
@Amr-Es
Okay...yeah, I think the fix I proposed earlier should work then.
Sorry about that, I conflated responses from you and @sureshwin006 (i.e. where he was indicating the proposed fix would not work).
Anyways we will implement the proposed fix and then probably circle back and see if one of you can test it.
Hi, @ktbyers Thanks for the quick reply. I have modified the code but it seems I got a lot of "Inconsistent use of tabs and spaces in indentation", issues
could you please send me to complete the code by email if possible,
Thanks
Updated Fortinet Driver is located here.
This should fix the original issue:
https://github.com/ktbyers/netmiko/pull/3091