Paramiko, Netmiko and Fabric

Paramiko

Paramiko is a Python (2.7, 3.4+) implementation of the SSHv2 protocol, providing both client and server functionality. While it leverages a Python C extension for low level cryptography (Cryptography), Paramiko itself is a pure Python interface around SSH networking concepts.

Install Paramiko

pip install paramiko

Paramiko example

#!/usr/bin/env python
import paramiko
import sys

if len(sys.argv) < 3:
    print("args missing")
    sys.exit(1)

hostname = sys.argv[1]
command = sys.argv[2]

username = "ubuntu"
port = 2222

try:
    client = paramiko.SSHClient()
    client.load_system_host_keys()
    client.set_missing_host_key_policy(paramiko.WarningPolicy)
    client.connect(hostname, port=port, username=username, key_filename='/Users/jasiplum/Development/Projects/Tieto/Python/naucse/.vagrant/machines/default/virtualbox/private_key')
    stdin, stdout, stderr = client.exec_command(command)
    print(stdout.read())
except Exception as e:
    print("Error happened: %s" % (e,))

You can specify SSH private key using option connect() method parameter key_filename=.

Netmiko

Multi-vendor library to simplify Paramiko SSH connections to network devices.

Currently supported:

Regularly tested

Arista vEOS
Cisco ASA
Cisco IOS
Cisco IOS-XE
Cisco IOS-XR
Cisco NX-OS
Cisco SG300
HP Comware7
HP ProCurve
Juniper Junos
Linux

Limited testing

Alcatel AOS6/AOS8
Apresia Systems AEOS
Calix B6
Cisco WLC
Dell OS9 (Force10)
Dell OS10
Dell PowerConnect
Extreme ERS (Avaya)
Extreme VSP (Avaya)
Extreme VDX (Brocade)
Extreme MLX/NetIron (Brocade/Foundry)
Huawei
IP Infusion OcNOS
Mellanox
NetApp cDOT
Palo Alto PAN-OS
Pluribus
Ruckus ICX/FastIron
Ubiquiti EdgeSwitch
Vyatta VyOS

Experimental

A10
Accedian
Aruba
Ciena SAOS
Citrix Netscaler
Cisco Telepresence
Check Point GAiA
Coriant
Dell OS6
Dell EMC Isilon
Eltex
Enterasys
Extreme EXOS
Extreme Wing
Extreme SLX (Brocade)
F5 LTM
Fortinet
MRV Communications OptiSwitch
Nokia/Alcatel SR-OS
QuantaMesh
Rad ETX

Netmiko installation

pip install netmiko

Netmiko example

A simple SSH session to a Cisco router that executes the 'show ip int brief' command:

>>> from netmiko import ConnectHandler

>>> cisco_881 = {
...   'device_type': 'cisco_ios',
...   'ip': '10.10.10.227',
...   'username': 'pyclass',
...   'password': 'password',
... }

>>> net_connect = ConnectHandler(**cisco_881)
SSH connection established to 10.10.10.227:22
Interactive SSH session established

Alternatively, I could just call the ConnectHandler function directly and not use a dictionary (as follows):

>>> net_connect = ConnectHandler(device_type='cisco_ios', ip='10.10.10.227', username='pyclass', password='password')

Now at this point we have an SSH connection. I can verify this by executing the .find_prompt() method

>>> net_connect.find_prompt()
u'pynet-rtr1#'

Use the .send_command() method to send the 'show ip int brief' command:

>>> output = net_connect.send_command("show ip int brief")
>>> print(output)
Interface                  IP-Address      OK? Method Status                Protocol
FastEthernet0              unassigned      YES unset  down                  down    
FastEthernet1              unassigned      YES unset  down                  down    
FastEthernet2              unassigned      YES unset  down                  down    
FastEthernet3              unassigned      YES unset  down                  down    
FastEthernet4              10.220.88.20    YES NVRAM  up                    up      
Vlan1                      unassigned      YES unset  down                  down

Fabric

Fabric is a high level Python (2.7, 3.4+) library designed to execute shell commands remotely over SSH.

Fabric installation

pip install Fabric

Fabric example

from fabric.api import *
import fabric.contrib.project as project
import os
import shutil
import sys
import SocketServer

