• Pushing a Python Script to Devices Using CVP (SWIX)

 
 
Print Friendly, PDF & Email

Introduction

In a previous article, I discussed how to push a simple Python script to managed devices with CVP through a configlet. In this article, I will show how to create and install an extension with the same python hello script through an image bundle in CVP. This method is useful if the script is very long and/or has dependencies.

 

Packaging the Script Into an RPM

First, we will need to build an RPM to package the python script. We can do this from a linux host with RPMBuild. I also highly recommend taking a look at this guide for building RPMs and SWIX files. I will still provide my steps but the other guide provides additional comments that may be useful to you.

Throughout the process I will use printf to generate files. You can transfer files to the environment or create them by your own preferential means. I just find it easier than using vi or nano. If using printf, please be sure to escape characters as needed and always double check the file contents.

To start, we will need to create the directories and files required to build the RPM.

cd $HOME
mkdir -p ~/rpmbuild/SPECS ~/rpmbuild/SOURCES
printf "%s\n" '$%_topdir /home/eosdev/rpmbuild' >> ~/.rpmmacros
printf "%s\n" '$%_sourcedir /home/eosdev/rpmbuild/SOURCES' >> ~/.rpmmacros

If using printf, check the file contents to ensure they are correct.

cat ~/.rpmmacros
%_topdir /home/eosdev/rpmbuild
%_sourcedir /home/eosdev/rpmbuild/SOURCES

Create a directory for your script files.

mkdir ~/source

Copy all script files to ~/source/ and in the correct structure if there are any dependencies.

cat hello.py
#!/usr/bin/env python
print('Hello World')
cp ~/hello.py ~/source/

Create a tar of the source folder and append a version. This version will be used in the RPM build process by the spec file and will need to match.

tar -cvf hello-1.0.tar source/

Copy the tar to the ~/rpmbuild/SOURCES directory. The rpmbuild/SOURCES directory was created in the first step.

cp hello-1.0.tar ~/rpmbuild/SOURCES

Create the spec file in ~/rpmbuild/SPECS directory that was created in the first step. This file contains the instructions for creating the RPM file. If desired you can paste the below text to create the file, if you don’t want to file transfer. Take care to replace your own values and escape characters in the string as needed.

printf "%s\n" 'Summary: hello' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'Name: hello' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'Version: 1.0' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'Release: 1' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '# Choose your favorite license and use it below' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'License: Arista Networks' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'Group: EOS/Extension' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'Source0: hello-%{version}.tar' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'BuildArch: noarch' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '# If desired, you can tie your extension to a specific set of releases by' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '# specifying the range of releases it supports.' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '#Requires: Eos-release >= 2:4.8.0' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '#Conflicts: Eos-release >= 2:4.9.0' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '%description' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'Description of my hello extension' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '%prep' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '%setup -q -n source' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '%build' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '%install' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '# In the install step we copy all of the files from hello-1.0.tar' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '# to their appropriate location and structure for any dependancies' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'mkdir -p $RPM_BUILD_ROOT/mnt/flash/' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" 'cp hello.py $RPM_BUILD_ROOT/mnt/flash/' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '%files' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '# Set permissions as needed' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '%defattr(-,root,root,-)' >> ~/rpmbuild/SPECS/hello.spec
printf "%s\n" '%attr(0755,root,root) /mnt/flash/hello.py' >> ~/rpmbuild/SPECS/hello.spec
File contents without printf:
Summary: hello
Name: hello
Version: 1.0
Release: 1
# Choose your favorite license and use it below
License: Arista Networks
Group: EOS/Extension
Source0: hello-%{version}.tar
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
BuildArch: noarch

# If desired, you can tie your extension to a specific set of releases by
# specifying the range of releases it supports.
#Requires: Eos-release >= 2:4.8.0
#Conflicts: Eos-release >= 2:4.9.0

%description
Description of my hello extension

%prep
%setup -q -n source

%build

%install
# In the install step we copy all of the files from hello-1.0.tar
# to their appropriate location and structure for any dependencies
mkdir -p $RPM_BUILD_ROOT/mnt/flash/
cp hello.py $RPM_BUILD_ROOT/mnt/flash/

