packagegreetertypeAppstruct{NamestringAppIDuint32// configState holds the application configuration and state.configState*ConfigState// configReceivedCh chan receives the value when the full config// is received by the stream client.configReceivedChchanstruct{}gRPCConn*grpc.ClientConnlogger*zerolog.LoggerretryTimeouttime.DurationgNMITarget*target.Target// NDK Service clientsSDKMgrServiceClientndk.SdkMgrServiceClientNotificationServiceClientndk.SdkNotificationServiceClientTelemetryServiceClientndk.SdkMgrTelemetryServiceClient}
The App struct is the main structure of the greeter application. It is way more complex than the app struct in the Bond-workflow, and that is because our app will have to do some leg work, that is done by Bond otherwise.
It holds the application config, state, logger instance, gNMI client and the NDK clients to communicate with the NDK services.
As stated in the NDK Operations, the first thing we need to do is to connect to the NDK socket. This is what we do with the helper connect function inside the NewApp constructor:
The connection is made to the NDK manager's unix socket using unsecured transport. The insecure transport is justifiable in this case as the NDK manager is running on the same host as the application.
The NDK service collection allows your application to receive notifications from different SR Linux apps and services. But when it comes to changing SR Linux configuration or reading it your application needs to utilize one of the management interfaces.
Since it is very common to have the application either reading existing configuration or changing it, we wanted our greeter app to demonstrate how to do it.
Note
When your application needs to read its own config, it can do so by leveraging the Config notifications and NDK Notification Client. It is only when the application needs to configure SR Linux or read the configuration outside of its own config that it needs to use the management interfaces.
When the greeter app creates the greeting message it uses the following template:
👋Hi${name},IamSRLinuxandmyuptimeis${uptime}!
Since name value belongs to the greeter' application config, we can get this value later with the help of the NDK Notification Client. But the last-boot-time value is not part of the greeter app config and we need to get it from the SR Linux configuration. This is where we need greeter to use the management interface.
We opted to use the gNMI interface in this tutorial powered by the awesome gNMIc project. gNMIc project has lots of subcomponents revolving around gNMI, but we are going to use its API package to interact with the SR Linux's gNMI server.
In the NewApp function right after we created the NDK clients we create the gNMI client:
The newGNMITarget function creates the gNMI Target using the gnmic API package. We provide the gRPC server unix socket as the address to establish the connection as well as hardcoded default credentials for SR Linux.
greeter/app.go
const(ndkSocket="unix:///opt/srlinux/var/run/sr_sdk_service_manager:50053"grpcServerUnixSocket="unix:///opt/srlinux/var/run/sr_gnmi_server"AppName="greeter")funcnewGNMITarget(ctxcontext.Context)(*target.Target,error){// create a targettg,err:=api.NewTarget(api.Name("srl"),api.Address(grpcServerUnixSocket),api.Username("admin"),api.Password("NokiaSrl1!"),api.Insecure(true),)iferr!=nil{returnnil,err}// create a gNMI clienterr=tg.CreateGNMIClient(ctx)iferr!=nil{returnnil,err}returntg,nil}
gNMI Configuration on SR Linux
When you're using Containerlab-based lab environment, the gNMI server is configured to run over the unix socket as well, but when you run the greeter app in a production environment, you will have to make sure the relevant configuration is in place.
Once the target is created we create the gNMI client for it and returning the pointer to the target struct.
Next task is to register the agent with the NDK manager. At this step NDK initializes the state of our agent, creates the IDB tables and assigns an ID to our application.
Registration is carried out by calling the AgentRegister function of the NDK manager client.
We pass the empty &ndk.AgentRegistrationRequest{} as this is all we need to do to register the agent.
The AgentRegister function returns the AgentRegistrationResponse that contains the agent ID assigned by the NDK manager. We store this response in a variable, since we will need it later.
The last bit is to initialize the structure for our app's config and state. This struct will hold the configured name, the computed greeting value. Here is how our ConfigState struct looks:
greeter/config.go
typeConfigStatestruct{// Name is the name to use in the greeting.Namestring`json:"name,omitempty"`// Greeting is the greeting message to be displayed.Greetingstring`json:"greeting,omitempty"`}