Book Image

Python Network Programming Techniques

By : Marcel Neidinger
Book Image

Python Network Programming Techniques

By: Marcel Neidinger

Overview of this book

Network automation offers a powerful new way of changing your infrastructure network. Gone are the days of manually logging on to different devices to type the same configuration commands over and over again. With this book, you'll find out how you can automate your network infrastructure using Python. You'll get started on your network automation journey with a hands-on introduction to the network programming basics to complement your infrastructure knowledge. You'll learn how to tackle different aspects of network automation using Python programming and a variety of open source libraries. In the book, you'll learn everything from templating, testing, and deploying your configuration on a device-by-device basis to using high-level REST APIs to manage your cloud-based infrastructure. Finally, you'll see how to automate network security with Cisco’s Firepower APIs. By the end of this Python network programming book, you'll have not only gained a holistic overview of the different methods to automate the configuration and maintenance of network devices, but also learned how to automate simple to complex networking tasks and overcome common network programming challenges.
Table of Contents (14 chapters)

Initiating an SSH session with Paramiko

The basis of connecting to a device via SSH with Python and Paramiko is the SSHClient object of the library. We will use this object to create an initial connection to the SSH server and later we will use the functions of this object to execute commands on the device.

In this recipe, you will see how to programmatically open an SSH connection.

Getting ready

Open your code editor and start by creating a file called initiating.py. Next, navigate your terminal to the same directory that you just created the initiating.py file in.

How to do it...

Let's start by importing the Paramiko library and creating a client object. We will also specify the host, username, and password in variables and then initiate a connection to the specified host:

  1. Import the Paramiko library:
    from paramiko.client import SSHClient
  2. Specify the host, username, and password. You can name these variables however you like. In the Python community, it has become standard to uppercase these global variables. The three variables SSH_USER, SSH_PASSWORD, and SSH_HOST are variables of type string and we thus use double quotes to mark them. The SSH_PORT variable is an integer and thus does not use double quotes:
    SSH_USER = "<Insert your ssh user here>"
    SSH_PASSWORD = "<Insert your ssh password here>"
    SSH_HOST = "<Insert the IP/host of your device/server here>"
    SSH_PORT = 22 # Change this if your SSH port is different
  3. Create an SSHClient object, which we just imported from Paramiko:
    client = SSHClient()
  4. While we have created our client object, we have not yet connected to the device. We will use the connect method of the client object to do so. Before actually connecting, we will need to make sure that our client knows the host keys:
    client.load_system_host_keys()
    try:
        client.connect(SSH_HOST, port=SSH_PORT,
                                 username=SSH_USER,
                                 password=SSH_PASSWORD,
                                 look_for_keys=False)
        print("Connected successfully!")
    except Exception:
        print("Failed to establish connection.")
  5. Finally, we have created our connection. It is a good habit to close connections after we are done using them. To do so, we can use the close() function of our client:
    finally:
        client.close()
  6. To run this script, go to your terminal and execute it with the following:
    python3 initiating.py

How it works...

In this example, we first imported the Paramiko library's SSHClient class. Next, we set up our connection details. While you could also provide these details directly when calling the connect method on the client object, it is good practice to put them into variables. This means that if you are creating multiple client objects at different points of your script, you don't have to change the username/password and host in each of these calls but just once in the variables. Be careful when submitting these scripts to your colleagues or uploading them to code hosting services though, as they do contain your login details. You can have a look at the There's more section of this recipe to see how you can either prompt for this information interactively or get it from your environment.

Before connecting to the device, using the connect method, we load the host keys. SSH uses host keys and the fingerprints of an SSH server to make sure that the IP you are connecting to is the server you connected to before. When connecting to a brand-new device, you'll have to accept this new host key. While we can keep a separate set of host keys for our Paramiko client, it is usually best to just use the host keys that the user executing the script has. By doing this, you can connect to every device you have previously connected to from your command line, also from your Paramiko scripts.

To do this loading, we are using the load_system_host_keys() function. This function searches the default known hosts file used by OpenSSH and copies them over into Paramiko. See the following section for an example of how to make Paramiko accept new keys by default.

With our host keys configured, we can now actually connect to the device that we have specified. To do so, we are using a try-catch block. This is a Python construct that allows us to catch exceptions, errors that can be raised by any part of the code we are using, and handle them. Python will attempt to execute the instructions in the try block. If any part of that code, in our example the connect() method, errors out and raises an exception, Python will jump into the except block and execute the instructions within that block. In our example, we are only printing out a message that our connection was unsuccessful. The third block, our finally block, will be executed both when the connection has been successful (our except block was not executed) as well as after a failed connection (our except block was executed). This allows us to clean up after both a successful and unsuccessful connection and avoids dangling SSH connections.

There's more...

In this example, we relied on the user having already logged into the device from their command line in order for the host to be known. If we use the preceding code to connect to a device that was not previously known, the code will fail with an exception.

The way Paramiko handles unknown host keys can be specified using a policy. One of these policies, AutoAddPolicy, allows us to just add unknown host keys to our scripts set of host keys:

from paramiko.client import SSHClient, AutoAddPolicy
SSH_USER = "<Insert your ssh user here>"
SSH_PASSWORD = "<Insert your ssh password here>"
SSH_HOST = "<Insert the IP/host of your device/server here>"
SSH_PORT = 22 # Change this if your SSH port is different
client = SSHClient()
client.set_missing_host_key_policy(AutoAddPolicy())
client.connect(SSH_HOST, port=SSH_PORT,
                         username=SSH_USER,
                         password=SSH_PASSWORD)

The preceding code will automatically add these host keys. Be aware that this might be a potential security risk since you are not verifying that the host you are connecting to is the one you connected to last time.

In this example, we passed connection details such as username, hostname, and password directly as a variable in the script. While this is great for testing, you might want to have your script prompt you for a variable upon execution. For non-secret variables such as the username, host, and port, we can use the built-in input() function, but for passwords, it's better to use a dedicated password prompt that hides what you have typed so that someone looking over your console history can't retrieve your password. For this purpose, Python has the built-in getpass module.

Follow these steps to retrieve the configuration variables necessary, not as static information in the script but rather interactively from the user using a combination of input and the getpass module:

import getpass
SSH_PASSWORD = getpass.getpass(prompt='Password: ', stream=None)
SSH_USER = input("Username: ")
SSH_HOST = input("Host: ")
SSH_PORT = int(input("Port: "))