How to create your own SM?

Introduction

Service models are the “contracts” that establish the allowed mechanisms and information by which near-RT RIC interacts with E2 nodes (e.g., CU/DU). O-RAN service models are defined in ASN.1 format, as they are used to be transported by SCTP from near-RT RIC to E2 nodes. In the SD-RAN project, internally the near-RT RIC implementation realizes the utilization of Service Models via gRPC, therefore the need for their definition in protobuf. In SD-RAN, the component responsible for the “translation” of ASN.1/SCTP to protobuf/gRPC format is onos-e2t. Therefore, xApps when implemented in SD-RAN utilize the library provided by the compilation of protobuf definitions of Service Models (SMs). To learn more about the definition of SMs in SD-RAN have a look at the readme of onos-e2-sm repository.

This tutorial defines how to write a Service Model and provide support for it in the SD-RAN project, so it can be utilized by the xApps for instance.

How to compile the SM from ASN1->protobuf->golang?

1. ASN.1 to Protobuf

1.1 You have to use the ONF’s distribution of the asn1c tool.

You can find it here. Please, follow its installation steps as indicated in the INSTALL.md file.

Once set, you can generate Protobuf out of asn1 definition using the -B option. The full command should look like:

Important: Make sure that the name for the top-level messages in the asn1 file start with “E2SM-

asn1c -B my_sm.asn1 > my_sm.proto

After that, depending on which way of implementation you choose (CGo or with Go APER library), you should make some adjustments to the Protobuf file.

Note (implementation choices):

  • CGo - is the Go glue code which wraps C code generated by the asn1c tool.

    • SM with the CGo approach is complex on implementation and requires a lot of effort for maintenance (e.g., future upgrade of SM).

    • In CGo, Go doesn’t take care of garbage collection and memory leaks (due to the C code) may happen.

  • Go APER - is the Go package which implements APER encoder and decoder. It is capable of converting the message in the APER bytes and decode the message from the APER bytes. This APER library is fully compatible with the asn1c tool.

    • Currently, this is the best way to implement SM.


1.2 Make sure that the Protobuf messages in your generated Protobuf correspond to reference ones defined in the ASN.1 definition.

A good example is the Protobuf for MHO SM stored in onos-e2-sm repo. For this tutorial, the MHO SM is being used as a reference example. So, please use as a reference this Protobuf file.

Go APER requires the definition of tags marked as annotations in the message fields. Having these tags would help the Go APER library to correctly encode and decode the messages. Currently, the automated definition of tags is not handled by the asn1c tool, but this feature is going to be implemented in the future.

It is necessary to insert all tags correctly, otherwise Go APER library wouldn’t be able to encode or decode the message correctly. Here are some examples:

Please also refer to this guide, which explains the APER tags usage in details.


1.3 Add SM compilation commands into the Makefile

Embed your SM in the onos-e2-sm Makefile script to generate Protobuf with the make protos command (i.e., to facilitate the translation of the Protobuf to golang code, the proper references of packages are done using the makefile targets. In the case of SMs it is done using the commands protoc and protoc-go-inject-tag).

Here is an example of how to generate a Golang driven Protobuf structures out of plain Protobuf. Don’t forget to create the target that calls the injection of all tag definitions from the Protobuf to the Golang pb generated code, for instance like here:


1.3.1 Importance of protoc-gen-validate plugin.

You can benefit from protoc-gen-validate plugin, it can validate your messages and throw an error if you’ve the data inserted in your message are out of range, i.e., INTEGER has an upper bound of 255, for some reason 256 is passed instead – protoc-gen-validate will catch this error and notify you. Asn1c tool already generates all necessary rules for this plugin, you only need to enable it in the compilation process.

Here is an example on how to do that.

If protoc-gen-validate is enabled, you can then validate your messages in this way.



Now you can run make protos, which will generate a Protobuf-based Golang code of your SM.

The CGo way of implementing SMs is a legacy way now. A better (and easier) way to implement E2* is to use Go APER library. You can refer to all SMs, the outcome of Go APER library, in the onos-e2-sm repo appended with _go to see how it’s done.


2. Create wrappers for encoder and decoder.

Each SM has a specific wrapper to encode each one of its messages to APER.

