Skip to content

Commit

Permalink
added notif stream doc
Browse files Browse the repository at this point in the history
  • Loading branch information
hellt committed Dec 2, 2023
1 parent dd00456 commit 256424a
Showing 1 changed file with 65 additions and 19 deletions.
84 changes: 65 additions & 19 deletions docs/ndk/guide/dev/go/notif-stream.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# Notification Stream

Recall that our program's entrypoint finishes with initializing the app struct and calling the `app.Start(ctx)` function. The `Start` function is a place where we start the application's lifecycle.
Recall that our program's entrypoint [finishes](main.md#initializing-the-application) with initializing the app struct and calling the `app.Start(ctx)` function. The `Start` function is a place where we start the application's lifecycle.

```{.go title="greeter/app.go"}
--8<-- "http://172.17.0.1:49080/greeter/app.go:app-start"
```

Largely, the `Start` function can be divided in three parts:

1. Start the Configuration Notification Stream
2. Process the Configuration Notification Stream responses
3. Stop the application when the context is cancelled
1. [Start](#__codelineno-0-2) the Configuration Notification Stream
2. [Process](#__codelineno-0-6:17) the Configuration Notification Stream responses
3. [Stop](#__codelineno-0-19) the application when the context is cancelled

The application exit procedure has been covered in the [Exit Handler](main.md#exit-handler) section so here we will focus on the first two parts.

Expand All @@ -21,13 +21,13 @@ In the NDK Operations section about [Subscribing to Notifications][operations-su
And you know what, our greeter app is no different, it needs to receive notificatons from the NDK about, but it only needs to receive a particular notification type - its own configuration updates.
Whenever we configure the `/greeter/name` leaf and commit this configuration, our app needs to receive this update and act on it.

Since our app logic is super simple, all the greeter needs to do is
Since our app logic is super simple, all the greeter needs to do is:

1. to take the configured `name` value
1. to receive the configured `name` value
2. query SR Linux state to retrieve the last booted time
3. create the `greeting` message with the two values above

So it all starts with our app requesting the NDK to stream its own configuration updates back. And this is exactly what happens in `a.StartConfigNotificationStream(ctx)`. Let's zoom in.
So it all starts with our app requesting the NDK to stream its own configuration updates. And this is exactly what happens in `a.StartConfigNotificationStream(ctx)`. Let's zoom in.

```{.go title="greeter/notification.go"}
--8<-- "http://172.17.0.1:49080/greeter/notification.go:start-cfg-notif-stream"
Expand All @@ -47,34 +47,80 @@ The function tries to create a notification stream for the `greeter` application

## Adding Config Subscription

With the notification stream created, we can now request the NDK to deliver the configuration updates to our app. This is done by crafting a [`NotificationRegisterRequest`][notif_reg_req_doc] on [lines 8-16](#__codelineno-1-8:16).
With the notification stream created, we can now request the NDK to deliver the configuration updates to our app. This is done in the [`a.addConfigSubscription(ctx, streamID)`](#__codelineno-1-8) function.

Note that we use `streamID` we received after creating the notification stream to specify the stream we want to receive the notifications on.
```{.go title="greeter/notification.go"}
--8<-- "http://172.17.0.1:49080/greeter/notification.go:add-cfg-sub"
```

We also set the `SubscriptionTypes` to the `&ndk.NotificationRegisterRequest_Config` value to indicate that we would like to subscribe to the configuration updates.
Passing the [`NotificationRegisterRequest`][notif_reg_req_doc] with the `streamID` received after creating the notification stream allows us to specify the stream we want to receive the notifications on.

```{.go title="greeter/notification.go, createNotificationStream func"}
SubscriptionTypes: &ndk.NotificationRegisterRequest_Config{ // config service
Config: &ndk.ConfigSubscriptionRequest{},
},
```
The `SubscriptionTypes` set to the `&ndk.NotificationRegisterRequest_Config` value indicates that we would like to subscribe to the configuration updates.

And we pass the empty [`ConfigSubscriptionRequest`][cfg_sub_req_doc] request since we don't want to apply any filtering on the notifications we receive.

Executing `NotificationRegister` function of the `SDKMgrServiceClient` with notification Stream ID and [`NotificationRegisterRequest`][notif_reg_req_doc] effectively tells NDK about our intention to receive `Config` messages.

We pass the empty [`ConfigSubscriptionRequest`][cfg_sub_req_doc] request since we don't want to apply any filtering on the notifications we receive.
It is time to start the notification stream.

## Starting Notification Stream

With notification Stream ID allocated and [`NotificationRegisterRequest`][notif_reg_req_doc] for `Config` messages created, we can now start the notification stream.
[The last bits](#__codelineno-1-10:14) in the `StartConfigNotificationStream` function create a Go channel[^10] of type [`NotificationStreamResponse`][notif_stream_resp_doc] and pass it to the `startNotificationStream` function that is started in its own goroutine. Here is the `startNotificationStream` function:

```{.go title="greeter/notification.go"}
--8<-- "http://172.17.0.1:49080/greeter/notification.go:start-notif-stream"
```

### Stream Client

The function [starts](#__codelineno-4-12) with creating a Notification Stream Client with `a.getNotificationStreamClient(ctx, req)` function call. This client is a pure gRPC construct, it is automatically generated from the gRPC service proto file and facilitates the streaming nature of the NDK Notification Service.

```{.go title="greeter/notification.go"}
--8<-- "http://172.17.0.1:49080/greeter/notification.go:stream-client"
```

And here is where Go channels come really handy because we can use them to deliver the notifications to our app.
### Receiving Notifications

On [lines 18-21](#__codelineno-1-18:21) we create a channel of type [`NotificationStreamResponse`][notif_stream_resp_doc], because this is the type of the messages the NDK will send us, and we pass it to the `StartNotificationStream` function that is started in its own goroutine.
Coming back to our `startNotificationStream` function, we can see that it [loops](#__codelineno-6-16:37) over the notifications received from the NDK. The `streamClient.Recv()` function call is a blocking call that waits for the next notification to be streamed from the NDK.

```{.go title="greeter/notification.go"}
--8<-- "http://172.17.0.1:49080/greeter/notification.go:start-notif-stream"
```

When the notification is received, it is passed to the `streamChan` channel. On the receiving end of this channel is our app's [`Start`](#__codelineno-0-6:17) function that starts the `handleConfigNotifications` for each received notification.

/// details | Stream Response Type

If you wonder what type the notifications are, it solely depends on the type of subscriptions we added on the notification stream. In our case, we only [added](#adding-config-subscription) the `Config` subscription, so the notifications we receive will be backed by the [`ConfigNotification`][config_notif_doc] type.

Since the Notification Client can transport notifications of different types, the notification type is hidden behind the [`NotificationStreamResponse`][notif_stream_resp_doc] type. The `NotificationStreamResponse` embeds the `Notification` message that can be one of the following types:

```proto
message Notification
{
uint64 sub_id = 1; /* Subscription identifier */
oneof subscription_types
{
InterfaceNotification intf = 10; // Interface details
NetworkInstanceNotification nw_inst = 11; // Network instance details
LldpNeighborNotification lldp_neighbor = 12; // LLDP neighbor details
ConfigNotification config = 13; // Configuration notification
BfdSessionNotification bfd_session = 14; // BFD session details
IpRouteNotification route = 15; // IP route details
AppIdentNotification appid = 16; // App identification details
NextHopGroupNotification nhg = 17; // Next-hop group details
}
}
```

See the `ConfigNotification` type? This is what we expect to receive in our app.
///

[operations-subscr-to-notif]: ../../operations.md#subscribing-to-notifications
[operations-create-notif-stream]: ../../operations.md#creating-notification-stream
[notif_reg_req_doc]: https://rawcdn.githack.com/nokia/srlinux-ndk-protobufs/v0.2.0/doc/index.html#srlinux.sdk.NotificationRegisterRequest
[cfg_sub_req_doc]: https://rawcdn.githack.com/nokia/srlinux-ndk-protobufs/v0.2.0/doc/index.html#srlinux.sdk.ConfigSubscriptionRequest
[notif_stream_resp_doc]: https://rawcdn.githack.com/nokia/srlinux-ndk-protobufs/v0.2.0/doc/index.html#srlinux.sdk.NotificationStreamResponse
[config_notif_doc]: https://rawcdn.githack.com/nokia/srlinux-ndk-protobufs/v0.2.0/doc/index.html#srlinux.sdk.ConfigNotification

[^10]: Here is where Go channels come really handy because we can use them to deliver the notifications to our app.

0 comments on commit 256424a

Please sign in to comment.