Arista Command API

The Arista Command API is a simple and complete API that allows you to configure and monitor your Arista switches. Once the API is enabled, the switch accepts commands using the industry standard CLI syntax, and responds with machine readable output and errors serialized in JSON, served over HTTP.

The Command API is easy to use...

   from jsonrpclib import Server

   switch = Server( "https://username:[email protected]/command-api" )
   response = switch.runCmds( 1, ["show version"] )
   print "The switch's system MAC addess is", response[0]["systemMacAddress"]

... and easy to configure!

   bash$ ssh [email protected]
   Password: <passw0rd>
   myswitch> enable
   myswitch# configure terminal
   myswitch(config)# management api http-commands
   myswitch(config-mgmt-api-http-cmds)# no shutdown
   myswitch(config-mgmt-api-http-cmds)# show management api http-commands
   Enabled:            Yes
   HTTPS server:       running, set to use port 443
   HTTP server:        shutdown, set to use port 80
   Local HTTP server:  shutdown, no authentication, set to use port 8080
   Unix Socket server: shutdown, no authentication
   VRFs:               default
   ...

Configuring the Command API Agent

Although disabled by default, it is very simple to get the Command API server running on your switch. From configure mode, enter management api http-commands mode. In this submode, you can turn on or off the server by typing [no] shutdown.

The configuration of the protocol itself in done in HttpServer mode. From configure mode, enter management http-server mode. In this submode, you can switch between accepting HTTP or HTTPS traffic via [no] protocol http[s], and adjust the ports the server should listen on using protocol http[s] port <portNumber>. You can also configure the API to only accept connections from scripts running on the switch itself. To enable this feature, see On-box Usage of Command API

In any CLI mode, you can issue the show management api http-commands command to view the status of the agent, including server information, attached users, last request time and other details.

   myswitch> enable
   myswitch# configure terminal
   myswitch(config)# management api http-commands
   myswitch(config-mgmt-api-http-cmds)# [no] shutdown
   myswitch(config-mgmt-api-http-cmds)# management http-server
   myswitch(config-mgmt-http-server)# [no] protocol https [port <portNumber>]
   myswitch(config-mgmt-http-server)# [no] protocol http [port <portNumber>]
   myswitch(config-mgmt-http-server)# [no] protocol http localhost [port <portNumber>]
   myswitch(config-mgmt-http-server)# [no] protocol unix-socket

Configuring a Certificate

Because clients use HTTP basic authentication to send usernames and passwords to the switch, we recommend using HTTPS so no passwords are sent in the clear over the network. By default a self-signed certificate will be used.

   myswitch> enable
   myswitch# copy scp:<username>@<server>//<serverCertificate>  certificate:eapiServerCert
   myswitch# copy scp:<username>@<server>//<serverKey> sslkey:eapiServerKey
   myswitch# configure terminal
   myswitch(config)# management security
   myswitch(config-mgmt-security)# ssl profile eapi
   myswitch(config-mgmt-sec-ssl-profile-eapi)# certificate eapiServerCert key eapiServerKey
   myswitch(config-mgmt-sec-ssl-profile-eapi)# management http-server
   myswitch(config-mgmt-http-server)# protocol https ssl profile eapi

Configuring multiple VRFs