You can find an example here, where all the MHO SM messages have defined their encoders.

Each definition of an SM message encoder file contains encode and decode functions associated with that message:

  • For example, the encoder for MHO-ControlHeader looks like that.

  • The encoding part is being invoked here.

  • Respectively, decoding is being done here.

Please also note, that it is expected that each top-level message starts from E2Sm, otherwise the protoc-gen-builder plugin will assume that there are no top-level messages defined.

Notice: as shown in line 24 of the file E2SM-MHO-ControlHeader.go, there is a mandatory prerequisite for marshaling (and unmarshaling). In MHO Control Header encoder look for the variable e2smmhov2.MhoChoicemap, this variable contains a map of all CHOICE structs, which are in your SM definition. Without that input, the encoder and decoder wouldn’t know which CHOICE option they’re expected to encode or decode (marshal or unmarshal a message).

The file containing the choice options, as mentioned above, can be automatically generated with a protoc-gen-choice plugin. Please study carefully the README in order to install and use this plugin.


3. Create pdubuilders around it.

In general, pdubuilder should help to create messages in a faster way (i.e., to create the header and the content of the message). A myriad of pdubuilders can be defined to facilitate the creation of messages. The goal of pdubuilders is to facilitate the utilization of the SM Golang code by the xApp.

All MHO SM pdubuilders are stored in the pdubuilder directory of the MHO SM.

To outline, here are some examples provided below:

All CHOICEs deserve their own wrappers! Like the one here.

Some other helper functions are stored in a builder.go. This file contains Setter functions for all OPTIONAL items in your SM definition.


4. Pack it as a plugin.

An example could be found here. This is an implementation of a common plugin interface. It should correspond to the rest of the SMs.

Steps 2, 3 and 4 are automated with protoc-gen-builder plugin. Please take a look at it. It should do the majority of the work.

Necessary prerequisite is a correct .proto file, with all tags inserted correctly! If you’re unsure about the correctness of the Protobuf, please visit this guide.


5. Verify that encoding and decoding works with unit tests!

Each unit test should verify that the Go APER library is able to encode the message without any errors and decode the generated set of APER bytes. Decoded message should be completely similar to the encoded one. A good example of such approach could be found here or here.

This is the most essential and a fundamental step before you can start using your SM!


6. Publish the SM

Add the SM definitions (service model, proto, Golang code, encoder, etc) to the onos-e2-sm repository. Similar to other SMs already contained, create a Golang module and push the code to the onos-e2-sm. Use the other SMs as reference to create the main.go file and structure of folders/files.

Please also include unit tests in the make test target. Here and here is an example of building an SM image in a Docker container. An example of loading of an image to the KinD can be found here.

Summary

Wrapping up all aforesaid, the source code for the SM, for instance E2SM-RC, could be generated with following commands:

$ cd onos-e2-sm/servicemodels
$ mkdir e2sm_rc && cd e2sm_rc
$ mkdir -p v1/choiceOptions encoder pdubuilder servicemodel
$ cd v1
# Generate your Protobuf with asn1c tool and locate it in the SM folder, for instance e2sm_rc/v1/ (since it is E2SM-RC v01.01.05)
$ asn1c -B my_sm.asn1 > my_sm.proto
# Then generate pb.go files for "my_sm.proto" file (or files)

$ cd ../../.. # get back to the root of the ono-e2-sm repo
$ pwd
/home/<your-username>/go/src/github.com/onosproject/onos-e2-sm
$ proto_imports=${GOPATH}/src/github.com/onosproject/onos-e2-sm
$ protoc -I="$proto_imports:${GOPATH}/src/github.com/onosproject/onos-lib-go/api" --proto_path="servicemodels/" --choice_out="servicemodels/e2sm_rc/v1/choiceOptions/" servicemodels/e2sm_rc/v1/e2sm_common_ies.proto servicemodels/e2sm_rc/v1/e2sm_rc.proto
$ protoc -I="$proto_imports:${GOPATH}/src/github.com/onosproject/onos-lib-go/api" --proto_path="servicemodels/" --builder_out="sm=true:servicemodels/e2sm_rc/" servicemodels/e2sm_rc/v1/e2sm_common_ies.proto servicemodels/e2sm_rc/v1/e2sm_rc.proto