%files
# Set permissions as needed
%defattr(-,root,root,-)
%attr(0755,root,root) /mnt/flash/hello.py

Note: Ensure the file name in Source0 matches the tar that was created earlier.

Create the RPM file.

cd ~/rpmbuild/SPECS/
rpmbuild -ba hello.spec
Output of command:
eosdev@bdaf384a02ae:~/rpmbuild/SPECS$ rpmbuild -ba hello.spec
Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.qNvtJH
+ umask 022
+ cd /home/eosdev/rpmbuild/BUILD
+ cd /home/eosdev/rpmbuild/BUILD
+ rm -rf source
+ /bin/tar -xof /home/eosdev/rpmbuild/SOURCES/hello-1.0.tar
+ cd source
+ /bin/chmod -Rf a+rX,u+w,g-w,o-w .
+ exit 0
Executing(%build): /bin/sh -e /var/tmp/rpm-tmp.I8AkdO
+ umask 022
+ cd /home/eosdev/rpmbuild/BUILD
+ cd source
+ exit 0
Executing(%install): /bin/sh -e /var/tmp/rpm-tmp.bfwrHU
+ umask 022
+ cd /home/eosdev/rpmbuild/BUILD
+ cd source
+ mkdir -p /home/eosdev/rpmbuild/BUILDROOT/hello-1.0-1.x86_64/mnt/flash/
+ cp hello.py /home/eosdev/rpmbuild/BUILDROOT/hello-1.0-1.x86_64/mnt/flash/
+ /usr/lib/rpm/brp-compress
+ /usr/lib/rpm/brp-strip /usr/bin/strip
+ /usr/lib/rpm/brp-strip-static-archive /usr/bin/strip
+ /usr/lib/rpm/brp-strip-comment-note /usr/bin/strip /usr/bin/objdump
Processing files: hello-1.0-1.noarch
Provides: hello = 1.0-1
Requires(rpmlib): rpmlib(CompressedFileNames) <= 3.0.4-1 rpmlib(FileDigests) <= 4.6.0-1 rpmlib(PayloadFilesHavePrefix) <= 4.0-1
Checking for unpackaged file(s): /usr/lib/rpm/check-files /home/eosdev/rpmbuild/BUILDROOT/hello-1.0-1.x86_64
Wrote: /home/eosdev/rpmbuild/SRPMS/hello-1.0-1.src.rpm
Wrote: /home/eosdev/rpmbuild/RPMS/noarch/hello-1.0-1.noarch.rpm
Executing(%clean): /bin/sh -e /var/tmp/rpm-tmp.8TNIl1
+ umask 022
+ cd /home/eosdev/rpmbuild/BUILD
+ cd source
+ /bin/rm -rf /home/eosdev/rpmbuild/BUILDROOT/hello-1.0-1.x86_64
+ exit 0

We can see it was written to /home/eosdev/rpmbuild/RPMS/noarch/hello-1.0-1.noarch.rpm

 

Creating the SWIX and Installing Locally to Test

A SWIX is an EOS extension bundle file. It can be used to bundle one RPM or many. For our case, we will just be using the hello RPM that was just created. We will also need a manifest file to give brief instructions for SWIX creation. I will run through a local installation for testing before moving it to CVP.

Let’s start by copying the RPM file to EOS. I’m using a mounted file share in cEOS so no need to SCP or FTP.

bash-4.2# cd /mnt/share/
bash-4.2# ls
hello-1.0-1.noarch.rpm

Generate a sha1sum for the RPM.

bash-4.2# for output in $(ls *.rpm); do sha1sum $output; done | awk '{print $2"-sha1: "$1}'
hello-1.0-1.noarch.rpm-sha1: 8cce6afa576b6a6a76738efbb9e95440de306b04

Create SWIX manifest.txt file and copy over to the same directory as the RPM.

Manifest.txt contents:
format: 1 
primaryRpm: hello-1.0-1.noarch.rpm
hello-1.0-1.noarch.rpm-sha1: 8cce6afa576b6a6a76738efbb9e95440de306b04

