1# Clixon main example 2 3 * [Content](#content) 4 * [Compile and run](#compile) 5 * [Using the CLI](#using-the-cli) 6 * [Using netconf](#using-netconf) 7 * [Streams](#streams) 8 * [RPC Operations](#rpc-operations) 9 * [State data](#state-data) 10 * [Extensions](#extension) 11 * [Authentication and NACM](#authentication-and-nacm) 12 * [Systemd](#systemd) 13 * [Docker](#docker) 14 * [Plugins](#plugins) 15 16## Content 17 18This directory contains a Clixon example used primarily as a part of the Clixon test suites. It can be used as a basis for making new Clixon applications. But please consider also the minimal [hello](../hello) example as well. It contains the following files: 19* `example.xml` The configuration file. See [yang/clixon-config@<date>.yang](../../yang/clixon-config@2019-03-05.yang) for the documentation of all available fields. 20* `clixon-example@2019-01-13.yang` The yang spec of the example. 21* `example_cli.cli` CLIgen specification. 22* `example_cli.c` CLI callback plugin containing functions called in the cli file above: a generic callback (`mycallback`) and an example RPC call (`example_client_rpc`). 23* `example_backend.c` Backend callback plugin including example of: 24 * transaction callbacks (validate/commit), 25 * notification, 26 * rpc handler 27 * state-data handler, ie non-config data 28* `example_backend_nacm.c` Secondary backend plugin. Plugins are loaded alphabetically. 29* `example_restconf.c` Restconf callback plugin containing an HTTP basic authentication callback 30* `example_netconf.c` Netconf callback plugin 31* `Makefile.in` Example makefile where plugins are built and installed 32 33## Compile and run 34 35Before you start, 36* You must configure with: `--enable-optyangs` to run the main example. 37* Make [group setup](../../doc/FAQ.md#do-i-need-to-setup-anything-important) 38* Setup [restconf](../../doc/FAQ.md#how-do-i-use-restconf) 39 40 41``` 42 cd example 43 make && sudo make install 44``` 45Start backend: 46``` 47 sudo clixon_backend -f /usr/local/etc/example.xml -s init 48``` 49Edit cli: 50``` 51 clixon_cli -f /usr/local/etc/example.xml 52``` 53Send netconf command: 54``` 55 clixon_netconf -f /usr/local/etc/example.xml 56``` 57Start clixon restconf daemon 58``` 59 sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data 60``` 61Send restconf command 62``` 63 curl -G http://127.0.0.1/restconf/data 64``` 65 66## Using the CLI 67 68The example CLI allows you to modify and view the data model using `set`, `delete` and `show` via generated code. 69There are also many other commands available as examples. View the source file (example_cli.cli)[example_cli.cli] for more details. 70 71The following example shows how to add an interface in candidate, validate and commit it to running, then look at it (as xml) and finally delete it. 72``` 73clixon_cli -f /usr/local/etc/example.xml 74cli> set interfaces interface eth9 ? 75 description enabled ipv4 76 ipv6 link-up-down-trap-enable type 77cli> set interfaces interface eth9 type ex:eth 78cli> validate 79cli> commit 80cli> show configuration xml 81<interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> 82 <interface> 83 <name>eth9</name> 84 <type>ex:eth</type> 85 <enabled>true</enabled> 86 </interface> 87</interfaces> 88cli> delete interfaces interface eth9 89``` 90 91## Using Netconf 92 93The following example shows how to set data using netconf: 94``` 95<rpc><edit-config><target><candidate/></target><config> 96 <interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"> 97 <interface> 98 <name>eth1</name> 99 <enabled>true</enabled> 100 <ipv4> 101 <address> 102 <ip>9.2.3.4</ip> 103 <prefix-length>24</prefix-length> 104 </address> 105 </ipv4> 106 </interface> 107 </interfaces> 108</config></edit-config></rpc>]]>]]> 109``` 110 111### Getting data using netconf 112``` 113<rpc><get-config><source><candidate/></source></get-config></rpc>]]>]]> 114<rpc><get-config><source><candidate/></source><filter/></get-config></rpc>]]>]]> 115<rpc><get-config><source><candidate/></source><filter type="xpath"/></get-config></rpc>]]>]]> 116<rpc><get-config><source><candidate/></source><filter type="subtree"><data><interfaces xmlns="urn:ietf:params:xml:ns:yang:ietf-interfaces"><interface><name>eth9</name><type>ex:eth</type></interface></interfaces></data></filter></get-config></rpc>]]>]]> 117<rpc><get-config><source><candidate/></source><filter type="xpath" select="/interfaces/interface"/></get-config></rpc>]]>]]> 118<rpc><validate><source><candidate/></source></validate></rpc>]]>]]> 119``` 120## Restconf 121 122Setup a web/reverse-proxy server. 123For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default: 124``` 125server { 126 ... 127 location / { 128 root /usr/share/nginx/html/restconf; 129 fastcgi_pass unix:/www-data/fastcgi_restconf.sock; 130 include fastcgi_params; 131 } 132 location /restconf { 133 fastcgi_pass unix:/www-data/fastcgi_restconf.sock; 134 include fastcgi_params; 135 } 136 location /streams { 137 fastcgi_pass unix:/www-data/fastcgi_restconf.sock; 138 include fastcgi_params; 139 proxy_http_version 1.1; 140 proxy_set_header Connection ""; 141 } 142} 143``` 144Start nginx daemon 145``` 146 sudo /etc/init.d/nginx start 147 sudo systemctl start nginx.service # alternative using systemd 148``` 149Start the clixon restconf daemon 150``` 151 sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data 152``` 153then access using curl or wget: 154``` 155 curl -X GET http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth1/type 156``` 157 158More info: (restconf)[../../apps/restconf/README.md]. 159 160## Streams 161 162The example has an EXAMPLE stream notification triggering every 5s. To start a notification 163stream in the session using netconf, create a subscription: 164``` 165<rpc><create-subscription xmlns="urn:ietf:params:xml:ns:netmod:notification"><stream>EXAMPLE</stream></create-subscription></rpc>]]>]]> 166<rpc-reply><ok/></rpc-reply>]]>]]> 167<notification xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0"><eventTime>2019-01-02T10:20:05.929272</eventTime><event><event-class>fault</event-class><reportingEntity><card>Ethernet0</card></reportingEntity><severity>major</severity></event></notification>]]>]]> 168... 169``` 170This can also be triggered via the CLI: 171``` 172clixon_cli -f /usr/local/etc/example.xml 173cli> notify 174cli> event-class fault; 175reportingEntity { 176 card Ethernet0; 177} 178severity major; 179... 180cli> no notify 181cli> 182``` 183 184Restconf support is also supported, see (restconf)[../../apps/restconf/README.md]. 185 186 187## RPC Operations 188 189Clixon implements Yang RPC operations by a mechanism that enables you 190to add application-specific operations. It works by adding 191user-defined callbacks for added netconf operations. It is possible to 192use the extension mechanism independent of the yang rpc construct, but 193not recommended . The example includes an example: 194 195Example using CLI: 196``` 197clixon_cli -f /usr/local/etc/example.xml 198cli> rpc ipv4 199<rpc-reply><x xmlns="urn:example:clixon">ipv4</x><y xmlns="urn:example:clixon">42</y></rpc-reply> 200``` 201Example using Netconf: 202``` 203clixon_netconf -qf /usr/local/etc/example.xml 204<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><example xmlns="urn:example:clixon"><x>ipv4</x></example></rpc>]]>]]> 205<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><x xmlns="urn:example:clixon">ipv4</x><y xmlns="urn:example:clixon">42</y></rpc-reply>]]>]]> 206``` 207Restconf (assuming nginx started): 208``` 209sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data& 210curl -X POST http://localhost/restconf/operations/clixon-example:example -H "Content-Type: application/yang-data+json" -d '{"clixon-example:input":{"x":"ipv4"}}' 211{ 212 "clixon-example:output": { 213 "x": "ipv4", 214 "y": "42" 215 } 216} 217``` 218 219### Details 220 221The example works by defining an RPC in clixon-example.yang: 222``` 223 rpc example { 224 description "Some example input/output for testing RFC7950 7.14. 225 RPC simply echoes the input for debugging."; 226 input { 227 leaf x { 228 ... 229``` 230 231In the CLI a netconf rpc call is constructed and sent to the backend: See `example_client_rpc()` in [example_cli.c] CLI plugin. 232 233The clixon backend plugin [example_backend.c] reveives the netconf call and replies. This is made byregistering a callback handling handling the RPC: 234``` 235static int 236example_rpc(clicon_handle h, 237 cxobj *xe, /* Request: <rpc><xn></rpc> */ 238 cbuf *cbret, /* Reply eg <rpc-reply>... */ 239 void *arg, /* Client session */ 240 void *regarg) /* Argument given at register */ 241{ 242 /* code that echoes the request */ 243 return 0; 244} 245int 246clixon_plugin_init(clicon_handle h) 247{ 248... 249 rpc_callback_register(h, example_rpc, NULL, "example"); 250... 251} 252``` 253 254## State data 255 256Netconf <get> and restconf GET also returns state data(not only configuration data). 257 258In YANG state data is specified with `config false;`. In the example, 259`state` is state data, see (example.yang)[example.yang] 260 261To return state data, you need to write a backend state data callback 262with the name "plugin_statedata" where you return an XML tree with 263state. This is then merged with config data by the system. 264 265A static example of returning state data is in the example. Note that 266a real example would poll or get the interface counters via a system 267call, as well as use the "xpath" argument to identify the requested 268state data. 269 270The state data is enabled by starting the backend with: `-- -s`. 271 272## Authentication and NACM 273The example contains some stubs for authorization according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341): 274* A basic auth HTTP callback, see: example_restconf_credentials() containing three example users: andy, wilma, and guest, according to the examples in Appendix A in [RFC8341](https://tools.ietf.org/html/rfc8341). 275* A NACM backend plugin reporting the mandatory NACM state variables. 276 277## Extensions 278 279Clixon supports Yang extensions by writing plugin callback code. 280The example backend implements an "example:e4" Yang extension, as follows: 281``` 282 extension e4 { 283 description 284 "The first child of the ex:e4 (unknown) statement is inserted into 285 the module as a regular data statement. This means that 'uses bar;' 286 in the ex:e4 statement below is a valid data node"; 287 argument arg; 288 } 289 ex:e4 arg1{ 290 uses bar; 291 } 292``` 293 294The backend plugin code registers an extension callback in the init struct: 295``` 296 .ca_extension=example_extension, /* yang extensions */ 297``` 298 299The callback then receives a callback on all "unknown" Yang statements 300during yang parsing. If the extension matches "example:e4", it applies 301the extension. In the example, it copies the child of the "ex:e4" statement and 302inserts in as a proper yang statement in the example module. 303 304## Systemd 305 306Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example. 307 308## Docker 309 310See [../../docker/system] for instructions on how to build this example 311as a docker container. 312 313## Plugins 314 315The example includes a restonf, netconf, CLI and two backend plugins. 316Each plugin is initiated with an API struct followed by a plugin init function. 317The content of the API struct is different depending on what kind of plugin it is. 318The plugin init function may also include registering RPC functions, see below is for a backend. 319``` 320static clixon_plugin_api api = { 321 "example", /* name */ 322 clixon_plugin_init, 323 plugin_start, 324 plugin_exit, 325 .ca_reset=plugin_reset,/* reset for extra XML at startup*/ 326 .ca_statedata=plugin_statedata, /* statedata */ 327 .ca_upgrade=example_upgrade, /* upgrade configuration */ 328 .ca_trans_begin=NULL, /* trans begin */ 329 .ca_trans_validate=transaction_validate,/* trans validate */ 330 .ca_trans_complete=NULL, /* trans complete */ 331 .ca_trans_commit=transaction_commit, /* trans commit */ 332 .ca_trans_end=NULL, /* trans end */ 333 .ca_trans_abort=NULL /* trans abort */ 334}; 335 336clixon_plugin_api * 337clixon_plugin_init(clicon_handle h) 338{ 339 /* Optional callback registration for RPC calls */ 340 rpc_callback_register(h, example_rpc, NULL, "example"); 341 /* Return plugin API */ 342 return &api; /* Return NULL on error */ 343} 344``` 345 346Here is a corresponding example for a CLI plugin: 347``` 348static clixon_plugin_api api = { 349 "example", /* name */ 350 clixon_plugin_init, /* init */ 351 NULL, /* start */ 352 NULL, /* exit */ 353 .ca_prompt=NULL, /* cli_prompthook_t */ 354 .ca_suspend=NULL, /* cligen_susp_cb_t */ 355 .ca_interrupt=NULL, /* cligen_interrupt_cb_t */ 356}; 357``` 358