Since release 4.20, you can configure multiple VRFs in management api http-commands mode. By entering submode vrf <vrfName>, you can enable or disable Command API server in multiple VRFs by typing [no] shutdown inside each VRF submode. If no VRF specified and Command API server is enabled, it will start in default VRF automatically. When you enable Command API server in the first user VRF, it will be removed from default VRF. You can run Command API server in default VRF by explicitly configuring in submode vrf default, with command no shutdown (this behavior is for reasons of backwards compatibility).

   myswitch> enable
   myswitch# configure terminal
   myswitch(config)# management api http-commands
   myswitch(config-mgmt-api-http-cmds)# vrf foo
   myswitch(config-mgmt-api-http-cmds-vrf-foo)# no shutdown
   myswitch(config-mgmt-api-http-cmds-vrf-foo)# exit
   myswitch(config-mgmt-api-http-cmds)# vrf bar
   myswitch(config-mgmt-api-http-cmds-vrf-bar)# no shutdown
   myswitch(config-mgmt-api-http-cmds-vrf-bar)# exit
   myswitch(config-mgmt-api-http-cmds)# vrf default
   myswitch(config-mgmt-api-http-cmds-vrf-default)# no shutdown
   myswitch(config-mgmt-api-http-cmds-vrf-default)# exit
   myswitch(config-mgmt-api-http-cmds)# show management api http-commands
   Enabled:            Yes
   HTTPS server:       running, set to use port 443
   HTTP server:        shutdown, set to use port 80
   Local HTTP server:  shutdown, no authentication, set to use port 8080
   Unix Socket server: shutdown, no authentication
   VRFs:               bar,default,foo
   ...

The Spec

The Command API uses the lightweight, standardized protocol JSON-RPC 2.0 to communicate between your program (the client) and the switch (the server). Although most languages have client libraries which abstract away the JSON-RPC internals, it is useful to understand the communication mechanism between the client and the switch.

Exploring the Command API

To explore the API, point your web browser to http[s]://myswitch/, after enabling Command API. This web-app lets you interactively explore the protocol, return values, and model documentation. Please note that if you visit the tool using HTTPS, your browser may emit a warning; this is because by default the agent uses a locally signed certificate for encryption purposes and can be ignored. You may always set a certificate and key issued by your own CA for enhanced security.

An annotated example

The client starts by sending a JSON-RPC request via an HTTP POST request to http://<yourswitch>/command-api, which encapsulates a list of CLI commands it wishes to run, and the switch replies with a JSON-RPC response containing the result of each CLI command that was executed. The commands in the request are run in order on the switch. After the switch has executed all commands, it exits back to unprivileged mode. If any of the commands emit an error, no further commands from that request are executed, and the response from the switch will contain an error object containing the details of the error that occurred.

To illustrate, here are annotated versions of the underlying Request and Response messages sent between the client and switch.

The Request

{
"jsonrpc": "2.0", The protocol version. Always "2.0", and usually set by a client library.
"method": "runCmds", The method to run on the switch. At this time runCmds is the only method supported.
"params": {
"version": 1, version is a mandatory parameter representing the API version. Currently always has the value 1.
"cmds": [ cmds is a mandatory parameter which contains a list of CLI commands. Commands may either be a string or a complex object type. For more details, see the command specification
"enable",
"configure",
"interface Ethernet1",
"switchport access vlan 3",
"show vlan 3",
],
"format": "json" format is an optional parameter specifying response format. Can be text or json, and defaults to json.
},
"id": 1 A unique identifier that will be echoed back by the switch. May be a string or number.
}

The Response: success case

This response is returned by the switch after receiving the above request. All responses conform JSON-RPC 2.0 specifications.

{
"jsonrpc": "2.0", The protocol version. Always "2.0".
"result": [ The result list. Contains one object for each CLI command executed, and the position of the result corresponds to the original command list. If the request specified a text format, however, result will contain a list of strings.
{},
{},
{},
{ "warnings": [ Most non-show commands produce no output and thus return an empty JSON object. However, in some cases, a command may produce warnings or errors which will be stored in arrays of the same name.
"Access VLAN does not exist. Creating vlan 3"
]
},
{ "sourceDetail": "", show commands will return formatted JSON objects containing all information seen in the ASCII output in the normal CLI. In this case, show vlan 3 will give you a mapping from VLAN id to VLAN model.
"vlans": {
"3": {
"status": "active",
"name": "VLAN0003",
"interfaces": {
"Ethernet1": {
"annotation": null,
"privatePromoted": false
}
},
"dynamic": false
}
}
}
],
"id": 1 The same id (string/number) that was provided by the request.
}