Ensure both files are in the same directory.

Arista-cEOS#bash
bash-4.2# cd /mnt/share/
bash-4.2# ls
hello-1.0-1.noarch.rpm manifest.txt

Create the SWIX.

bash-4.2# swix create hello-1.0-1.noarch.swix hello-1.0-1.noarch.rpm
adding: manifest.txt (stored 0%)
adding: hello-1.0-1.noarch.rpm (stored 0%)

bash-4.2# ls
hello-1.0-1.noarch.rpm hello-1.0-1.noarch.swix manifest.txt

Copy it the SWIX to /mnt/flash/ directory.

bash-4.2# cp hello-1.0-1.noarch.swix /mnt/flash/

Enter into CLI mode and check the directory of install. Our script is not there as expected.

bash-4.2# FastCli -p 15
Arista-cEOS#dir flash:
Directory of flash:/

-rw- 231 Jan 11 18:48 AsuFastPktTransmit.log
drwx 4096 Jan 11 18:48 Fossil
-rw- 142 Jan 11 18:48 SsuRestore.log
-rw- 142 Jan 11 18:48 SsuRestoreLegacy.log
drwx 4096 Jan 11 18:48 debug
drwx 4096 Jan 11 18:48 fastpkttx.backup
-rw- 6604 Jan 11 19:28 hello-1.0-1.noarch.swix
-rw- 180 Jan 11 18:48 kickstart-config
drwx 4096 Jan 11 19:29 persist
drwx 4096 Jan 11 18:49 schedule
-rw- 13 Nov 30 2020 zerotouch-config

Copy SWIX to extension directory and install the SWIX.

Arista-cEOS#copy flash:hello-1.0-1.noarch.swix extension:hello-1.0-1.noarch.swix
Copy completed successfully.

Arista-cEOS#extension hello-1.0-1.noarch.swix
Agents to be restarted:
Note: no agents to retart

Arista-cEOS#show extensions
Name Version/Release Status Extension
---------------------------- -------------------- ----------- ---------
hello-1.0-1.noarch.swix 1.0/1 A, I 1


A: available | NA: not available | I: installed | NI: not installed | F: forced
S: valid signature | NS: invalid signature
The extensions are stored on internal flash (flash:)


After the SWIX was installed, we can see that hello.py was installed in the flash directory. Testing the script shows the correct output and the syntax is also correct.

Arista-cEOS#dir flash:
Directory of flash:/

-rw- 231 Jan 11 18:48 AsuFastPktTransmit.log
drwx 4096 Jan 11 18:48 Fossil
-rw- 142 Jan 11 18:48 SsuRestore.log
-rw- 142 Jan 11 18:48 SsuRestoreLegacy.log
drwx 4096 Jan 11 18:48 debug
drwx 4096 Jan 11 18:48 fastpkttx.backup
-rw- 6604 Jan 11 19:28 hello-1.0-1.noarch.swix
-rwx 43 Jan 8 22:45 hello.py
-rw- 180 Jan 11 18:48 kickstart-config
drwx 4096 Jan 11 19:29 persist
drwx 4096 Jan 11 18:49 schedule
-rw- 13 Nov 30 2020 zerotouch-config


Arista-cEOS#bash /mnt/flash/hello.py
Hello World
Arista-cEOS#bash cat /mnt/flash/hello.py
#!/usr/bin/env python
print('Hello World')

Creating an Image Bundle in CVP and Assigning to a Device

Now that we know that the local install works, we can push it through CVP to devices at large scale.

The SWIX will be installed on managed switches through an image bundle. We will bundle the SWIX with the current running EOS version which will result in skipping the image upgrade. We need to make sure that the name of the .swi image file matches between cvp, flash:, and the boot variable before proceeding.

vEOS-50#show version | i image
Software image version: 4.24.2.3F

vEOS-50#dir flash:
Directory of flash:/

