Skip to content
mislavn edited this page Dec 10, 2015 · 6 revisions

configctl

Introduction

In today’s IT world, every daemon has its own configuration style. Some use XML, some use JSON, some use INI format, whereas some use the format appointed by the daemon developer which seemed to suit the best to the daemon in question.

The problem which arises under such a condition is that if certain configuration processes wish to be automatized, then the config file needs to be parsed. Depending on the file syntax and type, such parsing often presents a very time consuming process. An additional difficulty often is the lack of documentation on an open source configuration file and its options.

The third problem is that the config file cannot be validated before the daemon which uses it restarts itself or tries to reload the configuration. In most cases, configuration file management process workflow follows these three steps:

  • First the user or administrator installs the application.

  • Then the config file is written from scratch or the configuration which came with the program is changed via text editors such as vim, emacs, nano, and so on.

  • The program is then started or restarted to check if everything is all right with the configuration. In case there were no errors the loaded configuration is used.

Seems like a pretty cumbersome process, right? Well, the configctl project aims to take most of that weight out of your hands. The configctl is part of the sysrepo project umbrella specifically in charge of the configuration file management.

With configctl we want to change the existing processes for the better. Our project not only enables to check if the configuration is valid without the need for the daemon to do the checking, or removes the dependency to the format (XML, JSON, etc.) which was used for the configuration, but it also saves all changes made to the config file which implies that users can easily revert to an older version and have an up-to-date documentation on the config files and its options. Furthermore, the project’s aim is for all of this to be done automatically via programs.

At this point in time the configctl uses XML to store configuration data with limited GIT integration but we are aiming to improve this in the near future.

configctl command line utility

The configctl library comes with a handy configctl command line utility. The tool itself at the moment understands the following options:

$ configctl
Usage: configctl [-h] [-c file] [-y file] [-e location] [-p dir]

 -c, --config   Set config file (required)
 -y, --model    Set yang model (required)
 -e, --editor   Set editor (required)
 -p, --path     Set yang model search path
 -h, --help     Show this text

Taking that into account, the configuration file management workflow would resemble the afore-mentioned one, but would be much more powerful and controllable. Only the second step would be changed with something like this:

configctl -c /path/to/config/file -y /path/to/yang/model/in/yin/format.yin -e /usr/bin/vim

After the above command an text editor is launched in which the user can edit the configuration. Once the configuration is changed and saved the tool will validate if the new configuration file is conforming with the YANG model. At this point we have two options which depends on the explained validation.

In case configuration file is good the user will be prompted if he wishes to proceed and save the configuration. In case the configuration is not valid the user is prompted with a different menu where he can either try to edit again the configuration file or he can discard the changes. Of course, configctl shows additional information on the type of error so to the end user can take appropriate actions.

configctl C API

C API for configctl is available in this header. The easiest way to get started would be to look into the cmocka test examples.

Below you can find a basic example:

#include <stdio.h>
#include <stdlib.h>

#include <configctl.h>

int main(int argc, char **argv)
{
	struct configctl *ctx = NULL;
	const char *result_string;
	int32_t result_int32;
	int rc;

	ctx = calloc(1, sizeof(struct configctl));
	if (!ctx)
		return -1;

	rc = configctl_init(ctx, "../tests/config/hello.xml", "../tests/config/hello@2015-06-08.yin", "../tests/config/");
	if (rc) {
		printf("error in configctl_init()\n");
		return -1;
	}

	result_string = configctl_get_string(ctx, "hello/foo");
	if (!result_string) {
		printf("error in configctl_get_string()\n");
		return -1;
	}

	printf("hello/foo: '%s'\n", result_string);

	rc = configctl_get_int32(ctx, "hello/bar", &result_int32);
	if (!rc)
		printf("hello/bar: '%d'\n", result_int32);

	rc = configctl_set_int32(ctx, "hello/bar", 50);
	if (rc) {
		printf("error in configctl_set_int32()\n");
		return -1;
	}

	rc = configctl_get_int32(ctx, "hello/bar", &result_int32);
	if (!rc) {
		if (result_int32 != 50) {
			printf("error in configctl_get_int32()\n");
		}
	}

	printf("hello/bar: '%d'\n", result_int32);

	configctl_destroy(ctx);

	return 0;
}

In case you are using this example you can build it like this:

gcc -I ../src -L . -lconfigctl example.c -o example
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
./example

configctl Python examples

The configctl library comes with Python 2 and Python 3 bindings.

cd build
# build for either Python 2 bindings
cmake -DPYTHON_BINDING="2" ..
# or build for Python 3 bindings
cmake -DPYTHON_BINDING="3" ..

Examples below show how to use of these bindings. The first example is of course for the Python 2 while the second one is for Python 3.

#!/usr/bin/env python2

import os
import configctl_py2 as configctl

config_file = os.path.join(os.path.dirname(__file__), "../tests/config/hello.xml")
yang_file = os.path.join(os.path.dirname(__file__), "../tests/config/hello@2015-06-08.yin")
yang_folder = os.path.join(os.path.dirname(__file__), "../tests/config")

