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
).
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 theprotoc-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 theREADME
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:
Top-level
pdubuilder
for E2SM-MHO IndicationMessage Format1 and Format2An example of other helper functions which create specific/complex items.
All
CHOICE
s 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