An Introduction to the Golang eAPI

Introduction

Since the release of Arista EOS Command API (eAPI) many have grown to appreciate its stability and easy-to-use syntax which allow applications or scripts complete programmatic control over EOS. Development of applications that interface with your Arista device for the purpose of configuration or monitoring is simple and fairly straight forward. With a little knowledge of Python, Perl, Ruby, or your favorite language of choice, and familiarity with the underlying transport mechanism (JSON-RPC), it’s easy to write some custom functionality to help with deployments, provisioning, configurations and many other things. 

Arista has continued its ongoing effort to make life easier for our customers. Leveraging eAPI, and building on its capabilities, the Python and Ruby client for eAPI (pyeapi and rbeapi) were introduced making it even easier and fun to develop functionality to interface with Arista devices. These APIs provide a high level abstraction of CLI syntax as well as hide minor details of the underlying eAPI transport.  Aside from these advantages, they provide a rich set of features for some of the most popular programming languages.

Recently, a more modern programming language has started to gain traction and popularity. Golang (or ‘Go’ for short) is a C-like, statically typed, garbage collected, natively compiled language originally created and developed within the walls of Google but was made available to the open source community back in 2009. While Go is not a scripting language like Python or Ruby, lots of people write scripts with it since it’s a fast, and minimalistic programming language.

Some common features of Go associated with high-level languages:

  • unicode strings
  • built-in data structures
  • duck typing
  • garbage collection
  • high-level concurrency support
  • broad standard library

As part of Arista’s commitment to our customers, and furthering innovation, we have announced goeapi (Golang eAPI) for simplifying interaction with Arista devices using eAPI.

This article will provide a brief overview of installing, configuring, and using the Golang Client for eAPI.

Installation

Installation of goeapi requires little effort. Simply issue the following command to obtain the recent stable version:

go get github.com/aristanetworks/goeapi

