Skip to content

Developing Go NDK applications with Bond#

The srl-labs/bond package is a helper Go package that abstracts the low-level NDK API and assists in the development of the NDK applications. It is a wrapper around the NDK gRPC services with utility functions that were designed to provide a more pleasant development experience.

We recommend users to develop their apps with the bond package and resort to the barebones NDK API only when the bond package is not sufficient.

The development environment that we will use in this tutorial is covered in the introduction to the Go NDK development guide.

Deploying the lab#

Before taking a deep dive into the code, let's deploy the greeter app to SR Linux using containerlab and see how it works.

Containerlab for NDK

When developing NDK applications, it is important to have a lab environment to test the application. The lab environment should be as close as possible to the production environment and also be easy to spin up and tear down.

The Containerlab tool is a perfect fit for this purpose. Containerlab makes it easy to create a personal lab environment composed of network devices and connected by virtual links. We are going to use Containerlab to create a lab environment for the greeter NDK application development down the road.

It all starts with cloning the srl-labs/ndk-greeter-go repo:

git clone https://github.com/srl-labs/ndk-greeter-go.git && \
cd ndk-greeter-go

Note

Containerlab v0.57.2 version and SR Linux 24.3.3 are used in this tutorial. Users are advised to use these version to have the same outputs as in this tutorial.

Newer versions of Containerlab and SR Linux should work as well, but the outputs might be slightly different.

And then running the deployment script:

./run.sh deploy-all #(1)!
  1. deploy-all script builds the greeter app, deploys a containerlab topology file, and installs the app on the running SR Linux node.

It won't take you longer than 30 seconds to get the greeter app up and running on a freshly deployed lab. Type ssh greeter and let's configure our greeter app:

❯ ssh greeter #(1)!
Warning: Permanently added 'greeter' (ED25519) to the list of known hosts.

Welcome to the srlinux CLI.
Type 'help' (and press <ENTER>) if you need any help using this.

--{ running }--[  ]--
A:greeter#
  1. Containerlab injects host routes and SSH config on your system to allow you to connect to the lab nodes using only its name.

Once connected to the greeter SR Linux node, let's configure the app:

--{ running }--[  ]--
A:greeter# enter candidate

--{ candidate shared default }--[  ]--
A:greeter# greeter

--{ candidate shared default }--[ greeter ]--
A:greeter# name "Learn SR Linux Reader"

--{ * candidate shared default }--[ greeter ]--
A:greeter# commit stay
All changes have been committed. Starting new transaction.

Now that we've set the name value, let's verify that the name is indeed set in the candidate configuration and running datastore:

--{ + candidate shared default }--[ greeter ]--
A:greeter# info from running
    name "Learn SR Linux Reader"

Look at that, the greeting value is not there. That's because the greeting is a state leaf, it is only present in the state datastore. Let's check it out, while we're in the /greeter context we can use info from state command to get the state of the current context:

--{ + candidate shared default }--[ greeter ]--
A:greeter# info from state
    name "Learn SR Linux Reader"
    greeting "πŸ‘‹ Hi Learn SR Linux Reader, I am SR Linux and my uptime is 108h33m7s!"

As advertised, the greeter app greets us with a message that includes the name value we've set, and the uptime of the SR Linux node. Should you change the name value and commit the configuration, you will see the new greeting message.

Now let's go and see how this app is written, starting with the project structure.

Project structure#

The project structure is a matter of personal preference. There are no strict rules on how to structure a Go project. However, there are some best practices we can enforce making the NDK project structure more consistent and easier to understand.

This is the project structure used in this tutorial:

❯ tree
.
β”œβ”€β”€ LICENSE
β”œβ”€β”€ README.md
β”œβ”€β”€ build #(1)!
β”œβ”€β”€ go.mod
β”œβ”€β”€ go.sum
β”œβ”€β”€ goreleaser.yml #(2)!
β”œβ”€β”€ greeter #(3)!
β”œβ”€β”€ greeter.yml #(4)!
β”œβ”€β”€ lab
β”‚   └── greeter.clab.yml #(5)!
β”œβ”€β”€ logs
β”‚   β”œβ”€β”€ greeter #(6)!
β”‚   └── srl #(7)!
β”œβ”€β”€ main.go #(8)!
β”œβ”€β”€ nfpm.yml #(9)!
β”œβ”€β”€ run.sh #(10)!
└── yang #(11)!
    └── greeter.yang
  1. Directory to store build artifacts. This directory is ignored by Git.
  2. Goreleaser config file to build and publish the NDK application. Usually run via CI/CD pipeline.
  3. Directory to store the greeter package source code. This is where the application logic is implemented.
  4. Application configuration file.
  5. Containerlab topology file to assist with the development and testing of the NDK application.
  6. Directory with the application log file.
  7. Directory with the SR Linux log directory to browse the SR Linux applications logs.
  8. Main executable file.
  9. nFPM configuration file to build deb/rpm packages locally.
  10. Script to orchestrate lab environment and application lifecycle.
  11. Directory with the application YANG modules.

Besides short descriptions, we will cover the purpose of each file and directory in the following sections when we start to peel off the layers of the greeter NDK application.

Application Configuration#

As was mentioned before, in order for the NDK application to be installed on the SR Linux node, it needs to be registered with the Application Manager. The Application Manager is a service that manages the lifecycle of all applications, native and custom ones.

The Application Manager uses the application configuration file to onboard the application. Our greeter app comes with the following greeter.yml configuration file1:

greeter:
  path: /usr/local/bin
  launch-command: {{if ne (env.Getenv "NDK_DEBUG") "" }}{{ "/debug/dlv --listen=:7000"}}{{ if ne (env.Getenv "NDK_DEBUG") "" }} {{ "--continue --accept-multiclient" }}{{ end }} {{ "--headless=true --log=true --api-version=2 exec"}} {{ end }}greeter
  version-command: greeter --version
  failure-action: wait=10
  config-delivery-format: json
  yang-modules:
    names:
      - greeter
    source-directories:
      - /opt/greeter/yang

Refer to the application configuration section to better understand what each field means. Application Manager will look for the greeter binary in the /usr/local/bin/ directory when starting our application.

Application YANG#

Every SR Linux application needs its own schema, this is what makes SR Linux a 100% modelled system. Even the custom, user-defined application must have a schema so that it can be onboarded to the SR Linux NOS.

We have covered the YANG module structure in the architecture section, here is the resulting YANG module of our greeter application.

yang/greeter.yang
module greeter {
    yang-version 1.1;
    namespace "srlinux.dev/ndk/greeter";
    prefix "srl-labs-greeter";

    description
        "greeter YANG module";

    revision "2023-11-21" {
        description
            "initial release";
    }

    container greeter {
        leaf name {
            description
                "name of the person to greet";
            type string;
        }

        leaf greeting {
            description
                "greeting the greeter will say";
            type string;
            config false;
        }
    }
}

With this YANG module loaded in SR Linux, we will extend SR Linux schema with the following nodes:

module: greeter
  +--rw greeter
     +--rw name     string
     +--r  greeting string

Now that we have our YANG module and the application config file, we can start looking into the application's entrypoint - the main function.

Main function


  1. Don't mind a little template magic, it is for the debugging capabilities of the greeter app. 

Comments