The Python Client for eAPI (pyeapi
) is a Python library that simplifies working with Arista eAPI, removing the need to deal with the specifics of its implementation. It's straightforward to configure and use. In this blog post, we'll look at how to install pyeapi
and go through some examples.
If you're familiar with Arista's eAPI, you know that you can browse to the device's IP in a browser, run commands, and get the output directly. You can also achieve the same result using Python, but it typically requires understanding which libraries to use and how to construct the REST API requests.
However, pyeapi
simplifies all of this. You don't need to worry about what's happening behind the scenes. Below is a screenshot of running show vlan
command via the REST API, and in the following examples, we'll see how to get the same output using pyeapi
.
PyeAPI Installation
To install pyeapi
, you can use pip
, which is the standard package manager for Python. It's a good practice to use a virtual environment (venv
) to keep your dependencies isolated and avoid conflicts with other projects. First, create and activate a virtual environment. Once your virtual environment is activated, you can install pyeapi
using the pip command.
# Create a virtual environment named 'venv'
python3 -m venv venv
# Activate the virtual environment
source venv/bin/activate
pip install pyeapi
Before you start using pyeapi
please make sure the device is configured to accept API requests. Here is a sample config that I previously used. You may not need this if your device is already in production but if something doesn't work, use this as a reference.
security pki key generate rsa 4096 key.pem
security pki certificate generate self-signed cert.pem key key.pem validity 3650 parameters common-name access-01
management security
ssl profile MY_PROFILE
tls versions 1.2
certificate cert.pem key key.pem
management api http-commands
protocol https ssl profile MY_PROFILE
no shut
Connecting to a Single Arista Device
To kick things off, let's try to connect to a single Arista device using the script below.
import pyeapi
connection = pyeapi.client.connect(
transport="https",
host="192.168.100.212",
username="admin",
password='admin',
port=443
)
node = pyeapi.client.Node(connection)
response = node.enable(["show vlan"])
print(response)
In this script, we start by importing the pyeapi
library. We then create a connection to the device using the connect()
method, specifying parameters such as transport type, host IP, username, password, and port.
Once the connection is established, we create a Node
object, which allows us to interact with the device. In this case, we use the enable()
method to send the command "show vlan"
and then print the response.
[{'command': 'show vlan', 'result': {'vlans': {'1': {'name': 'default', 'dynamic': False, 'status': 'active', 'interfaces': {}}, '10': {'name': 'finance', 'dynamic': False, 'status': 'active', 'interfaces': {'Ethernet1': {'privatePromoted': False, 'blocked': None}, 'Ethernet2': {'privatePromoted': False, 'blocked': None}, 'Ethernet5': {'privatePromoted': False, 'blocked': None}}}, '20': {'name': 'sales', 'dynamic': False, 'status': 'active', 'interfaces': {'Ethernet1': {'privatePromoted': False, 'blocked': None}, 'Ethernet2': {'privatePromoted': False, 'blocked': None}}}, '30': {'name': 'cctv', 'dynamic': False, 'status': 'active', 'interfaces': {'Ethernet1': {'privatePromoted': False, 'blocked': None}, 'Ethernet2': {'privatePromoted': False, 'blocked': None}, 'Ethernet6': {'privatePromoted': False, 'blocked': None}}}}, 'sourceDetail': ''}, 'encoding': 'json'}]
If you want to make the output more readable, you can use the json.dumps()
method to pretty-print the response. This will format the JSON output in a structured way, making it easier to understand.
import json
print(json.dumps(response, indent=2))
[
{
"command": "show vlan",
"result": {
"vlans": {
"1": {
"name": "default",
"dynamic": false,
"status": "active",
"interfaces": {}
},
"10": {
"name": "finance",
"dynamic": false,
"status": "active",
"interfaces": {
"Ethernet1": {
"privatePromoted": false,
"blocked": null
},
"Ethernet2": {
"privatePromoted": false,
"blocked": null
},
"Ethernet5": {
"privatePromoted": false,
"blocked": null
}
}
},
"20": {
"name": "sales",
"dynamic": false,
"status": "active",
"interfaces": {
"Ethernet1": {
"privatePromoted": false,
"blocked": null
},
"Ethernet2": {
"privatePromoted": false,
"blocked": null
}
}
},
"30": {
"name": "cctv",
"dynamic": false,
"status": "active",
"interfaces": {
"Ethernet1": {
"privatePromoted": false,
"blocked": null
},
"Ethernet2": {
"privatePromoted": false,
"blocked": null
},
"Ethernet6": {
"privatePromoted": false,
"blocked": null
}
}
}
},
"sourceDetail": ""
},
"encoding": "json"
}
]
Getting Raw Output
For whatever reason, you just want to get the raw output, you can set the encoding to text
instead of JSON (default is JSON)
import pyeapi
connection = pyeapi.client.connect(
transport='https',
host='192.168.100.212',
username='admin',
password='admin',
port=443
)
node = pyeapi.client.Node(connection)
response = node.enable('show vlan', encoding='text')
print(response[0]['result']['output'])
VLAN Name Status Ports
----- -------------------------------- --------- -------------------------------
1 default active
10 finance active Et1, Et2, Et5
20 sales active Et1, Et2
30 cctv active Et1, Et2, Et6
98 VLAN0098 active
100 VLAN_100 active
Using the nodes.conf File
You can also use a nodes.conf
file to define your devices, serving as an inventory file. Here's an example nodes.conf
file.
[connection:access-01]
host: 192.168.100.212
transport: https
username: admin
password: admin
In this configuration file, we define a device called access-01
with its connection details such as the host, transport type, username, and password. The script below shows how to use nodes.conf
to connect to the device.
import pyeapi
import json
pyeapi.load_config('nodes.conf')
node = pyeapi.connect_to('access-01')
response = node.enable(["sh vlan"])
print(json.dumps(response, indent=2))
In this script, we first load the nodes.conf
file using pyeapi.load_config()
. Then, we connect to the device using pyeapi.connect_to('access-01')
. The rest of the script is similar to the previous example, sending a command ("show vlan"
) and pretty-printing the output.
Hiding Secrets
In the first example, we hard-coded the password directly into the script, which is not recommended at all, as it poses a security risk. In the updated script below, we use an environment variable instead.
import pyeapi
import json
import os
password = os.environ.get('PWD')
connection = pyeapi.client.connect(
transport="https",
host="192.168.100.212",
username="admin",
password=password,
port=443
)
node = pyeapi.client.Node(connection)
response = node.enable(["show vlan"])
print(json.dumps(response, indent=2))
Here, we retrieve the password from an environment variable using os.environ.get('PWD')
. This keeps sensitive information out of your script and reduces the risk of exposing passwords.
nodes.conf
file yet. If you know of a solution, please let me know in the comments.PyeAPI Modules
We know we can use any show commands and get parsed output, which is great, but pyeapi
also comes with predefined modules for managing configurations and retrieving specific outputs.
Get Interfaces
First, let's look at how to get specific outputs. You can find the available modules here.
import pyeapi
import json
import os
password = os.environ.get('PWD')
connection = pyeapi.client.connect(
transport="https",
host="192.168.100.212",
username="admin",
password=password,
port=443
)
node = pyeapi.client.Node(connection)
interfaces = node.api('interfaces')
print(json.dumps(interfaces.getall(), indent=2))
In this script, we're using the api('interfaces')
method to interact directly with the interfaces on the device. After establishing the connection, node.api('interfaces')
provides an instance of the interfaces API.
The interfaces.getall()
function then fetches details of all interfaces, giving us structured data to work with.
{
"defaults": {
"name": "defaults",
"type": "generic",
"shutdown": false,
"description": null
},
"Ethernet1": {
"name": "Ethernet1",
"type": "ethernet",
"shutdown": false,
"description": "CCTV",
"sflow": true,
"flowcontrol_send": "off",
"flowcontrol_receive": "off"
},
"Ethernet2": {
"name": "Ethernet2",
"type": "ethernet",
"shutdown": false,
"description": null,
"sflow": true,
"flowcontrol_send": "off",
"flowcontrol_receive": "off"
},
"Ethernet5": {
"name": "Ethernet5",
"type": "ethernet",
"shutdown": false,
"description": null,
"sflow": true,
"flowcontrol_send": "off",
"flowcontrol_receive": "off"
},
"Ethernet6": {
"name": "Ethernet6",
"type": "ethernet",
"shutdown": false,
"description": null,
"sflow": true,
"flowcontrol_send": "off",
"flowcontrol_receive": "off"
},
"Management0": {
"name": "Management0",
"type": "generic",
"shutdown": false,
"description": null
}
}
If you want to get information about IP addresses, you can use ipinterfaces
instead. Here I'm targeting a different device that has L3 interfaces.
node = pyeapi.client.Node(connection)
interfaces = node.api('ipinterfaces')
print(json.dumps(interfaces.getall(), indent=2))
{
"defaults": {
"name": "defaults",
"address": null,
"mtu": null
},
"Management0": {
"name": "Management0",
"address": "192.168.100.210/24",
"mtu": null
},
"Vlan10": {
"name": "Vlan10",
"address": "10.125.10.2/24",
"mtu": null
},
"Vlan20": {
"name": "Vlan20",
"address": "10.125.20.2/24",
"mtu": null
},
"Vlan30": {
"name": "Vlan30",
"address": "10.125.30.2/24",
"mtu": null
}
}
Manage VLANs
Now let's look at how to manage VLANs. Just like with interfaces, you can use the module vlans
to manage VLANs. Here is an example for retrieving VLANs.
import pyeapi
import json
import os
password = os.environ.get('PWD')
connection = pyeapi.client.connect(
transport="https",
host="192.168.100.210",
username="admin",
password=password,
port=443)
node = pyeapi.client.Node(connection)
vlans = node.api('vlans')
print(json.dumps(vlans.getall(), indent=2))
In the next script, we are creating a new VLAN with ID 100 and assigning it a name.
import pyeapi
import json
import os
password = os.environ.get('PWD')
connection = pyeapi.client.connect(
transport="https",
host="192.168.100.212",
username="admin",
password=password,
port=443)
node = pyeapi.client.Node(connection)
vlans = node.api('vlans')
output = vlans.configure_vlan(100, ['name VLAN_100'])
print(output)
After establishing the connection, we use node.api('vlans')
to get an instance of the VLANs API. Then, we use vlans.configure_vlan(100, ['name VLAN_100'])
to create VLAN 100 and assign it the name VLAN_100
.
The configure_vlan
method allows us to specify the VLAN ID and configure attributes (like name
) through a list. The output of the operation is then printed for confirmation (prints true
)
SSL/TLS Errors
If you encounter any SSL errors similar to the following, One way to fix it is to create a custom SSL context and pass it during the connection.
pyeapi.eapilib.ConnectionError: Socket error during eAPI connection: [SSL:
SSLV3_ALERT_HANDSHAKE_FAILURE] sslv3 alert handshake failure (_ssl.c:1007)
import ssl
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.minimum_version = ssl.TLSVersion.TLSv1_2
context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
context.set_ciphers('AES256-SHA:DHE-RSA-AES256-SHA:AES128-SHA:DHE-RSA-AES128-SHA')
connection = pyeapi.client.connect(
transport='https',
host='host',
username='admin',
password='admin',
port=443,
context=context
)
Closing Up
I hope you find this post useful. There are plenty of modules available to manage various parts of the configuration. Feel free to try them out, and let me know in the comments if you have any questions.