PyCharm - OSError: Socket is closed

Viewed 2287

Any assistance would be greatly appreciated! How can this be resolved? The issue seems to be with netmiko/paramiko & it's interaction with the script.

Thanks in advance!

Code:

from netmiko import ConnectHandler

with open('Devices.txt') as f:
    file_content = f.read().splitlines()


devices = list()
for item in file_content:
    tmp = item.split(':')  #tmp is a list
    devices.append(tmp)


for device in devices:
    net_device = {
        'device_type': device[0],
        'ip': device[1],
        'username': device[2],
        'password': device[3],
        'port': 22,
        'secret': device[3],  # this is the enable password
        'verbose': True
    }

    connection = ConnectHandler(** net_device)

    if not connection.check_enable_mode():
        connection.enable()


    #connection.config_mode()
    config_file = input('Enter configuration file for device type: ' + device[0] + ' with ip ' + device[1] + ':')
    print('Sending commnands to device ...')
    with open(config_file) as config:
        commands = config.read().splitlines()
    # print(commands)

    output = connection.send_config_set(commands)
    print(output)
    print('Disconecting ....')
    print('#' * 40)


    connection.disconnect()

Above is the script.

Error:

"C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\Scripts\python.exe" "C:/Users/Administrator/PycharmProjects/Python Network Automation/Python Netmiko.py"

SSH connection established to 192.168.181.150:22
Interactive SSH session established
Enter configuration file for device type: cisco_ios with ip 192.168.181.150:Static Route Configuration.txt
Sending commnands to device …

Traceback (most recent call last):
  File "C:/Users/Administrator/PycharmProjects/Python Network Automation/Python Netmiko.py", line 37, in <module>
    output = connection.send_config_set(commands)
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\netmiko\base_connection.py", line 1620, in send_config_set
    output += self.exit_config_mode()
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\netmiko\cisco_base_connection.py", line 55, in exit_config_mode
    return super(CiscoBaseConnection, self).exit_config_mode(
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\netmiko\base_connection.py", line 1531, in exit_config_mode
    if self.check_config_mode():
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\netmiko\cisco\cisco_ios.py", line 31, in check_config_mode
    return super(CiscoIosBase, self).check_config_mode(
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\netmiko\cisco_base_connection.py", line 37, in check_config_mode
    return super(CiscoBaseConnection, self).check_config_mode(
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\netmiko\base_connection.py", line 1496, in check_config_mode
    self.write_channel(self.RETURN)
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\netmiko\base_connection.py", line 422, in write_channel
    self._write_channel(out_data)
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\netmiko\base_connection.py", line 384, in _write_channel
    self.remote_conn.sendall(write_bytes(out_data, encoding=self.encoding))
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\paramiko\channel.py", line 846, in sendall
    sent = self.send(s)
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\paramiko\channel.py", line 801, in send
    return self._send(s, m)
  File "C:\Users\Administrator\PycharmProjects\Python Network Automation\venv\lib\site-packages\paramiko\channel.py", line 1198, in _send
    raise socket.error("Socket is closed")
OSError: Socket is closed

Process finished with exit code 1

Above is the error message received.

1 Answers

Maybe the remote device is refusing the connection for some reason. You are connecting mutiple times in a row. However, your code is good but needs some modifications.

I know my is answer is quite late, but it might be useful for others facing the same issue.

  1. I think reading the devices from a CSV file would be much more neat, easier, and more convenient. You don't have to index the variable device[0], device[1], and so on.

The CSV file must look exactly like:

device_type,ip,username,password,secret
cisco_ios,192.168.1.1,cisco,cisco,
cisco_ios,192.168.1.2,cisco,cisco,

You can open it in Excel to have pretty view.

As you can see, the header row contains the approriate keys of Netmiko.

You also don't need to explicitly set ports 22 and 23 for cisco_ios (SSH) and cisco_ios_telnet (Telnet) respectively. You need to set it only if you created a rotary-group and an ACL on the network device to change the default ports.

The only drawback of reading data from a CSV file with csv.DictReader() is that data values are parsed as str. "verbose": True is not an option in this case, because it will be parsed as a string not a Boolean ('verbose': 'True').

You can use csv.reader() instead of csv.DictReader() and manipulate the parsed data first.

  1. Sending configuration from a file is already implemented as a function in Netmiko. Use connection.send_config_from_file("config.txt") instead. It reads the file and does the heavy lifiting for you.

  2. Use a context manager (with statement) and remove the connection.disconnect().

A complete explanation of the above recommendations:

import csv

from netmiko import ConnectHandler

# File containing network devices
with open("Devices.csv") as f:
    csvfile = csv.DictReader(f=f)
    devices = [device for device in csvfile]

# print(devices)  # <--- See output below

for device in devices:
    print(f'Connecting to {device["ip"]}')
    with ConnectHandler(**device) as conn:
        print(f'Connection established to {device["ip"]}:{device["port"]}')

        if not conn.check_enable_mode():
            conn.enable()

        # Find the device prompt.
        # The slicing here ([:-1)] is to remove the `#` sign after hostname
        hostname = conn.find_prompt()[:-1]

        # If the input file is the same for all devices, then move it up before
        # the for loop, else keep it here
        config_file = input(
            f'Enter config file for {hostname} ({device["ip"]}): '
        ).strip()

        print(f"Sending commnands to {hostname}...")
        # Using `send_config_from_file` instead of `send_config_set`
        output = conn.send_config_from_file(config_file)
        # Don't forget to save the new config if you need to
        # output += conn.save_config()  # Uncomment this line to save the new config
    print(f"Disconnected from {hostname}")
    print("#" * 40)
    print(output)
    print("#" * 40)

When devices variable is printed, it looks exactly like:

[{'device_type': 'cisco_ios',
  'ip': '192.168.1.1',
  'password': 'cisco',
  'secret': '',
  'username': 'cisco'},
 {'device_type': 'cisco_ios',
  'ip': '192.168.1.2',
  'password': 'cisco',
  'secret': '',
  'username': 'cisco'}]
Related