from pelican.server import ComplexHTTPRequestHandler

# Local path configuration (can be absolute or relative to fabfile)
env.deploy_path = 'output'
DEPLOY_PATH = env.deploy_path

# Remote server configuration
production = 'root@localhost:22'
dest_path = '/var/www'

# Rackspace Cloud Files configuration settings
env.cloudfiles_username = 'my_rackspace_username'
env.cloudfiles_api_key = 'my_rackspace_api_key'
env.cloudfiles_container = 'my_cloudfiles_container'

# Github Pages configuration
env.github_pages_branch = "gh-pages"

# Port for `serve`
PORT = 8000

def clean():
    """Remove generated files"""
    if os.path.isdir(DEPLOY_PATH):
        shutil.rmtree(DEPLOY_PATH)
        os.makedirs(DEPLOY_PATH)

def build():
    """Build local version of site"""
    local('pelican -s pelicanconf.py')

def rebuild():
    """`build` with the delete switch"""
    local('pelican -d -s pelicanconf.py')

def regenerate():
    """Automatically regenerate site upon file modification"""
    local('pelican -r -s pelicanconf.py')

def serve():
    """Serve site at http://localhost:8000/"""
    os.chdir(env.deploy_path)

    class AddressReuseTCPServer(SocketServer.TCPServer):
        allow_reuse_address = True

    server = AddressReuseTCPServer(('', PORT), ComplexHTTPRequestHandler)

    sys.stderr.write('Serving on port {0} ...\n'.format(PORT))
    server.serve_forever()

def reserve():
    """`build`, then `serve`"""
    build()
    serve()

def preview():
    """Build production version of site"""
    local('pelican -s publishconf.py')

def cf_upload():
    """Publish to Rackspace Cloud Files"""
    rebuild()
    with lcd(DEPLOY_PATH):
        local('swift -v -A https://auth.api.rackspacecloud.com/v1.0 '
              '-U {cloudfiles_username} '
              '-K {cloudfiles_api_key} '
              'upload -c {cloudfiles_container} .'.format(**env))

@hosts(production)
def publish():
    """Publish to production via rsync"""
    local('pelican -s publishconf.py')
    project.rsync_project(
        remote_dir=dest_path,
        exclude=".DS_Store",
        local_dir=DEPLOY_PATH.rstrip('/') + '/',
        delete=True,
        extra_opts='-c',
    )