try:
    ctx = configctl.configctl_create()
    configctl.configctl_init(ctx, config_file, yang_file, yang_folder)
except:
    print("An error occured")

try:
    result = configctl.configctl_get_string(ctx, "hello/bar_error")
except:
    print("Success: Exception is thrown for geting non existing data!")

try:
    configctl.configctl_set_int8(ctx, "hello/number8", 123456)
except:
    print("Success: Exception is thrown for seting integer out of range!")

try:
    configctl.configctl_set_int8(ctx, "hello/number8", 12)
    result = configctl.configctl_get_int8(ctx, "hello/number8")
except:
    print("An error occured")
else:
    print("Succes: get hello/number8 ->" + str(result))

try:
    configctl.configctl_delete_element(ctx, "hello/foo")
    result = configctl.configctl_get_int8(ctx, "hello/foo")
except:
    print("Success: Exception is thrown for trying to get a node which was previously deleted!")

try:
    configctl.configctl_destroy(ctx)
except:
    print("An error occured")
else:
    print("Succes: freed context")
#!/usr/bin/env python3

import os
import configctl_py3 as configctl

config_file = os.path.join(os.path.dirname(__file__), "../tests/config/hello.xml")
yang_file = os.path.join(os.path.dirname(__file__), "../tests/config/hello@2015-06-08.yin")
yang_folder = os.path.join(os.path.dirname(__file__), "../tests/config")

try:
    ctx = configctl.configctl_create()
    configctl.configctl_init(ctx, config_file, yang_file, yang_folder)
except:
    print("An error occured")

try:
    result = configctl.configctl_get_string(ctx, "hello/foo")
except:
    print("An error occured")
else:
    print("Succes: get hello/foo -> " + result)

try:
    result = configctl.configctl_get_string(ctx, "hello/bar_error")
except:
    print("Success: Exception is thrown for geting non existing data!")

try:
    result = configctl.configctl_get_string(ctx, "hello/bar")
except:
    print("An error occured")
else:
    print("Succes: get hello/bar -> " + result)

try:
    configctl.configctl_set_int8(ctx, "hello/number8", 123456)
except:
    print("Success: Exception is thrown for seting integer out of range!")

try:
    configctl.configctl_set_int8(ctx, "hello/number8", 12)
    result = configctl.configctl_get_int8(ctx, "hello/number8")
except:
    print("An error occured")
else:
    print("Succes: get hello/number8 ->" + str(result))

try:
    configctl.configctl_delete_element(ctx, "hello/foo")
    result = configctl.configctl_get_int8(ctx, "hello/foo")
except:
    print("Success: Exception is thrown for trying to get a node which was previously deleted!")

try:
    configctl.configctl_destroy(ctx)
except:
    print("An error occured")
else:
    print("Succes: freed context")

configctl Java example

Along with Python bindings we have also made the Java bindings as well. The sample code for Java is displayed here:

public class swig_example_java {
	public static void main(String argv[]) {
		System.loadLibrary("configctlJava");
		configctl ctx = new configctl();
		try {
			configctlJava.initConfigctl(ctx, "../tests/config/hello.xml", "../tests/config/hello@2015-06-08.yin", "../tests/config");
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

		try {
			String result = configctlJava.getConfigctlString(ctx, "hello/foo");
			System.out.println("get hello/foo -> " + result);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

		try {
			int result = configctlJava.getConfigctlInt32(ctx, "hello/bar_error");
			System.out.println(result);
		} catch (Exception e) {
			System.out.println("Success: Exception is thrown for geting non existing data!");
		}

		try {
			int result = configctlJava.getConfigctlInt32(ctx, "hello/bar");
			System.out.println("get hello/bar -> " + result);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

		try {
			configctlJava.setConfigctlInt8(ctx, "hello/number8", 123456);
			int result = configctlJava.getConfigctlInt8(ctx, "hello/number8");
			System.out.println(result);
		} catch (Exception e) {
			System.out.println("Success: Exception is thrown for seting integer out of range!");
		}

		try {
			configctlJava.setConfigctlInt8(ctx, "hello/number8", 123);
			int result = configctlJava.getConfigctlInt8(ctx, "hello/number8");
			System.out.println("get hello/number8 -> " + result);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

		try {
			configctlJava.deleteConfigctlElement(ctx, "hello/foo");
			String result = configctlJava.getConfigctlString(ctx, "hello/foo");
			System.out.println(result);
		} catch (Exception e) {
			System.out.println("Success: Exception is thrown for trying to get a node which was previously deleted!");
		}

		try {
			configctlJava.destroyConfigctl(ctx);
		} catch (Exception e) {
			System.out.println(e.getMessage());
		}

	}
}

In order to use it build the project with Java suport like this:

# before you start make sure that JAVA_HOME environment variable is set
# export JAVA_HOME=/usr/lib/jvm/java-7-openjdk
cd build
cmake -DJAVA_BINDING=ON ..
make
javac -cp ".:Configctl.jar" swig_example_java.java
java -cp ".:Configctl.jar" -Djava.library.path=.  swig_example_java