Select Page

Adding a little class to our project – about 30 minutes

We actually have a pretty useful little utility at this point. It can keep a list of devices, connect to all of those devices, and save the output to your local disk. That’s a pretty good feature set. You could wrap it up here and feel pretty good about the endeavor. That probably would have good enough, you know, for a lesser person. But you might also walk away wondering: what else could we have done to make this program more broadly useable?

We’re going to write a class to wrap up and encapsulate our SSH host. A class is a representation of something like, say, a router. So a class has attributes, such as its host_name, and methods that are essentially functions that are part of the class itself. We say that we create an instance of a class. Each instance mostly contains its own, separate attributes. So you can have a first instance whose host_name points to one host while a second instance has an entirely different host_name. (It’s also possible for instances of a class to share variables, but we won’t dive into static variables just yet.)

This stuff can be pretty confusing. Expect to feel like you don’t really understand for a little while. We’re going to be taking a giant step here. Checking out the code and tinkering with it will help you to understand what’s going on. With some diligence you’ll get this. Or have a working program with some parts that you don’t quite understand. Yet.

Alright, let’s evolve this program and add in this class idea. First, we are going to write the code for the whole class.

Using the Python keyword class we are able to declare our SSHHost. Each instance of this class is going to contain its own data (host name, user name, and password). The data will be kept separate in each instance.

A class also defines some behavior. The behaviors are wrapped up in methods. In our case, SSHHostdefines methods:

  1. def __init__(self, device_name, user_name, password): This is a constructor method. It is used to set up or save anything that will be needed by this instance of the class.
  2. def connect(self): Knows how to connect to the device_name that was passed in as a parameter to init.
  3. def log_filename_for_command(self, command): Returns an appropriate filename in which we can write the output from device_name for a given command.
  4. def read_config(self): Can read the config from device_name.
  5. def perform_command_and_log_results(self, cmd): Actually executes the command in cmd and saves the output to a file on the local hard disk.
  6. def get_command_results(self): Gets the output form a command after it has been executed on device_name.
  7. def send_command_and_get_response(self, cmd): Sends a command using the previous method and then retrieves the output from the command and returns that output in a string.

We haven’t written our own methods before now. However, we have actually used methods from other classes. For example we have defined a connect() method in our class. Though we didn’t talk about it in detail we have actually made use of SQLite’s connect() method with this kind of code: conn = sqlite3.connect(database_name). Using our class definition we can do something similar like:

We’re actually going to do something a little different so that we can wrap things up pretty neatly.

To pretty things up, we’ll change the body of read_from_device() to use our new class.

This method is now easier to understand because we have factored out the code to do the work and put it into our SSHHost class. In this evolution of the project we now have two lines of code that appear to handle all of the details of interacting with our host devices. Let’s look at these in a little more detail.

We start using the class by creating and saving an instance of the class passing in our variables. Python automatically calls the class’s constructor method which we have defined as:

You can add a lot of functionality in a constructor, but we only need to save our variables. We are assigning the parameter variables, like device_name, to our instance variables, like self.device_name. By specifying the self. we are making it clear that we want the values to be saved as part of each instance (i.e., each host). Once we have an instance created and saved in our host variable then we can execute its methods, like read_config().

read_config() encapsulates the functionality of connecting, downloading, and saving config all into the one method. It starts out by attempting to self.connect() to the host. connect() is smart enough to be able to use the instance variables like self.device_name in order to connect. The method returns True if it’s successful. We can check that return result to know whether we can continue our task by using the self.ssh instance variable, which is our connection to the host. Once successfully connected, the method runs through the same basic steps we want any flunkie sidekick to do for us. The primary change to the code is to reference instance variables (like self.ssh and self.device_name). Also you may have noticed that we implemented a self.log_filename_for_command() method that returns the name of the config file to be written on our local hard drive for any given command. Since there are multiple places where we would want to know the filename it makes sense to wrap the logic into one method to keep our code DRY.

Share This