[[common-control-if]]
== Osmocom Control Interface

The VTY interface as described in <<vty>> is aimed at human interaction
with the respective Osmocom program.

Other programs *should not* use the VTY interface to interact with the
Osmocom software, as parsing the textual representation is cumbersome,
inefficient, and will break every time the formatting is changed by the
Osmocom developers.

Instead, the 'Control Interface' was introduced as a programmatic
interface that can be used to interact with the respective program.

=== Control Interface Protocol

The control interface protocol is a mixture of binary framing with text
based payload.

The protocol for the control interface is wrapped inside the IPA
multiplex header with the stream identifier set to IPAC_PROTO_OSMO (0xEE).

.IPA header for control protocol
[packetdiag]
----
{
	colwidth = 24
	node_height = 24

	0-15: 	Length (network byte order)
	16-23:	Stream ID (0xEE)
}
----

Inside the IPA header is a single byte of extension header with protocol
ID 0x00 which indicates the control interface.

.IPA extension header for control protocol
[packetdiag]
----
{
	colwidth = 8
	node_height = 24

	0-7: 	Protocol (0x00)
}
----

After the concatenation of the two above headers, the plain-text payload
message starts.  The format of that plain text is illustrated for each
operation in the respective message sequence chart in the chapters
below.

The fields specified below follow the following meaning:

<id>::
	A numeric identifier, uniquely identifying this particular
	operation. Value `0` is not allowed unless it's a TRAP message. It will be echoed back in any
	response to a particular request.
<var>::
	The name of the variable / field affected by the GET / SET /
	TRAP operation.  Which variables/fields are available is
	dependent on the specific application under control.
<val>::
	The value of the variable / field
<reason>::
	A text formatted, human-readable reason why the operation
	resulted in an error.

==== GET operation

The GET operation is performed by an external application to get a
certain value from inside the Osmocom application.

.Control Interface GET operation (successful outcome)
[mscgen]
----
msc {
	cli [label="Client"], ctrl [label="Control Interface"];

	cli => ctrl	[label="GET <id> <var>"];
	cli <= ctrl	[label="GET_REPLY <id> <var> <val>"];
}
----

.Control Interface GET operation (unsuccessful outcome)
[mscgen]
----
msc {
	cli [label="Client"], ctrl [label="Control Interface"];

	cli => ctrl	[label="GET <id> <var>"];
	cli <= ctrl	[label="ERROR <id> <reason>"];
}
----

==== SET operation

The SET operation is performed by an external application to set a value
inside the Osmocom application.

.Control Interface SET operation (successful outcome)
[mscgen]
----
msc {
	cli [label="Client"], ctrl [label="Control Interface"];

	cli => ctrl	[label="SET <id> <var> <val>"];
	cli <= ctrl	[label="SET_REPLY <id> <var> <val>"];
}
----

.Control Interface SET operation (unsuccessful outcome)
[mscgen]
----
msc {
	cli [label="Client"], ctrl [label="Control Interface"];

	cli => ctrl	[label="SET <id> <var> <val>"];
	cli <= ctrl	[label="ERROR <id> <reason>"];
}
----

==== TRAP operation

The program can at any time issue a trap.  The term is used in the
spirit of SNMP.

.Control Interface TRAP operation
[mscgen]
----
msc {
	cli [label="Client"], ctrl [label="Control Interface"];

	cli <= ctrl	[label="TRAP <var> <val>"];
}
----

[[ctrl_common_vars]]
=== Common variables

There are several variables which are common to all the programs using control
interface. They are described in the following table.

.Variables available over control interface
[options="header",width="100%",cols="20%,10%,50%,20%"]
|===
|Name|Access|Value|Comment
|counter.*|RO||Get counter value.
|rate_ctr.*|RO||Get list of rate counter groups.
|rate_ctr.IN.GN.GI.name|RO||Get value for interval IN of rate counter name which belong to group named GN with index GI.
|===

Those read-only variables allow to get value of arbitrary
counter using its name.

For example "+rate_ctr.per_hour.bsc.0.handover:timeout+" is the number of handover timeouts per hour.

Of course for that to work the program
in question have to register corresponding counter names and groups using
libosmocore functions.

In the example above, "+bsc+" is the rate counter group name and "+0+" is its index. It is possible to
obtain all the rate counters in a given group by requesting "+rate_ctr.per_sec.bsc.*+" variable.

The list of available groups can be obtained by requesting "+rate_ctr.*+" variable.

The rate counter group name have to be prefixed with interval
specification which can be any of "*per_sec*", "*per_min*", "*per_hour*", "*per_day*"
or "*abs*" for absolute value.

The old-style counters available via "+counter.*+" variables are superseded by "+rate_ctr.abs+"
so its use is discouraged.
There might still be some applications not yet converted to rate_ctr.

=== Control Interface python examples

In the `osmo-python-tests` repository, there is an example python script
called `scripts/osmo_ctrl.py` which implements the Osmocom
control interface protocol.

You can use this tool either stand-alone to perform control interface
operations against an Osmocom program, or you can use it as a reference
for developing your own python software talking to the control
interface.

Another implementation is in `scripts/osmo_rate_ctr2csv.py` which will retrieve performance counters
for a given Osmocom program and output it in csv format. This can be used to periodically (using systemd timer
for example) retrieve data to build KPI and evaluate how it changes over time.

Internally it uses "+rate_ctr.*+" variable described in <<ctrl_common_vars>> to get the list of counter groups
and than request all the counters in each group. Applications interested in individual metrics can request it
directly using `rate_ctr2csv.py` as an example.

==== Getting rate counters

.Example: Use `rate_ctr2csv.py` to get rate counters from OsmoBSC
----
$ ./scripts/osmo_rate_ctr2csv.py --header
Connecting to localhost:4249...
Getting rate counter groups info...
"group","counter","absolute","second","minute","hour","day"
"e1inp.0","hdlc:abort","0","0","0","0","0"
"e1inp.0","hdlc:bad_fcs","0","0","0","0","0"
"e1inp.0","hdlc:overrun","0","0","0","0","0"
"e1inp.0","alarm","0","0","0","0","0"
"e1inp.0","removed","0","0","0","0","0"
"bsc.0","chreq:total","0","0","0","0","0"
"bsc.0","chreq:no_channel","0","0","0","0","0"
...
"msc.0","call:active","0","0","0","0","0"
"msc.0","call:complete","0","0","0","0","0"
"msc.0","call:incomplete","0","0","0","0","0"
Completed: 44 counters from 3 groups received.
----

==== Setting a value

.Example: Use `osmo_ctrl.py` to set the short network name of OsmoBSC
----
$ ./osmo_ctrl.py -d localhost -s short-name 32C3
Got message: SET_REPLY 1 short-name 32C3
----

==== Getting a value

.Example: Use `osmo_ctrl.py` to get the mnc of OsmoBSC
----
$ ./osmo_ctrl.py -d localhost -g mnc
Got message: GET_REPLY 1 mnc 262
----

==== Listening for traps

You can use `osmo_ctrl.py` to listen for traps the following way:

.Example: Using `osmo_ctrl.py` to listen for traps:
----
$ ./osmo_ctrl.py -d localhost -m
<1>
----
<1> the command will not return and wait for any TRAP messages to arrive