def gh_pages():
    """Publish to GitHub Pages"""
    rebuild()
    local("ghp-import -b {github_pages_branch} {deploy_path} -p".format(**env))
{
  "data": {
    "sessionMaterial": {
      "id": "session-material:2019/tieto-ostrava-jaro:regular-expressions:0",
      "title": "Paramiko, Netmiko and Fabric",
      "html": "\n          \n    \n\n    <h2>Paramiko, Netmiko and Fabric</h2>\n<h3>Paramiko</h3>\n<p>Paramiko is a Python (2.7, 3.4+) implementation of the SSHv2 protocol, providing both client and server functionality. While it leverages a Python C extension for low level cryptography (Cryptography), Paramiko itself is a pure Python interface around SSH networking concepts.</p>\n<h4>Install Paramiko</h4>\n<div class=\"highlight\"><pre><code>pip install paramiko</code></pre></div><h4>Paramiko example</h4>\n<div class=\"highlight\"><pre><code>#!/usr/bin/env python\nimport paramiko\nimport sys\n\nif len(sys.argv) &lt; 3:\n    print(&quot;args missing&quot;)\n    sys.exit(1)\n\nhostname = sys.argv[1]\ncommand = sys.argv[2]\n\nusername = &quot;ubuntu&quot;\nport = 2222\n\ntry:\n    client = paramiko.SSHClient()\n    client.load_system_host_keys()\n    client.set_missing_host_key_policy(paramiko.WarningPolicy)\n    client.connect(hostname, port=port, username=username, key_filename=&apos;/Users/jasiplum/Development/Projects/Tieto/Python/naucse/.vagrant/machines/default/virtualbox/private_key&apos;)\n    stdin, stdout, stderr = client.exec_command(command)\n    print(stdout.read())\nexcept Exception as e:\n    print(&quot;Error happened: %s&quot; % (e,))</code></pre></div><p>You can specify SSH private key using option connect() method parameter <strong>key_filename=</strong>.</p>\n<h3>Netmiko</h3>\n<p>Multi-vendor library to simplify Paramiko SSH connections to network devices.</p>\n<p>Currently supported:</p>\n<p><strong>Regularly tested</strong></p>\n<div class=\"highlight\"><pre><code>Arista vEOS\nCisco ASA\nCisco IOS\nCisco IOS-XE\nCisco IOS-XR\nCisco NX-OS\nCisco SG300\nHP Comware7\nHP ProCurve\nJuniper Junos\nLinux</code></pre></div><p><strong>Limited testing</strong></p>\n<div class=\"highlight\"><pre><code>Alcatel AOS6/AOS8\nApresia Systems AEOS\nCalix B6\nCisco WLC\nDell OS9 (Force10)\nDell OS10\nDell PowerConnect\nExtreme ERS (Avaya)\nExtreme VSP (Avaya)\nExtreme VDX (Brocade)\nExtreme MLX/NetIron (Brocade/Foundry)\nHuawei\nIP Infusion OcNOS\nMellanox\nNetApp cDOT\nPalo Alto PAN-OS\nPluribus\nRuckus ICX/FastIron\nUbiquiti EdgeSwitch\nVyatta VyOS</code></pre></div><p><strong>Experimental</strong></p>\n<div class=\"highlight\"><pre><code>A10\nAccedian\nAruba\nCiena SAOS\nCitrix Netscaler\nCisco Telepresence\nCheck Point GAiA\nCoriant\nDell OS6\nDell EMC Isilon\nEltex\nEnterasys\nExtreme EXOS\nExtreme Wing\nExtreme SLX (Brocade)\nF5 LTM\nFortinet\nMRV Communications OptiSwitch\nNokia/Alcatel SR-OS\nQuantaMesh\nRad ETX</code></pre></div><h4>Netmiko installation</h4>\n<div class=\"highlight\"><pre><code>pip install netmiko</code></pre></div><h4>Netmiko example</h4>\n<p>A simple SSH session to a Cisco router that executes the &apos;show ip int brief&apos; command:</p>\n<div class=\"highlight\"><pre><code>&gt;&gt;&gt; from netmiko import ConnectHandler\n\n&gt;&gt;&gt; cisco_881 = {\n...   &apos;device_type&apos;: &apos;cisco_ios&apos;,\n...   &apos;ip&apos;: &apos;10.10.10.227&apos;,\n...   &apos;username&apos;: &apos;pyclass&apos;,\n...   &apos;password&apos;: &apos;password&apos;,\n... }\n\n&gt;&gt;&gt; net_connect = ConnectHandler(**cisco_881)\nSSH connection established to 10.10.10.227:22\nInteractive SSH session established</code></pre></div><p>Alternatively, I could just call the ConnectHandler function directly and not use a dictionary (as follows):</p>\n<div class=\"highlight\"><pre><code>&gt;&gt;&gt; net_connect = ConnectHandler(device_type=&apos;cisco_ios&apos;, ip=&apos;10.10.10.227&apos;, username=&apos;pyclass&apos;, password=&apos;password&apos;)</code></pre></div><p>Now at this point we have an SSH connection. I can verify this by executing the .find_prompt() method</p>\n<div class=\"highlight\"><pre><code>&gt;&gt;&gt; net_connect.find_prompt()\nu&apos;pynet-rtr1#&apos;</code></pre></div><p>Use the .send_command() method to send the &apos;show ip int brief&apos; command:</p>\n<div class=\"highlight\"><pre><code>&gt;&gt;&gt; output = net_connect.send_command(&quot;show ip int brief&quot;)\n&gt;&gt;&gt; print(output)\nInterface                  IP-Address      OK? Method Status                Protocol\nFastEthernet0              unassigned      YES unset  down                  down    \nFastEthernet1              unassigned      YES unset  down                  down    \nFastEthernet2              unassigned      YES unset  down                  down    \nFastEthernet3              unassigned      YES unset  down                  down    \nFastEthernet4              10.220.88.20    YES NVRAM  up                    up      \nVlan1                      unassigned      YES unset  down                  down</code></pre></div><h3>Fabric</h3>\n<p>Fabric is a high level Python (2.7, 3.4+) library designed to execute shell commands remotely over SSH.</p>\n<h4>Fabric installation</h4>\n<div class=\"highlight\"><pre><code>pip install Fabric</code></pre></div><h4>Fabric example</h4>\n<div class=\"highlight\"><pre><code>from fabric.api import *\nimport fabric.contrib.project as project\nimport os\nimport shutil\nimport sys\nimport SocketServer\n\nfrom pelican.server import ComplexHTTPRequestHandler\n\n# Local path configuration (can be absolute or relative to fabfile)\nenv.deploy_path = &apos;output&apos;\nDEPLOY_PATH = env.deploy_path\n\n# Remote server configuration\nproduction = &apos;root@localhost:22&apos;\ndest_path = &apos;/var/www&apos;\n\n# Rackspace Cloud Files configuration settings\nenv.cloudfiles_username = &apos;my_rackspace_username&apos;\nenv.cloudfiles_api_key = &apos;my_rackspace_api_key&apos;\nenv.cloudfiles_container = &apos;my_cloudfiles_container&apos;\n\n# Github Pages configuration\nenv.github_pages_branch = &quot;gh-pages&quot;\n\n# Port for `serve`\nPORT = 8000\n\ndef clean():\n    &quot;&quot;&quot;Remove generated files&quot;&quot;&quot;\n    if os.path.isdir(DEPLOY_PATH):\n        shutil.rmtree(DEPLOY_PATH)\n        os.makedirs(DEPLOY_PATH)\n\ndef build():\n    &quot;&quot;&quot;Build local version of site&quot;&quot;&quot;\n    local(&apos;pelican -s pelicanconf.py&apos;)\n\ndef rebuild():\n    &quot;&quot;&quot;`build` with the delete switch&quot;&quot;&quot;\n    local(&apos;pelican -d -s pelicanconf.py&apos;)\n\ndef regenerate():\n    &quot;&quot;&quot;Automatically regenerate site upon file modification&quot;&quot;&quot;\n    local(&apos;pelican -r -s pelicanconf.py&apos;)\n\ndef serve():\n    &quot;&quot;&quot;Serve site at http://localhost:8000/&quot;&quot;&quot;\n    os.chdir(env.deploy_path)\n\n    class AddressReuseTCPServer(SocketServer.TCPServer):\n        allow_reuse_address = True\n\n    server = AddressReuseTCPServer((&apos;&apos;, PORT), ComplexHTTPRequestHandler)\n\n    sys.stderr.write(&apos;Serving on port {0} ...\\n&apos;.format(PORT))\n    server.serve_forever()\n\ndef reserve():\n    &quot;&quot;&quot;`build`, then `serve`&quot;&quot;&quot;\n    build()\n    serve()\n\ndef preview():\n    &quot;&quot;&quot;Build production version of site&quot;&quot;&quot;\n    local(&apos;pelican -s publishconf.py&apos;)\n\ndef cf_upload():\n    &quot;&quot;&quot;Publish to Rackspace Cloud Files&quot;&quot;&quot;\n    rebuild()\n    with lcd(DEPLOY_PATH):\n        local(&apos;swift -v -A https://auth.api.rackspacecloud.com/v1.0 &apos;\n              &apos;-U {cloudfiles_username} &apos;\n              &apos;-K {cloudfiles_api_key} &apos;\n              &apos;upload -c {cloudfiles_container} .&apos;.format(**env))\n\n@hosts(production)\ndef publish():\n    &quot;&quot;&quot;Publish to production via rsync&quot;&quot;&quot;\n    local(&apos;pelican -s publishconf.py&apos;)\n    project.rsync_project(\n        remote_dir=dest_path,\n        exclude=&quot;.DS_Store&quot;,\n        local_dir=DEPLOY_PATH.rstrip(&apos;/&apos;) + &apos;/&apos;,\n        delete=True,\n        extra_opts=&apos;-c&apos;,\n    )\n\ndef gh_pages():\n    &quot;&quot;&quot;Publish to GitHub Pages&quot;&quot;&quot;\n    rebuild()\n    local(&quot;ghp-import -b {github_pages_branch} {deploy_path} -p&quot;.format(**env))</code></pre></div>\n\n\n        "
    }
  }
}