Step 1 sets up the dependent standard library modules. In this case, we use only the well-trodden modules included with the standard Python distribution. The sys module provides access to the command-line environment. The subprocess module provides a flexible way of managing child processes. ElementTree is the Python built-in XML parsing environment.
In step 2, we create a Python new-style class with a constructor and a destructor. The constructor invokes the subprocess module in order to manage a child process consisting of an SSH client. We use the typical options of SSH to influence its behavior:
Option
|
Description |
-q |
Quiet mode. Typically omits message-of-the-day banners, which are not helpful for machine reading.
|
-i JUNOS_auto_id_rsa |
Specify the private SSH key file.
|
-p 830 |
Establish TCP port 830 as the transport endpoint.
|
-s |
Invoke the SSH subsytem specified (netconf).
|
The destructor attempts to clean up by closing the standard input stream to the SSH client, which will usually result in the SSH client disconnecting from the remote endpoint.
In step 3, we define a method to read data from the SSH client. The data is read line-by-line until we see the special NETCONF delimiter token. When we see that, we know a message has been completed and it is passed to the ElementTree routines for XML decomposition as a Python object.
In step 4, we define the complimenting output method — a function to write a command RPC. The method simply wraps the input parameter — which is the command line to be executed — in the necessary XML decoration in order to invoke the command RPC.
Step 5 is about putting it all together. We read the command-line arguments to determine the hostname and the command to use. Since most commands consist of multiple words, the user is expected to quote the command. For example:
unix$ ./netconf.py 10.0.201.201 "show route summary"
We call the method to read data from the SSH stream in order to eat the hello message - we've no real need to understand its contents. Then we output a command RPC for the desired command, and call the read method once more in order to receive the response.
As we handle the response from the command RPC, we anticipate receiving one of three types of tag, as shown in the following table:
Tag
|
Description
|
<output>
|
Normal output from a show command or otherwise
|
<configuration-output>
|
Output from the show configuration command
|
<error-message>
|
An error message when something goes wrong
|
Note that the response.find() calls in step 5 make use of the so-called fully qualified XML tag name. The braces denote an XML namespace identifier. Namespaces allow the construction of XML documents comprising of multiple tag dictionaries from multiple sources without collision. They are a flexible tool, but they can make for wordy and verbose text.
Finally, in step 6, we print what we've discovered for the user's attention.