The Response: failure case

In this case, let's assume our account does not have the appropriate AAA permissions to enter configure mode.

{
"jsonrpc": "2.0",
"error": { On an error, no result object is present, only an error object, which is guaranteed to have the following attributes: code, messages, and data.
"code": 1005, Command API error code.
"message": "CLI command 2 of 5 'configure' failed: permission to run command denied", General high-level error message.
"data": [ Similar to the result object in the successful response, the data object is a list of objects corresponding to the results of all commands up to, and including, the failed command. If there was a an error before any commands were executed (e.g. bad credentials), data, will be empty.
{}, The enable command passed without incident.
{ "errors": [
"Authorization denied for command 'configure'" In this case, we failed on the second command. The last object in the data array will always correspond to the failed command. The command failure details are always stored in the errors array.
] }
]
},
"id": 1
}

Command Specification

In most cases, the client will use a simple string to specify the CLI command in the cmds parameter in the request. In certain cases, however, clients may wish to specify additional parameters during the command's execution. To use complex commands, pass a JSON object in lieu of a string, with the following attributes:

For example, to set the message of the day to Hello World!, the client should set cmds to
[ "enable", "configure", { "cmd": "banner motd", "input": "Hello World!\nEOF" } ]
Similarly, if the switch requires an enable password, the following cmds value would let you enter exec mode and clear interface counters
[ { "cmd": "enable", "input": "hunter2" },  "clear counters" ]

Error Codes

The following errors are generated if there is an issue with the request. Do note that new error codes may be introduced in future releases.

JSON-RPC 2.0 Defined Errors
CodeDescription
-32700Parse Error
-32600Invalid Request
-32601Method not found
-32602Invalid params
-32603Internal server error
Command API Errors
CodeDescription
1000General error
1001Command caused internal exception
1002Invalid command: the string does not match any command
1003Command not ready: attempted to get json from a show command, however the command has not been converted to be compatible with the Command API yet. Use format = text to run this command and get ASCII output.
1004Command incompatible: cannot run command over the Command API
1005Command unauthorized: user has insufficient permissions to run the command

Using the Command API

In practice, most libraries hide the JSON-RPC version, id and protocol details. Instead you only need to call the runCmds method with the version and cmds parameters and, optionally, the format parameter. The responses generated by the client library usually follow language conventions. For example, in Python, an error response results in an Exception being thrown, while Javascript expects an error handler callback.

Unsupported commands

Certain commands are not permitted and will always return an error. The largest class of such commands are interactive commands; watch is a prime example. Other commands, like reload must use their non-interactive versions: such as reload now. The bash command is only allowed with the timeout <timeout> argument, ie. bash timeout <timeout> <ARG>.

Commands that attempt to use CLI pipes (e.g. show interfaces | grep Ethernet1 ) are also not allowed. In addition, no abbreviations are allowed in commands. This is necessary because future versions of EOS may add more commands, rendering previous abbreviations ambiguous.

Unconverted commands

Although you can access almost any CLI command via the Command API, not all show commands have been converted to return formatted data, and trying to run the command with the format parameter set to json will result in an error. However, you can still get the CLI ASCII output for the unconverted command by setting the format parameter to text. With this parameter set, the response will contain an array of strings (rather than an array of objects), each representing the text output of the corresponding command. When operating with format set to text, no errors will be generated; rather they will be included in the ASCII output, the same way they are printed in a standard CLI session. To view converted commands visit the command documentation center.

Using JSON-RPC

The JSON-RPC spec is well documented at JSON-RPC Specification, and there are libraries in almost every language that make dealing with JSON-RPC a breeze. Here are a list of some recommended client libraries:

On-box Usage of Command API