-rw- 693 Jan 22 17:14 AsuFastPktTransmit.log
drwx 4096 Jan 13 23:07 Fossil
-rw- 426 Jan 22 17:14 SsuRestore.log
-rw- 426 Jan 22 17:14 SsuRestoreLegacy.log
-rw- 34 Jan 14 18:37 boot-config
-rw- 39 Jan 14 18:39 boot-extensions
drwx 4096 Jan 13 23:30 debug
drwx 4096 Jan 13 23:07 fastpkttx.backup
drwx 16384 Oct 1 2020 lost+found
drwx 4096 Jan 26 20:18 persist
drwx 4096 Jan 13 23:15 schedule
-rw- 1593 Jan 18 17:28 startup-config
-rw- 446693362 Oct 1 2020 vEOS-lab-4.24.2.3F.swi
-rw- 13 Jan 13 23:56 zerotouch-config

vEOS-50#show boot
Software image: flash:/vEOS-lab-4.24.2.3F.swi
Console speed: (not set)
Aboot password (encrypted): (not set)
Memory test iterations: (not set)
vEOS-50#

Once we have verified the names match we can create the image bundle. We will create the image bundle from Provisioning>Image Management. Click on the + icon to create a new bundle. Name it and select Existing Images icon (1), check the correct image that is running on the switch(2), and add(3).

Next, select the Folder icon to upload the SWIX since it doesn’t already exist in CVP.

The completed image bundle should look like the below screen snip.

Now we need to assign the image bundle to the desired device. This is done from Provisioning>Network Provisioning. Right-click the device and select Manage>Image Bundle. Checkbox the newly created bundle and click Update.

 

The switch should now show a green outline.

Save and a new task will be generated that will be used in a change control.

Let’s verify that the extension does not already exist before the image bundle is pushed by CVP.

vEOS-50#show extensions 
! No extensions are available
The extensions are stored on internal flash (flash:)

vEOS-50#show clock 
Tue Jan 26 21:36:54 2021
Timezone: UTC
Clock source: local

Finally, create the change control from the task that was generated and execute it.

Verifying After the SWIX Has Been Installed

Now that the change has completed, let’s check on the switch from the CLI. If it did not complete successfully, make sure CVP isn’t trying to push the same version down to the switch from the change control logs. Double-check the version and correct the .swi file names and boot variables to match if needed.

vEOS-50#show clock 
Tue Jan 26 21:38:24 2021
Timezone: UTC
Clock source: local

vEOS-50#dir flash:
Directory of flash:/

-rw- 693 Jan 22 17:14 AsuFastPktTransmit.log
drwx 4096 Jan 13 23:07 Fossil
-rw- 426 Jan 22 17:14 SsuRestore.log
-rw- 426 Jan 22 17:14 SsuRestoreLegacy.log
-rw- 34 Jan 14 18:37 boot-config
-rw- 39 Jan 26 21:37 boot-extensions
drwx 4096 Jan 13 23:30 debug
drwx 4096 Jan 13 23:07 fastpkttx.backup
-rwx 43 Jan 11 19:41 hello.py
drwx 16384 Oct 1 2020 lost+found
drwx 4096 Jan 26 21:38 persist
drwx 4096 Jan 13 23:15 schedule
-rw- 1644 Jan 26 20:33 startup-config
-rw- 446693362 Oct 1 2020 vEOS-lab-4.24.2.3F.swi
-rw- 13 Jan 13 23:56 zerotouch-config

4093313024 bytes total (3024666624 bytes free)

vEOS-50#show extensions 
Name Version/Release Status Extension
---------------------------- -------------------- ----------- ---------
hello-1.0-1.noarch.swix 1.0/1 A, I 1


A: available | NA: not available | I: installed | NI: not installed | F: forced
S: valid signature | NS: invalid signature
The extensions are stored on internal flash (flash:)

Let’s test the script that was installed with the SWIX.

vEOS-50#bash /mnt/flash/hello.py
Hello World
vEOS-50#bash cat /mnt/flash/hello.py
#!/usr/bin/env python
print('Hello World')

Overall, this is a lengthy process but it could be very useful for large scale deployments. However, it is quick to generate RPMs/SWIX files if an environment and a EOS test host have already been previously setup.

 

Follow

Get every new post on this blog delivered to your Inbox.

Join other followers: