Skip to content

Build and Development Environment

ruferp edited this page May 1, 2024 · 25 revisions

Building and Developing an Agent

Introduction

EOS SDK lets you develop agents on any 32-bit Linux environments, making it easy to compile and test new programs. This is because the SDK is simply a collection of C++ header files. On a real switch or vEOS instance, these headers are exposed in the library libeos.so, provided by the EosSdk.i686.rpm extension, and contain the implementation for communicating with Sysdb and the rest of EOS. However, in your development environment, these headers are backed by stubbed-out functionality provided by the EosSdk-stubs.tar.gz bundle, and is free from any EOS-specific dependencies. Your agent builds against this stub library and produces a binary executable that, when copied to the switch, dynamically links against the full libeos.so implementation.

This GitHub repository contains the entirety of the headers, stub implementation and examples. To develop against a specific version of the SDK, you can view and download a tarball from the releases page. We welcome improvements to stubs functionality, along enhancements to header comments and examples via pull requests. You can submit bug reports and suggestions through GitHub's issue tracker.

Set up

In order to compile C++ programs, you will need a 32-bit Linux system that has the following software installed (those were the original versions, since updated, see further down under "Cross-Compiler" for details):

Component Minimum Version
g++ 4.5.1
glibc 2.13 (libc version 6)
automake 1.14 (minimum version supported)
autoconf 2.66
m4 1.4.14

These build tools are included with many distributions, including Ubuntu 12.04, Fedora Core 14, and Fedora Core 18. On Ubuntu systems, the build tools are included with g++-multilib, while Fedora systems use libraries from the libgcc.i686, glibc-devel.i686, and libstdc++.i686 RPMs.

We recommend that users create a new (virtual) machine with one of these operating systems installed. Advanced users can cross-compile from a 64-bit environment to a 32-bit one. We provide a Debian package with a cross-compiler that will give you a compile environment that exactly matches the one we use to build EOS itself, see the "cross-compiler" chapter further down.

Creating the stubs library

Once you have a system that meets the above requirements, download the stubs tarball with the same version as the release of the EosSdk.i686.rpm you plan on using. Then un-tar the package:

bash# tar xzf <SDK-stubs-version>.tar.gz

and run the build.sh script, which will configure the build system and make the stubbed version of libeos.so.

bash# cd <Newly-created-untarred-SDK-stubs-folder>
bash# ./build.sh
bash# sudo make install

The recommended set of flags when building your code can be found in build.sh's CFLAGS variable, and can be edited to include the compilation flags you prefer. Build output is saved in config.log, and will show relevant information in case of a compilation error.

Building the Python bindings

If you'd like to use Python, you can simply transfer your program to the switch, as the language does not require a compilation step. However, you may find it useful to generate the Python bindings locally so you can inspect the naming changes, run unit tests, or use pylint in your development environment. To do so, first install SWIG and then invoke build.sh with the --enable-python flag:

bash# ./build.sh --enable-python
bash# sudo make install
bash# python -c "import eossdk; print eossdk.version"
1.3.1

Building your agent

After creating the libeos.so library, you can build your agent:

bash# g++ -std=gnu++0x -o MyAgent MyAgent.cpp -leos

The -std=gnu++0x indicates that this should be built using the C++11 standard, and -leos lets the compiler know that we are linking against the libeos.so library we built in the previous steps. You may pass additional compilation flags to g++ in order to create binaries with higher levels of optimization, debuggability, compiler warnings, etc. To see the recommended list of parameters the stubs library is built with, you can view the CFLAGS variable that build.sh uses for compilation:

CFLAGS='-Os -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector --param=ssp-buffer-size=4 -Wno-unused -Wno-uninitialized -fasynchronous-unwind-tables'
CFLAGS_32B='-m32 -march=i686 -mtune=atom'

At this point, you have a working C++ binary and a real EOS agent! This binary can be unit tested and even run locally. Currently the stubs only have minimal functionality, though, so agents run locally will not do much. When used in conjunction with a library that lets you externally trigger events, like GoogleMock, you can write tests that invoke any handlers you've overridden. Once you copy this executable to a switch with the extension installed, you can run the agent and it will link against the functional SDK implementation.

Linking against other libraries

You may wish to use third party libraries when writing your agent so you use can externally developed code and open source projects. You can accomplish this in two ways. The first is to statically link against the library. This is fairly straightforward: when compiling your program, set the appropriate flags to link against the library statically, which will cause the third party libraries logic to be included in the resulting binary. The second approach is dynamic linking. This requires that the external library is also available on the switch, and thus should be packaged as an extension and installed before attempting to run the agent. The only caveat is that linked libraries, just like your own agent, must be built for a 32 bit environment.