Assuming a proper working Go environment and workspace (as described in http://golang.org/doc/code.html) exists, you should now have access to goeapi and can proceed with building your application.

Note: Golang 1.5+ is recommended for using goeapi.

Configuration

Goeapi relies on a configuration file (~/.eapi.conf) to define individual device/node settings. This configuration file serves as a central and convenient location to store/manage device connection information. INI style formatting is used where each section represents a given device and its connection credentials. A device defined can be referenced within goeapi without the need to import device characteristics manually.

To start, in our home directory let us first create our .eapi.conf file with 2 devices (Arista1 and Arista2) along with their respective credentials. The connection names Arista1 and Arista2 will be used by goeapi to refer to the device connection characteristics when setting up the underlying connection. We will define one connection for HTTP and the other HTTPS:

$ cat ~/.eapi.conf
 [connection:Arista1]
 host=192.168.3.11
 username=goeapi
 password=yeahright
 transport=http
 
 [connection:Arista2]
 host=192.168.3.12
 username=goeapi
 password=yeahright
 transport=https

The following configuration options are available for defining node entries:

  • host – The IP address or FQDN (Fully Qualified Domain Name) of the remote device. If the host parameter is omitted then the connection name is used
  • username – The eAPI username to use for authentication (only required for http or https connections)
  • password – The eAPI password to use for authentication (only required for http or https connections)
  • enablepwd – The enable mode password if required by the destination node
  • transport – Configures the type of transport connection to use. The default value is https. Valid values are:
    • http
    • https
    • socket

Using Goeapi

Once goeapi has been installed, and your .eapi.config file is setup correctly, you are now ready to try it out. Using goeapi to write an interactive device application is fairly easy. Here is a simple working example of a go program using the device (Arista1) defined above.

package main
 
 import (
    "fmt"
 
     "github.com/aristanetworks/goeapi"
     "github.com/aristanetworks/goeapi/module"
 )
 
 func main() {
     // connect to our device
    node, err := goeapi.ConnectTo("Arista1")
    if err != nil {
        panic(err)
    }
     // get the running config and print it
    conf := node.RunningConfig()
    fmt.Printf("Running Config:\n%s\n", conf)
 
    // get api system module
    sys := module.System(node)
     // change the host name to "Ladie"
     if ok := sys.SetHostname("Ladie"); !ok {
        fmt.Printf("SetHostname Failed\n")
    }
     // get system info
    sysInfo := sys.Get()
    fmt.Printf("\nSysinfo: %#v\n", sysInfo.HostName())
 }

In order to instantiate the client for a goeapi connection, you should use ConnectTo(), passing the name of the connection specified in your .eapi.conf. In the above code, we are connecting to node ‘Arista1’.

The return from ConnectTo() yields a node object that can be used to perform basic simple operations against the device. Above we are using the defined function to get the running-config for the device. Goeapi also provides a suite of defined api modules that can be used to perform different operations/queries on a device. For instance, later in the above example we use the node reference to obtain a handle to the system api. From this handle we can set the hostname (shown by our call to sys.SetHostName(“Ladie”)) or get the current system info where we can check the new value of hostname on the device.

Goeapi also provides a way for users to directly couple a command with a predefined response. The underlying api will issue the command and the response is stored in the defined type. For example, say a user would like to obtain the Version, Serial Number, and System MAC for a device. We first need to know the command response JSON layout and then we can form our response structure. We know that Version, Serial Number, and System MAC are all attainable via the ‘show version’ CLI command. Using Arista Command API Explorer we can determine the response layout:

commandAPI-explorer

An alternative way using the CLI command and converting to JSON

 vEOS1#sh version | json
  {
     "modelName": "vEOS", 
     "internalVersion": "4.15.3F-2812776.4153F", 
     "systemMacAddress": "00:0c:29:a8:7d:08", 
     "serialNumber": "", 
     "memTotal": 1897532, 
     "bootupTimestamp": 1461275519.58, 
     "memFree": 115944, 
     "version": "4.15.3F", 
     "architecture": "i386", 
     "internalBuildId": "34549125-b84f-41f0-b8bb-ce9d509814de",
     "hardwareRevision": "" 
  }

In the above screenshot we can see the command `show version` has several informational fields provided as part of the response. Using this, we can then build our response structure and couple our command associated with it. In our example we will define the response structure to accommodate all the fields represented in the show version JSON response, however if only a subset is needed then you would only define the fields you need. Using the above JSON response, our ShowVersionResp structure will be defined as:

 // ShowVersionResp defined data structure for mapping JSON response
 // of 'show version' to manageable object
 type ShowVersionResp struct {
   ModelName        string
   InternalVersion  string
   SystemMacAddress string
   SerialNumber     string
   MemTotal         int
   BootupTimestamp  float64
   MemFree          int
   Version          string
   Architecture     string
   InternalBuildID  string
   HardwareRevision string
 }

First thing to note (in the above structure definition) is how closely the member fields match the JSON keys in the response. The purpose for this is to help map the JSON response data to the correct structure field. So in the case of the JSON key systemMacAddress, goeapi will identify the fields in which to store the data by looking over the defined structure ShowVersionResp to find an exported field named SystemMacAddress or SystemMACAddress or some other case-insensitive match of systemMacAddress. Once a match is identified the data will be stored in the respective ShowVersionResp field. Make sure the fields you define are spelled the same as the JSON keys otherwise the field will not be populated.

In order for our response structure to be filled in properly, the underlying API needs to know the command associated with the expected response. The EapiCommand interface defines a method GetCmd() that must be defined as part of your response to return the command being issued:

// GetCmd returns the command this EapiCommand relates
func (s ShowVersionResp) GetCmd() string {
   return "show version"
}

The resulting code:

package main
import (
   "fmt"
   "github.com/aristanetworks/goeapi"
)
type ShowVersionResp struct {
   ModelName        string
   InternalVersion  string
   SystemMacAddress string
   SerialNumber     string
   MemTotal         int
   BootupTimestamp  float64
   MemFree          int
   Version          string
   Architecture     string
   InternalBuildID  string
   HardwareRevision string
}
func (s *ShowVersionResp) GetCmd() string {
   return "show version"
}
func main() {
   node, err := goeapi.ConnectTo("Arista1")
   if err != nil {
      panic(err)
   }
   svRsp := &ShowVersionResp{}
   handle, _ := node.GetHandle("json")
   handle.AddCommand(svRsp)
   if err := handle.Call(); err != nil {
      panic(err)
   }
   fmt.Printf("Version           : %s\n", svRsp.Version)
   fmt.Printf("System MAC        : %s\n", svRsp.SystemMacAddress)
   fmt.Printf("Serial Number     : %s\n", svRsp.SerialNumber)
}

In the above code our ShowVersionResp implements interface EapiCommand by defining GetCmd().
AddCommand() stages our command to be executed and only allows type EapiCommand as a passed argument.
The type checking nature of Go will prohibit one from passing a response structure without
properly implementing the EapiCommand interface. After this, Call() is issued to execute the command
against the device and svRsp is populated with data from the response.

Lets build the example and try it:

$ go build showver_example.go
$ ./showver_example
  Version           : 4.15.3F
  System MAC        : 001c.732a.ca56
  Serial Number     : JPE12380827

One interesting feature of AddCommand() is you can specify multiple EapiCommands to issue. Provided several commands/responses have been defined, goeapi supports command stacking to batch issue all the commands at once:

  ...
  handle, _ := node.GetHandle("json")
  handle.AddCommand(showVersion)
  handle.AddCommand(showVlan)
  handle.AddCommand(showHostname)
  handle.AddCommand(showIp)
  if err := handle.Call(); err != nil {
    panic(err)
  }
  fmt.Printf("Version           : %s\n", showVersion.Version)
  fnt.Printf("Hostname          : %s\n", showHostname.Hostname)
  ...

Summary

The goal of this article is to introduce and provide a starting point for writing Golang applications that can interact with EOS. From installation of the Golang eAPI library, creating and defining devices in your eapi config file, to examples of basic goeapi usage we provide the groundwork for users wanting to utilize golang to interface with Arista devices.  Happy coding!

 


About Arista EOS+ CS

Arista EOS+ CS is an organization that has been created to embrace, alongside our customers, the devops community. EOS+ CS remains focused on delivering technical solutions freely to the open source community for the purposes of advancing innovation around traditional network operational environments. The Arista EOS+ CS team contributes to a variety of open source projects and continues to actively engage in devops communities in support of building more advanced operational and development models that operate next generation massively scalable data center environments.

Resources