It is often useful to run scripts that use Command API directly on the switch itself, without configuring them with AAA credentials. EOS provides two mechanisms to accomplish this. The first is an HTTP server bound to localhost (on port 8080 by default), which only accepts connections arriving from the same machine. The other solution is a Unix domain socket. To enable one or both, use the following commands:

   myswitch> enable
   myswitch# configure terminal
   myswitch(config)# management api http-commands
   myswitch(config-mgmt-api-http-cmds)# protocol http localhost
   myswitch(config-mgmt-api-http-cmds)# protocol unix-socket
   myswitch(config-mgmt-api-http-cmds)# no shutdown

Once Command API is enabled then you access via the local domain socket unix:/var/run/command-api.sock

   switch = Server( "unix:/var/run/command-api.sock" )

If configured to use HTTP over localhost, your script can access the API as follows:

   switch = Server( "http://localhost:8080/command-api" )

Client-Side Certificates

Client authentication can be accomplished via X.509 public key certificates.

What is this good for? Imagine you have a general purpose script that does some operations on an Arista switch using eAPI. That script will need your EOS username/password. Since the various users of that script might have their own EOS usernames/passwords, putting them into the script makes little sense; having the script collect them from a well-known password file relative to $HOME makes more sense. The public key certificates is simply the standard way of dealing with such a well-known password file.

Public key certificates are digitally signed by a Certificate Authority (CA). If you trust that CA, then you must trust the certificates it signed. The trusted CA(s) has to be configured into the Arista switch. Then any client certificate signed by that trusted CA will be accepted by eAPI, but only if the CA directly signed it, that is, a client cannot create "sub-clients" (the CA plays the role of the admin that creates the account for which the certificate stands). Client certificates can also be self-signed, in which case each client certificate has to be configured into the Arista switch as trusted.

The certificate will need to contain the username and its privilege level (a number between 1 and 15) in the common name field of the certificate's subject field (see openssl command syntax below for more exact details). If a connection with this certificate is accepted, then on the switch it will be accounted against that username and given specified privilege level. Note that this will bypass RADIUS/TACACS+.

If you configure trusted CA/clients, then you also need to configure the HTTPS server's certificate/privateKey using the management security ssl profile commands as illustrated below (server keys are no longer auto-generated in this case -- once you generate the client's certificate, you might as well generate the server certificate and key). Those certificates/keys are stored in bootflash under /persist/secure/ssl, not in the startup-config.

   myswitch> enable
   myswitch(config)# configure
   myswitch(config)# ! download the client certificates and server certificate/key
   myswitch(config)# copy scp:<username>@<server>//<certificate1-filename> certificate:ca1
   myswitch(config)# copy scp:<username>@<server>//<certificate2-filename> certificate:user1
   myswitch(config)# copy scp:<username>@<server>//<certificate3-filename> certificate:httpServer
   myswitch(config)# copy scp:<username>@<server>//<privateKey3-filename>  sslkey:httpServer
   myswitch(config)# ! create an ssl profile and add those certs/keys into it
   myswitch(config)# management security
   myswitch(config-mgmt-security)# ssl profile eapi
   myswitch(config-mgmt-sec-ssl-profile-eapi)# trust certificate ca1
   myswitch(config-mgmt-sec-ssl-profile-eapi)# trust certificate user1
   myswitch(config-mgmt-sec-ssl-profile-eapi)# certificate httpServer key httpServer
   myswitch(config)# ! now configure eAPI to use that ssl profile
   myswitch(config)# management api http-commands
   myswitch(config-mgmt-api-http-cmds)# protocol https
   myswitch(config-mgmt-api-http-cmds)# protocol https ssl profile eapi
   myswitch# ! some show commands
   myswitch# show management security ssl certificate
   myswitch# show management security ssl profile
   myswitch# show management security ssl key
   myswitch# ! find more commands with:
   myswitch# show cli commands all | grep ssl 