Word of Caution

If you want to statically link with a different version of a standard library that libeos.so also links against, then depending on the case, this might cause binary incompatibility issues, as can be the case with libstdc++ that defines the std::map etc. data structures, since some EosSdk APIs use those data structures, which are thus passed between your code and our code, and therefore should have been compiled against the same header files to ensure binary compatibility. Since libstdc++ is normally tied to the gcc version, and libstdc++ is not as backwards compatible / versioned as libc is, this essentially means that if you use such EosSdk APIs, you better use the same gcc version as EOS did to build itself.

Cross-Compiler

To facilitate the building requirements, we have a cross-compiler for Ubuntu, distributed as a Debian package, that you can install on your system in case you do not have the same version of gcc and libraries than those used in the EOS ecosystem. Because libstdc++ is tied to the gcc version, and libstdc++ versions are not always binary compatible (unlike libc), we usually needed a different cross-compiler each time we upgraded gcc. Lately libstdc++ has been more stable tough, however we do compile our gcc in 4.0 binary compatibility mode, and this is not the default, also we changed some other defaults (the sse optimisations for example) and most EOS are still running 32 bit code, so a cross-compiler environment that also comes with the exact library versions found on the switch is still very valuable. Plus moving to a later gcc version gives you more compile options and warnings, so upgrading to a new cross-compiler is always advantageous.

Cross-Compiler Package Use With EOS Target installs to
arista-fc14.deb < 4.17.0F /opt/arista/fc14/
arista-fc18-gcc4-9-2.deb >= 4.17.0F /opt/arista/fc18-gcc4.9.2/
arista-fc18-gcc5-4-0.deb >= 4.20.0F /opt/arista/fc18-gcc5.4.0/
arista-fc18-gcc6.3.1-glibc2.18.deb >= 4.22.0F /opt/arista/fc18-gcc6.3.1-glibc2.18/
arista-centos7-5-gcc6-3-1-glibc2-17.deb >= 4.23.0 /opt/arista/centos7.5-gcc6.3.1-glibc2.17/
arista-centos7-5-gcc6-5-0-glibc2-17.deb >= 4.23.2 /opt/arista/centos7.5-gcc6.5.0-glibc2.17/
arista-centos7-5-gcc8-4-0-glibc2-17.deb >= 4.25.0 /opt/arista/centos7.5-gcc8.4.0-glibc2.17/
arista-gcc11.deb >= 4.32.0 /opt/arista/gcc11/

Before EOS 4.22 the cross-compiler itself was a 32bit executable, which your 64bit system can run as long as the corresponding libraries have been installed:

sudo apt-get install -y libc6-i386

With EOS 4.22 the cross-compiler is a 64bit executable that can produce both 32 bit (with option -m32) and 64 bit (with option -m64, the default) executables. That is because now there is also a 64 bit variant of EOS (that can be used on routers with enough RAM).

That cross-compiler comes with all the standard libraries as they are installed on the EOS switch (and links against those, not the ones natively installed on Ubuntu). However, the fc14 versions of the cross-compiler environment do not have the exact same SSL library as the switches, nor the corresponding SSL related development header files (we do keep SSL current in terms of security vulnerabilities, so it changes quite often).

The debian package is installed like this:

sudo dpkg -i arista-fc14.deb

and can be un-installed with the corresponding -r option.

The cross-compiler is also available as an rpm, see https://www.arista.com/en/support/software-download

Here is a full end-to-end example:

# install the cross-compiler
ncftpget ftp://ftp.arista.com/data/ar/crossgcc/arista-fc14.deb
sudo dpkg -i arista-fc14.deb

# get EosSdk headers and build the stubbed libeos.so
mkdir build; cd build/
scp you@machine:/somewhere/EosSdk-stubs-1.5.4.tar.gz .
tar -xzvf EosSdk-stubs-1.5.4.tar.gz
cd EosSdk-stubs-1.5.4/
PATH=/opt/arista/fc14/bin:$PATH ./build.sh

# add one of the example shipped with the stubs to the makefile and build again
vim Makefile.am # add the lines below
    noinst_PROGRAMS = HelloWorld
    HelloWorld_SOURCES = examples/HelloWorld.cpp
    HelloWorld_LDADD = libeos.la
sudo apt-get install autoconf
autoreconf --force
PATH=/opt/arista/fc14/bin:$PATH ./build.sh

# copy to switch and run it
scp .libs/HelloWorld root@myEosRouter:/tmp
ssh myEosRouter
myEosRouter# configure
myEosRouter(config)# daemon HelloWorld
myEosRouter(config-daemon)# exe /tmp/HelloWorld
myEosRouter(config-daemon)# no shut
myEosRouter(config-daemon)# bash cat /var/log/agents/HelloWorld-$(pidof HelloWorld)
myEosRouter(config-daemon)# show daemon
...

