NDK Architecture
SR Linux provides a Software Development Kit (SDK) to assist operators with developing agents that run alongside SR Linux applications. This SDK is named NetOps Development Kit, or NDK for short.
NDK allows operators to write applications (a.k.a agents) that deeply integrate with other native SR Linux applications. The deep integration is the courtesy of the NDK gRPC service that enables custom applications to interact with other SR Linux applications via Impart Database (IDB).
In Fig. 1, custom NDK applications app-1
and app-2
interact with other SR Linux subsystems via gRPC-based NDK service.
In addition to the traditional tasks of reading and writing configuration, NDK-based applications gain low-level access to the SR Linux system. For example, these apps can install FIB routes or listen to LLDP events.
gRPC & Protocol buffers#
NDK uses gRPC - a high-performance, open-source framework for remote procedure calls.
gRPC framework by default uses Protocol buffers as its Interface Definition Language as well as the underlying message exchange format.
Info
Protocol buffers are Google's language-neutral, platform-neutral, extensible mechanism for serializing structured data – think XML, but smaller, faster, and simpler. You define how you want your data to be structured once, then you can use special generated source code to easily write and read your structured data to and from a variety of data streams and using a variety of languages.
In gRPC, a client application can directly call a method on a server application on a different machine as if it were a local object. As in many RPC systems, gRPC is based around the idea of defining a service, specifying the methods that can be called remotely with their parameters and return types.
On the server side, the server implements this interface and runs a gRPC server to handle client calls. On the client side, the client provides the same methods as the server.
Leveraging gRPC and protobufs provides some substantial benefits for NDK users:
- Language neutrality: NDK apps can be written in any language for which protobuf compiler exists. Go, Python, C, Java, Ruby, and more languages are supported by Protocol buffers enabling SR Linux users to write apps in the language of their choice.
- High-performance: protobuf-encoded messaging is an efficient way to exchange data in a client-server environment. Applications that consume high-volume streams of data (for example, route updates) benefit from an efficient and fast message delivery enabled by protobuf.
- Backwards API compatibility: a protobuf design property of using IDs for data fields makes it possible to evolve API over time without ever breaking backward compatibility. Old clients will still be able to consume data stored in the original fields, whereas new clients will benefit from accessing data stored in the new fields.
NDK Service#
NDK provides a collection of gRPC services, each of which enables custom applications to interact with a particular subsystem on an SR Linux OS, delivering a high level of integration and extensibility.
With this architecture, NDK agents act as gRPC clients that execute remote procedure calls (RPC) on a system that implements a gRPC server.
On SR Linux, ndk_mgr
is the application that runs the NDK gRPC server. Fig 3. shows how custom agents interact via gRPC with NDK, and NDK executes the remote procedure and communicates with other system applications through IDB and pub/sub interface to return the result of the RPC to a client.
As a result, custom applications are able to communicate with the native SR Linux apps as if they were shipped with SR Linux OS.
Proto files#
NDK services, underlying RPCs, and messages are defined in .proto
files. These files are used to generate language bindings essential for the NDK apps development process and serve as the data modeling language for the NDK itself.
The source .proto
files for NDK are open and published in nokia/srlinux-ndk-protobufs
repository. Anyone can clone this repository and explore the NDK gRPC services or build language bindings for the programming language of their choice.
Additionally users can find the NDK proto files on SR Linux filesystem by the /opt/srlinux/protos/ndk
path3.
Documentation#
Although the proto files are human-readable, it is easier to browse the NDK services using the generated documentation that we keep in the same nokia/srlinux-ndk-protobufs
repo. The HTML document is provided in the readme file that appears when a user selects a tag that matches the NDK release version1.
The generated documentation provides the developers with a human-readable reference of all the services, messages, and types that comprise the NDK service.
Operations flow#
Regardless of the language in which the agents are written, at a high level, the following flow of operations applies to all agents when interacting with the NDK service:
- Establish gRPC channel with NDK manager and instantiate an NDK client
- Register the agent with the NDK manager
- Register notification streams for different types of NDK services (config, lldp, interface, etc.)
- Start streaming notifications
- Handle the streamed notifications
- Update agent's state data if needed
- Exit gracefully if required
To better understand the steps each agent undergoes, we will explain them in a language-neutral manner. For language-specific implementations, read the "Developing with NDK" chapter.
gRPC Channel and NDK Manager Client#
NDK agents communicate with gRPC based NDK service by invoking RPCs and handling responses. An RPC generally takes in a client request message and returns a response message from the server.
A gRPC channel must be established before communicating with the NDK manager application running on SR Linux2. NDK server runs on port 50053
; agents which are installed on SR Linux OS use localhost:50053
socket to establish the gRPC channel.
Once the gRPC channel is set up, a gRPC client (often called stub) needs to be created to perform RPCs. Each gRPC service needs to have its own client. In NDK, the SdkMgrService
service is the first service that agents interact with, therefore, users first need to create the NDK Manager Client (Mgr Client on diagram) that will be able to call RPCs defined for SdkMgrService
.
Agent registration#
Agent must be first registered with SRLinux NDK by calling AgentRegister
RPC of SdkMgrService
. Initial agent state is created during the registration process.
An AgentRegistrationResponse
is returned (omitted in Fig. 4) with the status of the registration process.
Registering notifications#
Agents interact with other services like Network Instance, Config, LLDP, BFD by subscribing to notification updates from these services.
Before subscribing to a notification stream of a certain service the subscription stream needs to be created. To create it, a client of SdkMgrService
calls NotificationRegister
RPC with NotificationRegistrationRequest
field Op
set to Create
and other fields absent.
Info
NotificationRegistrationRequest
message's field Op
(for Operation) may have one of the following values:
Create
creates a subscription stream and returns aStreamId
that is used when adding subscriptions with theAddSubscription
operation.Delete
deletes the existing subscription stream that has a particularSubId
.AddSubscription
adds a subscription. The stream will now be able to stream notifications of that subscription type (e.g., Intf, NwInst, etc).DeleteSubscription
deletes the previously added subscription.
When Op
field is set to Create
, NDK Manager responds with NotificationRegisterResponse
message with stream_id
field set to some value. The stream has been created, and the subscriptions can be added to the created stream.
To subscribe to a certain service notification updates another call of NotificationRegister
RPC is made with the following fields set:
stream_id
set to an obtained value from theNotificationRegisterResponse
Op
is set toAddSubscription
- one of the
subscription_types
is set according to the desired service notifications. For example, if notifications from theConfig
service are of interest, thenconfig
field of typeConfigSubscriptionRequest
is set.
NotificationRegisterResponse
message follows the request and contains the same stream_id
but now also the sub_id
field - subscription identifier. At this point agent successfully indicated its desire to receive notifications from certain services, but the notification streams haven't been started yet.
Streaming notifications#
Requesting applications to send notifications is done by interfacing with SdkNotificationService
. As this is another gRPC service, it requires its own client - Notification client.
To initiate streaming of updates based on the agent subscriptions the Notification Client executes NotificationStream
RPC which has NotificationStreamRequest
message with stream_id
field set to the ID of a stream to be used. This RPC returns a stream of NotificationStreamResponse
, which makes this RPC of type "server streaming RPC".
Server-streaming RPC
A server-streaming RPC is similar to a unary RPC, except that the server returns a stream of messages in response to a client's request. After sending all its messages, the server's status details (status code and optional status message) and optional trailing metadata are sent to the client. This completes processing on the server side. The client completes once it has all the server's messages.
NotificationStreamResponse
message represents a notification stream response that contains one or more notifications. The Notification
message contains one of the subscription_types
notifications, which will be set in accordance to what notifications were subscribed by the agent.
In our example, we sent ConfigSubscriptionRequest
inside the NotificationRegisterRequest
, hence the notifications that we will get back for that stream_id
will contain ConfigNotification
messages inside Notification
of a NotificationStreamResponse
.
Handling notifications#
The agent handles the stream of notifications by analyzing which concrete type of notification was read from the stream. The Server streaming RPC will provide notifications till the last available one; the agent then reads out the incoming notifications and handles the messages contained within them.
The handling of notifications is done when the last notification is sent by the server. At this point, the agent may perform some work on the received data and, if needed, update the agent's state if it has one.
Updating agent's state data#
Each agent may keep state and configuration data modeled in YANG. When an agent needs to set/update its own state data (for example, when it made some calculations based on received notifications), it needs to use SdkMgrTelemetryService
and a corresponding client.
The state that an agent intends to have will be available for gNMI telemetry, CLI access, and JSON-RPC retrieval, as it essentially becomes part of the SR Linux state.
Updating or initializing agent's state with data is done with TelemetryAddOrUpdate
RPC that has a request of type TelemetryUpdateRequest
that encloses a list of TelemetryInfo
messages. Each TelemetryInfo
message contains a key
field that points to a subtree of agent's YANG model that needs to be updated with the JSON data contained within data
field.
Exiting gracefully#
When an agent needs to stop its operation and be removed from the SR Linux system, it needs to be unregistered by invoking AgentUnRegister
RPC of the SdkMgrService
. The gRPC connection to the NDK server needs to be closed.
When unregistered, the agent's state data will be removed from SR Linux system and will no longer be accessible to any of the management interfaces.