Above we first downloaded certificates and a private key (key is for the https server). Certificates (that contain the public key) go to the “certificate:” filesystem, (private) keys go to the “sslkey:” filesystem. Then we add those certificates/keys into an ssl profile called “capi”, then make command-api use that profile.

A self signed certificate and associated private key (a "pair") can be generated with the openssl tool like so: (replace the <fields> with actual values)

   openssl req -new -nodes -x509 -days <number-of-days> \
      -subj '/CN=<username> <privilege> <optional-stuff-to-your-liking>' \
      -out <certificate-filename> \
      -keyout <key-filename> 

You can use the above for generating the certificate:httpServer and sslkey:httpServer or the certificate:user1/ca1 used in the example above. In case of the client/user, the key-file is for the client to install in his account for access by tools like wget/browsers (in the same way as a username/password would be). In case of the CA, you (the admin), would keep that key to sign client certificates with.

You can create a self signed certificate per user, and have EOS trust each one, or you can create a CA, have EOS trust just that CA, then sign the user certificates you generate with the (private) key of the CA. In that case, creating the certificate is a 2 step process: you first create a "signing-request" (similar to creating a cert/key pair, but without the "-x509 -days" options), which is then signed in the second step.

   openssl req -new -nodes \
      -subj '/CN=<username> <privilege>' \
      -out <signing-request-filename> \
      -keyout <user-key-filename> \
   openssl x509 -req -sha256 -days <valid-for-days> \
      -in <signing-request-filename> \
      -CA <ca-certificate-filename> -CAkey <ca-key-filename> \
      -set_serial <serial-number> \
      -out <user-certificate-filename> 

An eAPI request can be generated like so by using the wget utility: (the --no-check-certificate is required if the server uses a self-signed certificate)

   wget --no-check-certificate \
       --private-key=<key-filename> \
       --certificate=<certificate-filename> \
       -q -O - \
       https://<server>/command-api --post-data="$(cat <file-with-json-request>)" 

A simple <file-with-json-request> could have this content:

   {
      "jsonrpc": "2.0",
      "method": "runCmds",
      "params": {
          "version": 1,
          "cmds": [ "show clock" ],
          "format": "json"
      },
      "id": "1"
   } 

Session Based Authorization

Some systems use 2-factor authentication where RSA is implemented and keys rotate every 30 seconds. It is cumbersome to authenticate using username and password in these systems. Command API requests use basic authentication by default. This results in high overhead on the switch to verify user credentials for each command request. HTTP sessions can be used to reduce the overhead in the above use cases by caching user authentication. Usage of the session mechanism is explained below.

User Login

A user logs in by sending an HTTP POST request to http[s]://<yourswitch>/login with an encapsulated JSON user credential.

{ 
"username": <user>,
"password": <password>
}
The switch will authenticate the user credentials in the request and if successful, send a set cookie response containing a Session=<sessionId> field and an expiry of 24 hours. The sessionId is an unique session identifier. On failure, a "401 Unauthorized" HTTP response will be sent in lieu of a cookie.

Command Requests

Subsequent command requests to http[s]://<yourswitch>/command-api need to include Session=<sessionId> in the cookie header. The format of the command request as well as its response is same as the one using the basic authentication scheme. If the session is invalid, a "401 Unauthorized" response will be sent.

User Logout

A user may logout by sending an HTTP POST request to http[s]://<yourswitch>/logout with Session=<sessionId> in the cookie header. On success, a "200 OK" response will be sent, otherwise a "400 Bad Request" response will be sent.

Session Validation

Browser-based applications may find it useful to check if a valid session already exists, thereby avoiding a prompt. To do this, send a POST request to http[s]://<yourswitch>/login with an empty JSON body: {}. If the cookie included in your request exists and contains a valid sessionId, the server will send a "200 OK" HTTP response, otherwise it will reply with a "401 Unauthorized" response.

Versioning

