diff --git a/.gitignore b/.gitignore index 75ddfe20..c191c5b0 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ *_parse.tab.c *_parse.tab.h lex.*_parse.c +*.depend Makefile apps/Makefile diff --git a/CHANGELOG.md b/CHANGELOG.md index dee1a374..dd30fc82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ ### API changes on existing features (you may need to change your code) +* Directory change: Moved example to example/main to make room for other examples. * Removed argc/argv parameters from ca_start plugin API function: * You may need to change signatures of your startup in your plugins, eg from: ``` diff --git a/configure b/configure index 899236bb..e3293970 100755 --- a/configure +++ b/configure @@ -4447,7 +4447,7 @@ _ACEOF -ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile extras/rpm/Makefile docker/Makefile docker/system/Makefile docker/base/Makefile datastore/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/Makefile doc/Makefile test/Makefile" +ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile extras/rpm/Makefile docker/Makefile docker/system/Makefile docker/base/Makefile datastore/Makefile util/Makefile yang/Makefile yang/clixon/Makefile yang/standard/Makefile doc/Makefile test/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure @@ -5154,6 +5154,7 @@ do "etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;; "etc/clixonrc") CONFIG_FILES="$CONFIG_FILES etc/clixonrc" ;; "example/Makefile") CONFIG_FILES="$CONFIG_FILES example/Makefile" ;; + "example/main/Makefile") CONFIG_FILES="$CONFIG_FILES example/main/Makefile" ;; "extras/rpm/Makefile") CONFIG_FILES="$CONFIG_FILES extras/rpm/Makefile" ;; "docker/Makefile") CONFIG_FILES="$CONFIG_FILES docker/Makefile" ;; "docker/system/Makefile") CONFIG_FILES="$CONFIG_FILES docker/system/Makefile" ;; diff --git a/configure.ac b/configure.ac index e2947ad7..daf21edc 100644 --- a/configure.ac +++ b/configure.ac @@ -248,6 +248,7 @@ AC_OUTPUT(Makefile etc/Makefile etc/clixonrc example/Makefile + example/main/Makefile extras/rpm/Makefile docker/Makefile docker/system/Makefile diff --git a/doc/FAQ.md b/doc/FAQ.md index ab023a96..0afffdc0 100644 --- a/doc/FAQ.md +++ b/doc/FAQ.md @@ -75,7 +75,7 @@ Clixon: sudo make install; sudo make install-include ``` -The example: +The main example: ``` cd example; make; @@ -229,7 +229,7 @@ configuration file is /usr/local/etc/clixon.xml. The example configuration file is installed at /usr/local/etc/example.xml. The YANG specification for the configuration file is clixon-config.yang. -See the [example config file](../example/example.xml). +See the [example config file](../example/main/example.xml). ## How are Clixon configuration files found? @@ -364,7 +364,7 @@ You may also add a default method in the configuration file: ## Can I use systemd with Clixon? Yes. Systemd example files are provide for the backend and the -restconf daemon as part of the [example](../example/systemd). +restconf daemon as part of the [example](../example/main/systemd). ## How can I add extra XML? @@ -386,7 +386,7 @@ The second way is by programming the plugin_reset() in the backend plugin. The example code contains an example on how to do this (see plugin_reset() in example_backend.c). ## I want to program. How do I extend the example? -See [../apps/example](../apps/example) +See [../example/main](../example/main) - example.xml - Change the configuration file - The yang specifications - This is the central part. It changes the XML, database and the config cli. - example_cli.cli - Change the fixed part of the CLI commands @@ -412,7 +412,7 @@ Each plugin is initiated with an API struct followed by a plugin init function a return &api; /* Return NULL on error */ } ``` -For more info see [../example/README.md] +For more info see [../example/main/README.md] ## How do I write a commit function? @@ -560,7 +560,7 @@ plugin_credentials(clicon_handle h, To authenticate, the callback needs to return the value 1 and supply a username. -See [../apps/example/example_restconf.c] example_restconf_credentials() for +See [../example/main/example_restconf.c] example_restconf_credentials() for an example of HTTP basic auth. ## What about access control? diff --git a/example/Makefile.in b/example/Makefile.in index d0e502b5..90a0aa19 100644 --- a/example/Makefile.in +++ b/example/Makefile.in @@ -33,127 +33,46 @@ VPATH = @srcdir@ srcdir = @srcdir@ top_srcdir = @top_srcdir@ -prefix = @prefix@ -bindir = @bindir@ -includedir = @includedir@ -datarootdir = @datarootdir@ -sysconfdir = @sysconfdir@ -datarootdir = @datarootdir@ -localstatedir = @localstatedir@ -libdir = @exec_prefix@/lib - -APPNAME = example -# Here is where example yang appears -CLIXON_DATADIR = @CLIXON_DATADIR@ -# Install here if you want default clixon location: -CLIXON_DEFAULT_CONFIG = @CLIXON_DEFAULT_CONFIG@ - CC = @CC@ -CFLAGS = @CFLAGS@ -rdynamic -fPIC -INSTALLFLAGS = @INSTALLFLAGS@ -with_restconf = @with_restconf@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ -INCLUDES = -I$(includedir) @INCLUDES@ -CPPFLAGS = @CPPFLAGS@ -fPIC +SHELL = /bin/sh -BE_PLUGIN = $(APPNAME)_backend.so -BE2_PLUGIN = $(APPNAME)_backend_nacm.so -CLI_PLUGIN = $(APPNAME)_cli.so -NETCONF_PLUGIN = $(APPNAME)_netconf.so -RESTCONF_PLUGIN = $(APPNAME)_restconf.so +SUBDIRS = main -PLUGINS = $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) -ifeq ($(with_restconf),yes) -PLUGINS += $(RESTCONF_PLUGIN) -endif +.PHONY: all clean depend install $(SUBDIRS) -.PHONY: all clean depend install +all: $(SUBDIRS) -all: $(PLUGINS) +depend: + for i in $(SUBDIRS); \ + do (cd $$i; $(MAKE) $(MFLAGS) $@); done -.SUFFIXES: .c .o +$(SUBDIRS): + (cd $@; $(MAKE) $(MFLAGS) all) -# implicit rule -.c.o: - $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c $< +install-include: + for i in $(SUBDIRS); \ + do (cd $$i && $(MAKE) $(MFLAGS) $@||exit 1); done; -CLISPECS = $(APPNAME)_cli.cli +install: + for i in $(SUBDIRS); \ + do (cd $$i && $(MAKE) $(MFLAGS) $@)||exit 1; done -YANGSPECS = clixon-example@2019-01-13.yang - -# Backend plugin -BE_SRC = $(APPNAME)_backend.c -BE_OBJ = $(BE_SRC:%.c=%.o) -$(BE_PLUGIN): $(BE_OBJ) - $(CC) -Wall -shared -o $@ -lc $< - -# Secondary NACM backend plugin -BE2_SRC = $(APPNAME)_backend_nacm.c -BE2_OBJ = $(BE2_SRC:%.c=%.o) -$(BE2_PLUGIN): $(BE2_OBJ) - $(CC) -Wall -shared -o $@ -lc $< - -# CLI frontend plugin -CLI_SRC = $(APPNAME)_cli.c -CLI_OBJ = $(CLI_SRC:%.c=%.o) -$(CLI_PLUGIN): $(CLI_OBJ) - $(CC) -Wall -shared -o $@ -lc $^ - -# NETCONF frontend plugin -NETCONF_SRC = $(APPNAME)_netconf.c -NETCONF_OBJ = $(NETCONF_SRC:%.c=%.o) -$(NETCONF_PLUGIN): $(NETCONF_OBJ) - $(CC) -Wall -shared -o $@ -lc $^ - -# See configure.ac -# RESTCONF frontend plugin -RESTCONF_SRC = $(APPNAME)_restconf.c -RESTCONF_OBJ = $(RESTCONF_SRC:%.c=%.o) -$(RESTCONF_PLUGIN): $(RESTCONF_OBJ) - $(CC) -Wall -shared -o $@ -lc $^ - -SRC = $(BE_SRC) $(BE2_SRC) $(CLI_SRC) $(NETCONF_SRC) -SRC += $(RESTCONF_SRC) - -OBJS = $(BE_OBJ) $(BE2_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) -OBJS += $(RESTCONF_OBJ) +uninstall: + for i in $(SUBDIRS); \ + do (cd $$i; $(MAKE) $(MFLAGS) $@)||exit 1; done clean: - rm -f $(PLUGINS) $(OBJS) + for i in $(SUBDIRS); \ + do (cd $$i; $(MAKE) $(MFLAGS) $@); done distclean: clean rm -f Makefile *~ .depend + for i in $(SUBDIRS); \ + do (cd $$i; $(MAKE) $(MFLAGS) $@); done -install: $(YANGSPECS) $(CLISPECS) $(PLUGINS) $(APPNAME).xml - install -d -m 0755 $(DESTDIR)$(sysconfdir) - install -m 0644 $(APPNAME).xml $(DESTDIR)$(sysconfdir) -# install -m 0644 $(APPNAME).xml $(DESTDIR)$(CLIXON_DEFAULT_CONFIG) - install -d -m 0755 $(DESTDIR)$(datarootdir)/$(APPNAME)/yang - install -m 0644 $(YANGSPECS) $(DESTDIR)$(DESTDIR)$(CLIXON_DATADIR) - install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/cli - install -m 0644 $(INSTALLFLAGS) $(CLI_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/cli - install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/backend - install -m 0644 $(INSTALLFLAGS) $(BE_PLUGIN) $(BE2_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/backend - install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/netconf - install -m 0644 $(INSTALLFLAGS) $(NETCONF_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/netconf -ifeq ($(with_restconf),yes) - install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/restconf - install -m 0644 $(INSTALLFLAGS) $(RESTCONF_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/restconf -endif - install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/clispec - install -m 0644 $(CLISPECS) $(DESTDIR)$(libdir)/$(APPNAME)/clispec - install -d -m 0755 $(DESTDIR)$(localstatedir)/$(APPNAME) - -uninstall: - rm -rf $(DESTDIR)$(sysconfdir)/$(APPNAME).xml - rm -rf $(DESTDIR)$(datarootdir)/$(APPNAME) - rm -rf $(DESTDIR)$(localstatedir)/$(APPNAME) - rm -rf $(DESTDIR)$(libdir)/$(APPNAME) - -install-include: - -depend: - $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend - -#include .depend - +TAGS: + find $(srcdir) -name '*.[chyl]' -print | etags - diff --git a/example/README.md b/example/README.md index 0db41ded..709cf2dd 100644 --- a/example/README.md +++ b/example/README.md @@ -1,325 +1,4 @@ -# Clixon example +# Clixon examples - * [Content](#content) - * [Compile and run](#compile) - * [Using the CLI](#using-the-cli) - * [Using netconf](#using-netconf) - * [Streams](#streams) - * [RPC Operations](#rpc-operations) - * [State data](#state-data) - * [Authentication and NACM](#authentication-and-nacm) - * [Systemd](#systemd) - * [Docker](#docker) - * [Plugins](#plugins) - -## Content - -This directory contains a Clixon example which includes a simple example. It contains the following files: -* `example.xml` The configuration file. See [yang/clixon-config@.yang](../yang/clixon-config@2018-10-21.yang) for the documentation of all available fields. -* `clixon-example@2019-01-13.yang` The yang spec of the example. -* `example_cli.cli` CLIgen specification. -* `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`). -* `example_backend.c` Backend callback plugin including example of: - * transaction callbacks (validate/commit), - * notification, - * rpc handler - * state-data handler, ie non-config data -* `example_backend_nacm.c` Secondary backend plugin. Plugins are loaded alphabetically. -* `example_restconf.c` Restconf callback plugin containing an HTTP basic authentication callback -* `example_netconf.c` Netconf callback plugin -* `Makefile.in` Example makefile where plugins are built and installed - - -## Compile and run - -Before you start, -* Make [group setup](../doc/FAQ.md#do-i-need-to-setup-anything-important) -* Setup [restconf](../doc/FAQ.md#how-do-i-use-restconf) - -``` - cd example - make && sudo make install -``` -Start backend: -``` - sudo clixon_backend -f /usr/local/etc/example.xml -s init -``` -Edit cli: -``` - clixon_cli -f /usr/local/etc/example.xml -``` -Send netconf command: -``` - clixon_netconf -f /usr/local/etc/example.xml -``` -Start clixon restconf daemon -``` - sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data -``` -Send restconf command -``` - curl -G http://127.0.0.1/restconf/data -``` - -## Using the CLI - -The example CLI allows you to modify and view the data model using `set`, `delete` and `show` via generated code. -There are also many other commands available as examples. View the source file (example_cli.cli)[example_cli.cli] for more details. - -The 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. -``` -clixon_cli -f /usr/local/etc/example.xml -cli> set interfaces interface eth9 ? - description enabled ipv4 - ipv6 link-up-down-trap-enable type -cli> set interfaces interface eth9 type ex:eth -cli> validate -cli> commit -cli> show configuration xml - - - eth9 - ex:eth - true - - -cli> delete interfaces interface eth9 -``` - -## Using Netconf - -The following example shows how to set data using netconf: -``` - - - - eth1 - true - -
- 9.2.3.4 - 24 -
-
-
-
-
]]>]]> -``` - -### Getting data using netconf -``` -]]>]]> -]]>]]> -]]>]]> -eth9ex:eth]]>]]> -]]>]]> -]]>]]> -``` -## Restconf - -Setup a web/reverse-proxy server. -For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default: -``` -server { - ... - location / { - root /usr/share/nginx/html/restconf; - fastcgi_pass unix:/www-data/fastcgi_restconf.sock; - include fastcgi_params; - } - location /restconf { - fastcgi_pass unix:/www-data/fastcgi_restconf.sock; - include fastcgi_params; - } - location /streams { - fastcgi_pass unix:/www-data/fastcgi_restconf.sock; - include fastcgi_params; - proxy_http_version 1.1; - proxy_set_header Connection ""; - } -} -``` -Start nginx daemon -``` -sudo /etc/init.d/nginx start -``` -Start the clixon restconf daemon -``` -sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data -``` -then access using curl or wget: -``` - curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth9/type -``` - -## Streams - -The example has an EXAMPLE stream notification triggering every 5s. To start a notification -stream in the session using netconf, create a subscription: -``` -EXAMPLE]]>]]> -]]>]]> -2019-01-02T10:20:05.929272faultEthernet0major]]>]]> -... -``` -This can also be triggered via the CLI: -``` -clixon_cli -f /usr/local/etc/example.xml -cli> notify -cli> event-class fault; -reportingEntity { - card Ethernet0; -} -severity major; -... -cli> no notify -cli> -``` - -Restconf support is also supported, see (restc)[../apps/restconf/README.md]. - - -## RPC Operations - -Clixon implements Yang RPC operations by an extension mechanism. The -extension mechanism enables you to add application-specific -operations. It works by adding user-defined callbacks for added -netconf operations. It is possible to use the extension mechanism -independent of the yang rpc construct, but it is recommended. The example includes an example: - -Example using CLI: -``` -clixon_cli -f /usr/local/etc/example.xml -cli> rpc ipv4 -ipv442 -``` -Example using Netconf: -``` -clixon_netconf -qf /usr/local/etc/example.xml -ipv4]]>]]> -ipv442]]>]]> -``` -Restconf (assuming nginx started): -``` -sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data& -curl -X POST http://localhost/restconf/operations/clixon-example:example -d '{"clixon-example:input":{"x":"ipv4"}}' -{ - "clixon-example:output": { - "x": "ipv4", - "y": "42" - } -} -``` - -### Details - -The example works by defining an RPC in clixon-example.yang: -``` - rpc example { - description "Some example input/output for testing RFC7950 7.14. - RPC simply echoes the input for debugging."; - input { - leaf x { - ... -``` - -In the CLI a netconf rpc call is constructed and sent to the backend: See `example_client_rpc()` in [example_cli.c] CLI plugin. - -The clixon backend plugin [example_backend.c] reveives the netconf call and replies. This is made byregistering a callback handling handling the RPC: -``` -static int -example_rpc(clicon_handle h, - cxobj *xe, /* Request: */ - cbuf *cbret, /* Reply eg ... */ - void *arg, /* Client session */ - void *regarg) /* Argument given at register */ -{ - /* code that echoes the request */ - return 0; -} -int -clixon_plugin_init(clicon_handle h) -{ -... - rpc_callback_register(h, example_rpc, NULL, "example"); -... -} -``` - -## State data - -Netconf and restconf GET also returns state data(not only configuration data). - -In YANG state data is specified with `config false;`. In the example, -`state` is state data, see (example.yang)[example.yang] - -To return state data, you need to write a backend state data callback -with the name "plugin_statedata" where you return an XML tree with -state. This is then merged with config data by the system. - -A static example of returning state data is in the example. Note that -a real example would poll or get the interface counters via a system -call, as well as use the "xpath" argument to identify the requested -state data. - -The state data is enabled by starting the backend with: `-- -s`. - -## Authentication and NACM -The example contains some stubs for authorization according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341): -* 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). -* A NACM backend plugin reporting the mandatory NACM state variables. - -## Systemd - -Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example. - -## Docker - -See [../docker/system] for instructions on how to build this example -as a docker container. - -## Plugins - -The example includes a restonf, netconf, CLI and two backend plugins. -Each plugin is initiated with an API struct followed by a plugin init function. -The content of the API struct is different depending on what kind of plugin it is. -The plugin init function may also include registering RPC functions, see below is for a backend. -``` -static clixon_plugin_api api = { - "example", /* name */ - clixon_plugin_init, - plugin_start, - plugin_exit, - .ca_reset=plugin_reset,/* reset for extra XML at startup*/ - .ca_statedata=plugin_statedata, /* statedata */ - .ca_upgrade=example_upgrade, /* upgrade configuration */ - .ca_trans_begin=NULL, /* trans begin */ - .ca_trans_validate=transaction_validate,/* trans validate */ - .ca_trans_complete=NULL, /* trans complete */ - .ca_trans_commit=transaction_commit, /* trans commit */ - .ca_trans_end=NULL, /* trans end */ - .ca_trans_abort=NULL /* trans abort */ -}; - -clixon_plugin_api * -clixon_plugin_init(clicon_handle h) -{ - /* Optional callback registration for RPC calls */ - rpc_callback_register(h, example_rpc, NULL, "example"); - /* Return plugin API */ - return &api; /* Return NULL on error */ -} -``` - -Here is a corresponding example for a CLI plugin: -``` -static clixon_plugin_api api = { - "example", /* name */ - clixon_plugin_init, /* init */ - NULL, /* start */ - NULL, /* exit */ - .ca_prompt=NULL, /* cli_prompthook_t */ - .ca_suspend=NULL, /* cligen_susp_cb_t */ - .ca_interrupt=NULL, /* cligen_interrupt_cb_t */ -}; -``` \ No newline at end of file +Clixon have the following examples: + * [Main example](main/README.md) \ No newline at end of file diff --git a/example/main/Makefile.in b/example/main/Makefile.in new file mode 100644 index 00000000..d0e502b5 --- /dev/null +++ b/example/main/Makefile.in @@ -0,0 +1,159 @@ +# +# ***** BEGIN LICENSE BLOCK ***** +# +# Copyright (C) 2009-2019 Olof Hagsand and Benny Holmgren +# +# This file is part of CLIXON +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Alternatively, the contents of this file may be used under the terms of +# the GNU General Public License Version 3 or later (the "GPL"), +# in which case the provisions of the GPL are applicable instead +# of those above. If you wish to allow use of your version of this file only +# under the terms of the GPL, and not to allow others to +# use your version of this file under the terms of Apache License version 2, +# indicate your decision by deleting the provisions above and replace them with +# the notice and other provisions required by the GPL. If you do not delete +# the provisions above, a recipient may use your version of this file under +# the terms of any one of the Apache License version 2 or the GPL. +# +# ***** END LICENSE BLOCK ***** +# +VPATH = @srcdir@ +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +prefix = @prefix@ +bindir = @bindir@ +includedir = @includedir@ +datarootdir = @datarootdir@ +sysconfdir = @sysconfdir@ +datarootdir = @datarootdir@ +localstatedir = @localstatedir@ +libdir = @exec_prefix@/lib + +APPNAME = example +# Here is where example yang appears +CLIXON_DATADIR = @CLIXON_DATADIR@ +# Install here if you want default clixon location: +CLIXON_DEFAULT_CONFIG = @CLIXON_DEFAULT_CONFIG@ + +CC = @CC@ +CFLAGS = @CFLAGS@ -rdynamic -fPIC +INSTALLFLAGS = @INSTALLFLAGS@ +with_restconf = @with_restconf@ + +INCLUDES = -I$(includedir) @INCLUDES@ +CPPFLAGS = @CPPFLAGS@ -fPIC + +BE_PLUGIN = $(APPNAME)_backend.so +BE2_PLUGIN = $(APPNAME)_backend_nacm.so +CLI_PLUGIN = $(APPNAME)_cli.so +NETCONF_PLUGIN = $(APPNAME)_netconf.so +RESTCONF_PLUGIN = $(APPNAME)_restconf.so + +PLUGINS = $(BE_PLUGIN) $(BE2_PLUGIN) $(CLI_PLUGIN) $(NETCONF_PLUGIN) +ifeq ($(with_restconf),yes) +PLUGINS += $(RESTCONF_PLUGIN) +endif + +.PHONY: all clean depend install + +all: $(PLUGINS) + +.SUFFIXES: .c .o + +# implicit rule +.c.o: + $(CC) $(INCLUDES) $(CPPFLAGS) $(CFLAGS) -c $< + +CLISPECS = $(APPNAME)_cli.cli + +YANGSPECS = clixon-example@2019-01-13.yang + +# Backend plugin +BE_SRC = $(APPNAME)_backend.c +BE_OBJ = $(BE_SRC:%.c=%.o) +$(BE_PLUGIN): $(BE_OBJ) + $(CC) -Wall -shared -o $@ -lc $< + +# Secondary NACM backend plugin +BE2_SRC = $(APPNAME)_backend_nacm.c +BE2_OBJ = $(BE2_SRC:%.c=%.o) +$(BE2_PLUGIN): $(BE2_OBJ) + $(CC) -Wall -shared -o $@ -lc $< + +# CLI frontend plugin +CLI_SRC = $(APPNAME)_cli.c +CLI_OBJ = $(CLI_SRC:%.c=%.o) +$(CLI_PLUGIN): $(CLI_OBJ) + $(CC) -Wall -shared -o $@ -lc $^ + +# NETCONF frontend plugin +NETCONF_SRC = $(APPNAME)_netconf.c +NETCONF_OBJ = $(NETCONF_SRC:%.c=%.o) +$(NETCONF_PLUGIN): $(NETCONF_OBJ) + $(CC) -Wall -shared -o $@ -lc $^ + +# See configure.ac +# RESTCONF frontend plugin +RESTCONF_SRC = $(APPNAME)_restconf.c +RESTCONF_OBJ = $(RESTCONF_SRC:%.c=%.o) +$(RESTCONF_PLUGIN): $(RESTCONF_OBJ) + $(CC) -Wall -shared -o $@ -lc $^ + +SRC = $(BE_SRC) $(BE2_SRC) $(CLI_SRC) $(NETCONF_SRC) +SRC += $(RESTCONF_SRC) + +OBJS = $(BE_OBJ) $(BE2_OBJ) $(CLI_OBJ) $(NETCONF_OBJ) +OBJS += $(RESTCONF_OBJ) + +clean: + rm -f $(PLUGINS) $(OBJS) + +distclean: clean + rm -f Makefile *~ .depend + +install: $(YANGSPECS) $(CLISPECS) $(PLUGINS) $(APPNAME).xml + install -d -m 0755 $(DESTDIR)$(sysconfdir) + install -m 0644 $(APPNAME).xml $(DESTDIR)$(sysconfdir) +# install -m 0644 $(APPNAME).xml $(DESTDIR)$(CLIXON_DEFAULT_CONFIG) + install -d -m 0755 $(DESTDIR)$(datarootdir)/$(APPNAME)/yang + install -m 0644 $(YANGSPECS) $(DESTDIR)$(DESTDIR)$(CLIXON_DATADIR) + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/cli + install -m 0644 $(INSTALLFLAGS) $(CLI_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/cli + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/backend + install -m 0644 $(INSTALLFLAGS) $(BE_PLUGIN) $(BE2_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/backend + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/netconf + install -m 0644 $(INSTALLFLAGS) $(NETCONF_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/netconf +ifeq ($(with_restconf),yes) + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/restconf + install -m 0644 $(INSTALLFLAGS) $(RESTCONF_PLUGIN) $(DESTDIR)$(libdir)/$(APPNAME)/restconf +endif + install -d -m 0755 $(DESTDIR)$(libdir)/$(APPNAME)/clispec + install -m 0644 $(CLISPECS) $(DESTDIR)$(libdir)/$(APPNAME)/clispec + install -d -m 0755 $(DESTDIR)$(localstatedir)/$(APPNAME) + +uninstall: + rm -rf $(DESTDIR)$(sysconfdir)/$(APPNAME).xml + rm -rf $(DESTDIR)$(datarootdir)/$(APPNAME) + rm -rf $(DESTDIR)$(localstatedir)/$(APPNAME) + rm -rf $(DESTDIR)$(libdir)/$(APPNAME) + +install-include: + +depend: + $(CC) $(DEPENDFLAGS) $(INCLUDES) $(CFLAGS) -MM $(SRC) > .depend + +#include .depend + diff --git a/example/main/README.md b/example/main/README.md new file mode 100644 index 00000000..e55d46eb --- /dev/null +++ b/example/main/README.md @@ -0,0 +1,325 @@ +# Clixon main example + + * [Content](#content) + * [Compile and run](#compile) + * [Using the CLI](#using-the-cli) + * [Using netconf](#using-netconf) + * [Streams](#streams) + * [RPC Operations](#rpc-operations) + * [State data](#state-data) + * [Authentication and NACM](#authentication-and-nacm) + * [Systemd](#systemd) + * [Docker](#docker) + * [Plugins](#plugins) + +## Content + +This directory contains a Clixon example which includes a simple example. It contains the following files: +* `example.xml` The configuration file. See [yang/clixon-config@.yang](../../yang/clixon-config@2018-10-21.yang) for the documentation of all available fields. +* `clixon-example@2019-01-13.yang` The yang spec of the example. +* `example_cli.cli` CLIgen specification. +* `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`). +* `example_backend.c` Backend callback plugin including example of: + * transaction callbacks (validate/commit), + * notification, + * rpc handler + * state-data handler, ie non-config data +* `example_backend_nacm.c` Secondary backend plugin. Plugins are loaded alphabetically. +* `example_restconf.c` Restconf callback plugin containing an HTTP basic authentication callback +* `example_netconf.c` Netconf callback plugin +* `Makefile.in` Example makefile where plugins are built and installed + + +## Compile and run + +Before you start, +* Make [group setup](../../doc/FAQ.md#do-i-need-to-setup-anything-important) +* Setup [restconf](../../doc/FAQ.md#how-do-i-use-restconf) + +``` + cd example + make && sudo make install +``` +Start backend: +``` + sudo clixon_backend -f /usr/local/etc/example.xml -s init +``` +Edit cli: +``` + clixon_cli -f /usr/local/etc/example.xml +``` +Send netconf command: +``` + clixon_netconf -f /usr/local/etc/example.xml +``` +Start clixon restconf daemon +``` + sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data +``` +Send restconf command +``` + curl -G http://127.0.0.1/restconf/data +``` + +## Using the CLI + +The example CLI allows you to modify and view the data model using `set`, `delete` and `show` via generated code. +There are also many other commands available as examples. View the source file (example_cli.cli)[example_cli.cli] for more details. + +The 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. +``` +clixon_cli -f /usr/local/etc/example.xml +cli> set interfaces interface eth9 ? + description enabled ipv4 + ipv6 link-up-down-trap-enable type +cli> set interfaces interface eth9 type ex:eth +cli> validate +cli> commit +cli> show configuration xml + + + eth9 + ex:eth + true + + +cli> delete interfaces interface eth9 +``` + +## Using Netconf + +The following example shows how to set data using netconf: +``` + + + + eth1 + true + +
+ 9.2.3.4 + 24 +
+
+
+
+
]]>]]> +``` + +### Getting data using netconf +``` +]]>]]> +]]>]]> +]]>]]> +eth9ex:eth]]>]]> +]]>]]> +]]>]]> +``` +## Restconf + +Setup a web/reverse-proxy server. +For example, using nginx, install, and edit config file: /etc/nginx/sites-available/default: +``` +server { + ... + location / { + root /usr/share/nginx/html/restconf; + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + } + location /restconf { + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + } + location /streams { + fastcgi_pass unix:/www-data/fastcgi_restconf.sock; + include fastcgi_params; + proxy_http_version 1.1; + proxy_set_header Connection ""; + } +} +``` +Start nginx daemon +``` +sudo /etc/init.d/nginx start +``` +Start the clixon restconf daemon +``` +sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data +``` +then access using curl or wget: +``` + curl -G http://127.0.0.1/restconf/data/ietf-interfaces:interfaces/interface=eth9/type +``` + +## Streams + +The example has an EXAMPLE stream notification triggering every 5s. To start a notification +stream in the session using netconf, create a subscription: +``` +EXAMPLE]]>]]> +]]>]]> +2019-01-02T10:20:05.929272faultEthernet0major]]>]]> +... +``` +This can also be triggered via the CLI: +``` +clixon_cli -f /usr/local/etc/example.xml +cli> notify +cli> event-class fault; +reportingEntity { + card Ethernet0; +} +severity major; +... +cli> no notify +cli> +``` + +Restconf support is also supported, see (restc)[../../apps/restconf/README.md]. + + +## RPC Operations + +Clixon implements Yang RPC operations by an extension mechanism. The +extension mechanism enables you to add application-specific +operations. It works by adding user-defined callbacks for added +netconf operations. It is possible to use the extension mechanism +independent of the yang rpc construct, but it is recommended. The example includes an example: + +Example using CLI: +``` +clixon_cli -f /usr/local/etc/example.xml +cli> rpc ipv4 +ipv442 +``` +Example using Netconf: +``` +clixon_netconf -qf /usr/local/etc/example.xml +ipv4]]>]]> +ipv442]]>]]> +``` +Restconf (assuming nginx started): +``` +sudo su -c "/www-data/clixon_restconf -f /usr/local/etc/example.xml " -s /bin/sh www-data& +curl -X POST http://localhost/restconf/operations/clixon-example:example -d '{"clixon-example:input":{"x":"ipv4"}}' +{ + "clixon-example:output": { + "x": "ipv4", + "y": "42" + } +} +``` + +### Details + +The example works by defining an RPC in clixon-example.yang: +``` + rpc example { + description "Some example input/output for testing RFC7950 7.14. + RPC simply echoes the input for debugging."; + input { + leaf x { + ... +``` + +In the CLI a netconf rpc call is constructed and sent to the backend: See `example_client_rpc()` in [example_cli.c] CLI plugin. + +The clixon backend plugin [example_backend.c] reveives the netconf call and replies. This is made byregistering a callback handling handling the RPC: +``` +static int +example_rpc(clicon_handle h, + cxobj *xe, /* Request: */ + cbuf *cbret, /* Reply eg ... */ + void *arg, /* Client session */ + void *regarg) /* Argument given at register */ +{ + /* code that echoes the request */ + return 0; +} +int +clixon_plugin_init(clicon_handle h) +{ +... + rpc_callback_register(h, example_rpc, NULL, "example"); +... +} +``` + +## State data + +Netconf and restconf GET also returns state data(not only configuration data). + +In YANG state data is specified with `config false;`. In the example, +`state` is state data, see (example.yang)[example.yang] + +To return state data, you need to write a backend state data callback +with the name "plugin_statedata" where you return an XML tree with +state. This is then merged with config data by the system. + +A static example of returning state data is in the example. Note that +a real example would poll or get the interface counters via a system +call, as well as use the "xpath" argument to identify the requested +state data. + +The state data is enabled by starting the backend with: `-- -s`. + +## Authentication and NACM +The example contains some stubs for authorization according to [RFC8341(NACM)](https://tools.ietf.org/html/rfc8341): +* 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). +* A NACM backend plugin reporting the mandatory NACM state variables. + +## Systemd + +Example systemd files for backend and restconf daemons are found under the systemd directory. Install them under /etc/systemd/system for example. + +## Docker + +See [../../docker/system] for instructions on how to build this example +as a docker container. + +## Plugins + +The example includes a restonf, netconf, CLI and two backend plugins. +Each plugin is initiated with an API struct followed by a plugin init function. +The content of the API struct is different depending on what kind of plugin it is. +The plugin init function may also include registering RPC functions, see below is for a backend. +``` +static clixon_plugin_api api = { + "example", /* name */ + clixon_plugin_init, + plugin_start, + plugin_exit, + .ca_reset=plugin_reset,/* reset for extra XML at startup*/ + .ca_statedata=plugin_statedata, /* statedata */ + .ca_upgrade=example_upgrade, /* upgrade configuration */ + .ca_trans_begin=NULL, /* trans begin */ + .ca_trans_validate=transaction_validate,/* trans validate */ + .ca_trans_complete=NULL, /* trans complete */ + .ca_trans_commit=transaction_commit, /* trans commit */ + .ca_trans_end=NULL, /* trans end */ + .ca_trans_abort=NULL /* trans abort */ +}; + +clixon_plugin_api * +clixon_plugin_init(clicon_handle h) +{ + /* Optional callback registration for RPC calls */ + rpc_callback_register(h, example_rpc, NULL, "example"); + /* Return plugin API */ + return &api; /* Return NULL on error */ +} +``` + +Here is a corresponding example for a CLI plugin: +``` +static clixon_plugin_api api = { + "example", /* name */ + clixon_plugin_init, /* init */ + NULL, /* start */ + NULL, /* exit */ + .ca_prompt=NULL, /* cli_prompthook_t */ + .ca_suspend=NULL, /* cligen_susp_cb_t */ + .ca_interrupt=NULL, /* cligen_interrupt_cb_t */ +}; +``` \ No newline at end of file diff --git a/example/clixon-example@2019-01-13.yang b/example/main/clixon-example@2019-01-13.yang similarity index 100% rename from example/clixon-example@2019-01-13.yang rename to example/main/clixon-example@2019-01-13.yang diff --git a/example/example.xml b/example/main/example.xml similarity index 100% rename from example/example.xml rename to example/main/example.xml diff --git a/example/example_backend.c b/example/main/example_backend.c similarity index 100% rename from example/example_backend.c rename to example/main/example_backend.c diff --git a/example/example_backend_nacm.c b/example/main/example_backend_nacm.c similarity index 100% rename from example/example_backend_nacm.c rename to example/main/example_backend_nacm.c diff --git a/example/example_cli.c b/example/main/example_cli.c similarity index 100% rename from example/example_cli.c rename to example/main/example_cli.c diff --git a/example/example_cli.cli b/example/main/example_cli.cli similarity index 100% rename from example/example_cli.cli rename to example/main/example_cli.cli diff --git a/example/example_netconf.c b/example/main/example_netconf.c similarity index 100% rename from example/example_netconf.c rename to example/main/example_netconf.c diff --git a/example/example_restconf.c b/example/main/example_restconf.c similarity index 100% rename from example/example_restconf.c rename to example/main/example_restconf.c diff --git a/example/systemd/example.service b/example/main/systemd/example.service similarity index 100% rename from example/systemd/example.service rename to example/main/systemd/example.service diff --git a/example/systemd/example_restconf.service b/example/main/systemd/example_restconf.service similarity index 100% rename from example/systemd/example_restconf.service rename to example/main/systemd/example_restconf.service