# install the development headers to /opt/arista/fc14/usr/include/eos/ 
# and the stubbed EosSdk library to /opt/arista/fc14/usr/lib/libeos.so
# (where the crosss-compiler will naturally look for them).
sudo make install

Note that if you constantly switch between different EosSdk versions and share a build server, then you might want to install the headers/library elsewhere and provide a -I directive to the compiler and a -L to the linker, depending on the target version. Alternatively, you could run build.sh as part of your build system which picks the correct EosSdk-stubs-xxx.tar.gz (that is, never install them, like we did with the HelloWorld example above).

Download the Cross Compiler

Sometimes available via ftp, for easier pickup of pre-releases (old files get sweeped, so might disappear):

ftp://ftp.arista.com/data/ar/crossgcc/arista-centos7-5-gcc6-3-1-glibc2-17.deb
ftp://ftp.arista.com/data/ar/crossgcc/arista-centos7.5-gcc6.3.1-glibc2.17-1.0-0.i686.rpm
ftp://ftp.arista.com/data/ar/crossgcc/arista-centos7-5-gcc6-5-0-glibc2-17.deb
ftp://ftp.arista.com/data/ar/crossgcc/arista-centos7.5-gcc6.5.0-glibc2.17-1.0-0.i686.rpm

But the official place to get the cross-compiler is https://www.arista.com/en/support/software-download, so having an account and signing an EULA is de rigueur. Click open the "EOS SDK" folder, then the "Cross-Compiler" folder.

Newly, the 3 last versions of the cross-compiler have been uploaded to GitHub, and you can conveniently wget them from there:

https://github.com/aristanetworks/EosSdk-cross-compiler/releases

With the latest versions being:

https://github.com/aristanetworks/EosSdk-cross-compiler/releases/download/v4.25.0/arista-centos7-5-gcc8-4-0-glibc2-17.deb
https://github.com/aristanetworks/EosSdk-cross-compiler/releases/download/v4.32.0/arista-gcc11.deb

Docker based Cross-Compiler

Later EosSdk versions also come with a Dockerfile to build a docker image that contains the EOS cross-compiler. You can use Dockerfile to build a docker image and then instantiate a container from that image. When running the container, map the parts of your filesystem that contain your EosSdk application code into the container so you can build your EosSdk app using the tool-chain from the container (which is exactly the same tool-chain that compiled the EOS applications/libs on your switch).

Might be more difficult to insert into a build system though, since it involves more than just setting CC=/opt/arista/gcc11/bin/gcc, but also does not require root privilege to install the cross-compiler into /opt. And creating a docker image per EosSdk release is more flexible than replacing /opt/arista/gcc11/include/eos and /opt/arista/gcc11/usr/lib/libeos.so with the EosSdk version du jour.

It looks something like this:

# Build the image, instantiate a container, get into the container
# Assuming code-to-be-compiled is in /src on the host (and map that to /src in the container).
wget -O Dockerfile.EOS https://raw.githubusercontent.com/aristanetworks/EosSdk/master/Dockerfile
docker build . -f Dockerfile.EOS -t EOS-xgcc
docker run -v /src:/src -itd --name EOS-xgcc-1  EOS-xgcc
docker exec -it EOS-xgcc-1 /bin/bash

# Now build HelloWorld (HelloWorld.cpp is from EosSdk-stubs*.tar.gz, under /examples)
cd /src
g++      -g -o /src/build/HelloWorld.o -c /src/HelloWorld.cpp
g++      -g -o /src/build/HelloWorld      /src/HelloWorld.o  -leos -lrt
g++ -m32 -g -o /src/build/HelloWorld.o -c /src/HelloWorld.cpp
g++ -m32 -g -o /src/build/HelloWorld32    /src/HelloWorld.o  -leos -lrt
exit

# Test it
scp /src/build/HelloWorld root@eosRouter:/tmp
ssh eosRouter
eosRouter(config)#daemon HelloWorld
eosRouter(config-daemon-HelloWorld)#exe /tmp/HelloWorld
eosRouter(config-daemon-HelloWorld)#no shut
eosRouter(config-daemon-HelloWorld)#option name value AristaCustomer
eosRouter(config-daemon-HelloWorld)#show daemon HelloWorld | grep greeting
greeting       Hello AristaCustomer!
eosRouter(config-daemon-HelloWorld)#
exit

# cleanup
docker stop EOS-xgcc-1
docker rm EOS-xgcc-1
docker rmi EOS-xgcc