eAPI models are now revisioned.
This means that if a model is modified in a non-backwards compatible way, then its revision will be bumped up (revisions are numbers, default value is 1).
By default an eAPI request will return revision 1 of the model instance, this ensures that older management software will not suddenly stop working when a switch is upgraded.
If a specific revision is required, then that revision must be specified in the json rpc, like illustrated in this example json request below:

{
"jsonrpc": "2.0",
"method": "runCmds",
"params": {
"version": 1,
"cmds": [
"enable",
{ "cmd": "show version", "revision": 2 }, Use a complex command to specify extra attributes like the revision of a command
"show clock",
],
"format": "json"
},
"id": 1
}

Incompatible Model Changes

The following list contains changes that will result in a new revision of a model. In the list below, the term "element" refers to an attribute or submodel. Looking at it from the other side, the model changes that can happen without a revision bump can be summed up as:

Normally, in TLV/schema-less systems (like JSON is), adding new key-value pairs is considered a compatible change, as long as the new pairs are just an "enhancement" that does not affect the meaning of the remaining pairs, since older system will be oblivious to them (they naturally skip over them).
So why is adding a mandatory element non-backwards compatible? Because we are talking from the perspective of the published model.
If a network management developer looks at the latest model and sees that model X is at revision 1 and attribute Y is mandatory, then he will assume Y to be always present, then get a key error when discovering a switch running an older release! The downside is that you need extra effort to get the newer attributes (specify a later revision), but unless you add code to deal with that extra data, you might as well just continue receiving only the older attributes.
If your application can deal with "fuzzy models", then look for "Get Current Revision" further below.

Understanding Revisions And Versions

Do not confuse "revisions" and "versions" as they are not the same. A "revision" applies to a particular CLI command whereas a "version" is global and is internally translated to a specific "revision" for each CLI command in the rpc. At this time, the API remains at version 1, and will do so until many models have progressed to later revisions. Until the version gets a bump, in order to access newer revisions of CLI output, you have to name the revision you desire, per command, or use the "latest" version as documented in the "Accessing a Command's Current Revision" section below. Note that an explicit revision value attached to a command always takes precedence over the version.

Here is how revisions and versions play together.
Assume commands C1 and C2 have shipped with those changes across EOS releases (C3 is new and has no revisions yet -- still at revision=1):

Example Commands with Versions and Revisions
CommandEOS-ReleaseeAPI-VersionCommand-Revision
C14.13.111
C14.13.222
C14.13.323
C14.13.433
C14.13.534
C24.13.111
C24.13.432
C34.13.511
Resulting Responses
CommandVersion-RequestedRevision-Returned
C11 1
C12 2
C13-*3
C21-21
C23-*2
C31-*1
If a revision bump occurs without version bump, then, for a given version, you are stuck at the initial revision level: no matter the version, you will never get revision 4 for C1. The only way to see a more up-to-date output (for example C1 at revision 4) is to explicitely ask for that revision of the command, or to request the current revision (see next chapter), which could return anything from the distant future, thus unpredictable.

Note that requiring a version higher than the command knows about will simply return the latest available one.

Accessing a Command's Current Revision

If your code can deal with new output naturally, then you may want to always get output according to the latest revision a switch can provide (its current revision), which is possible by providing the special version value 'latest' (in that case only, the version is a string). For example:

{
"jsonrpc": "2.0",
"method": "runCmds",
"params": {
"version": "latest", Use the special string value of "latest" for the version to get the current output revision of requested show commands
"cmds": [
"enable",
"show ip interfaces"
],
"format": "json"
},
"id": 1
}

More Documentation On Revisions

The revisions and corresponding versions (if any) of cli show commands are added to the CommandApiGuide.pdf that is published for each EOS release.

Since there is now infrastructure in place during the software development cycle to detect changes to models and force the bumping up of revision numbers, thus ensuring that eAPI is a stable and predictable API, we eventually could also publish our per revision "Reference Models", as those are a python structures and thus more formally usable than a web-page or pdf.

˶