#include <stdlib.h>
#include <sysrepo.h>
#include <uv.h>
+#include <libyang/libyang.h>
#include "lib/module.h"
#include "common/sysrepo_conf.h"
};
/** Callback to Lua used for applying configuration */
-static set_leaf_conf_t apply_conf_f = NULL;
+static apply_conf_f apply_conf = NULL;
+/** Callback to Lua used for reading configuration */
+static read_conf_f read_conf = NULL;
static el_subscription_ctx_t *el_subscription_ctx = NULL;
/**
// declining the change now
int sr_err = SR_ERR_OK;
- sr_change_iter_t *it = NULL;
- sr_change_oper_t oper;
- sr_val_t *old_value = NULL;
- sr_val_t *new_value = NULL;
+ struct lyd_node* tree;
- // get all changes
- sr_err = sr_get_changes_iter(session, XPATH_BASE "//.", &it);
+ // get the whole config tree
+ sr_err = sr_get_data(session, XPATH_BASE, 0, 0, 0, &tree);
if (sr_err != SR_ERR_OK)
goto cleanup;
- while ((sr_get_change_next(session, it, &oper, &old_value,
- &new_value)) == SR_ERR_OK) {
- apply_conf_f(new_value);
- }
+ // apply the configuration
+ apply_conf(tree);
cleanup:
if (sr_err != SR_ERR_OK && sr_err != SR_ERR_NOT_FOUND)
kr_log_error("Sysrepo module error: %s\n",
sr_strerror(sr_err));
- sr_free_val(old_value);
- sr_free_val(new_value);
- sr_free_change_iter(it);
+ lyd_free_withsiblings(tree); // FIXME sure about this?
}
return SR_ERR_OK;
}
sr_process_events(el_subscr->subscription, el_subscr->session, NULL);
}
-int sysrepo_init(set_leaf_conf_t apply_conf_callback)
+int sysrepo_init(apply_conf_f apply_conf_callback)
{
// store callback to Lua
- apply_conf_f = apply_conf_callback;
+ apply_conf = apply_conf_callback;
int sr_err = SR_ERR_OK;
sr_conn_ctx_t *sr_connection = NULL;
{
el_subscription_free(el_subscription_ctx);
// remove reference to Lua callback so that it can be free'd safely
- apply_conf_f = NULL;
+ apply_conf = NULL;
return kr_ok();
}
+
+KR_EXPORT struct lyd_node* node_child_first(struct lyd_node* parent) {
+ assert(
+ parent->schema->nodetype == LYS_CONTAINER ||
+ parent->schema->nodetype == LYS_LIST ||
+ parent->schema->nodetype == LYS_CHOICE
+ );
+
+ return parent->child;
+}
+
+KR_EXPORT struct lyd_node* node_child_next(struct lyd_node* prev_child) {
+ return prev_child->next;
+}
+
+KR_EXPORT const char* node_get_name(struct lyd_node* node) {
+ return node->schema->name;
+}
+
+KR_EXPORT const char* node_get_value_str(struct lyd_node* node) {
+ assert(
+ node->schema->nodetype == LYS_LEAF ||
+ node->schema->nodetype == LYS_LEAFLIST
+ );
+
+ return ((struct lyd_node_leaf_list*) node)->value_str;
+}
local function initialize_ffi()
--- Definition of `sr_val_t` copied from sysrepo.h on 2020-01-01.
ffi.cdef[[
- /**
- * @brief Possible types of a data element stored in the sysrepo datastore.
- */
- typedef enum sr_type_e {
- /* special types that does not contain any data */
- SR_UNKNOWN_T, /**< Element unknown to sysrepo (unsupported element). */
-
- SR_LIST_T, /**< List instance. ([RFC 7950 sec 7.8](http://tools.ietf.org/html/rfc7950#section-7.8)) */
- SR_CONTAINER_T, /**< Non-presence container. ([RFC 7950 sec 7.5](http://tools.ietf.org/html/rfc7950#section-7.5)) */
- SR_CONTAINER_PRESENCE_T, /**< Presence container. ([RFC 7950 sec 7.5.1](http://tools.ietf.org/html/rfc7950#section-7.5.1)) */
- SR_LEAF_EMPTY_T, /**< A leaf that does not hold any value ([RFC 7950 sec 9.11](http://tools.ietf.org/html/rfc7950#section-9.11)) */
- SR_NOTIFICATION_T, /**< Notification instance ([RFC 7095 sec 7.16](https://tools.ietf.org/html/rfc7950#section-7.16)) */
-
- /* types containing some data */
- SR_BINARY_T, /**< Base64-encoded binary data ([RFC 7950 sec 9.8](http://tools.ietf.org/html/rfc7950#section-9.8)) */
- SR_BITS_T, /**< A set of bits or flags ([RFC 7950 sec 9.7](http://tools.ietf.org/html/rfc7950#section-9.7)) */
- SR_BOOL_T, /**< A boolean value ([RFC 7950 sec 9.5](http://tools.ietf.org/html/rfc7950#section-9.5)) */
- SR_DECIMAL64_T, /**< 64-bit signed decimal number ([RFC 7950 sec 9.3](http://tools.ietf.org/html/rfc7950#section-9.3)) */
- SR_ENUM_T, /**< A string from enumerated strings list ([RFC 7950 sec 9.6](http://tools.ietf.org/html/rfc7950#section-9.6)) */
- SR_IDENTITYREF_T, /**< A reference to an abstract identity ([RFC 7950 sec 9.10](http://tools.ietf.org/html/rfc7950#section-9.10)) */
- SR_INSTANCEID_T, /**< References a data tree node ([RFC 7950 sec 9.13](http://tools.ietf.org/html/rfc7950#section-9.13)) */
- SR_INT8_T, /**< 8-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- SR_INT16_T, /**< 16-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- SR_INT32_T, /**< 32-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- SR_INT64_T, /**< 64-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- SR_STRING_T, /**< Human-readable string ([RFC 7950 sec 9.4](http://tools.ietf.org/html/rfc7950#section-9.4)) */
- SR_UINT8_T, /**< 8-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- SR_UINT16_T, /**< 16-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- SR_UINT32_T, /**< 32-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- SR_UINT64_T, /**< 64-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- SR_ANYXML_T, /**< Unknown chunk of XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
- SR_ANYDATA_T, /**< Unknown set of nodes, encoded in XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
- } sr_type_t;
-
- /**
- * @brief Data of an element (if applicable), properly set according to the type.
- */
- typedef union sr_data_u {
- char *binary_val; /**< Base64-encoded binary data ([RFC 7950 sec 9.8](http://tools.ietf.org/html/rfc7950#section-9.8)) */
- char *bits_val; /**< A set of bits or flags ([RFC 7950 sec 9.7](http://tools.ietf.org/html/rfc7950#section-9.7)) */
- bool bool_val; /**< A boolean value ([RFC 7950 sec 9.5](http://tools.ietf.org/html/rfc7950#section-9.5)) */
- double decimal64_val; /**< 64-bit signed decimal number ([RFC 7950 sec 9.3](http://tools.ietf.org/html/rfc7950#section-9.3)) */
- char *enum_val; /**< A string from enumerated strings list ([RFC 7950 sec 9.6](http://tools.ietf.org/html/rfc7950#section-9.6)) */
- char *identityref_val; /**< A reference to an abstract identity ([RFC 7950 sec 9.10](http://tools.ietf.org/html/rfc7950#section-9.10)) */
- char *instanceid_val; /**< References a data tree node ([RFC 7950 sec 9.13](http://tools.ietf.org/html/rfc7950#section-9.13)) */
- int8_t int8_val; /**< 8-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- int16_t int16_val; /**< 16-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- int32_t int32_val; /**< 32-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- int64_t int64_val; /**< 64-bit signed integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- char *string_val; /**< Human-readable string ([RFC 7950 sec 9.4](http://tools.ietf.org/html/rfc7950#section-9.4)) */
- uint8_t uint8_val; /**< 8-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- uint16_t uint16_val; /**< 16-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- uint32_t uint32_val; /**< 32-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- uint64_t uint64_val; /**< 64-bit unsigned integer ([RFC 7950 sec 9.2](https://tools.ietf.org/html/rfc7950#section-9.2)) */
- char *anyxml_val; /**< Unknown chunk of XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
- char *anydata_val; /**< Unknown set of nodes, encoded in XML ([RFC 7950 sec 7.10](https://tools.ietf.org/html/rfc7950#section-7.10)) */
- } sr_data_t;
-
- /**
- * @brief Structure that contains value of an data element stored in the sysrepo datastore.
- */
- typedef struct sr_val_s {
- /** [XPath](@ref paths) (or rather path) identifier of the data element. */
- char *xpath;
-
- /** Type of an element. */
- sr_type_t type;
-
- /**
- * Flag for node with default value (applicable only for leaves).
- * It is set to TRUE only if the value was *implicitly* set by the datastore as per
- * module schema. Explicitly set/modified data element (through the sysrepo API) always
- * has this flag unset regardless of the entered value.
- */
- bool dflt;
-
- /** [Origin](@ref oper_ds) of the value. */
- char *origin;
-
- /** Data of an element (if applicable), properly set according to the type. */
- sr_data_t data;
-
- } sr_val_t;
-
- typedef void (*set_leaf_conf_t)(sr_val_t *val);
- int sysrepo_init(set_leaf_conf_t set_leaf_conf_cb);
+
+ typedef void (*apply_conf_f)(struct lyd_node *root);
+ typedef void (*read_conf_f)(struct lyd_node* root);
+ int sysrepo_init(apply_conf_f apply_conf);
int sysrepo_deinit(void);
+
+ /** Given a libyang node, returns it's first child */
+ struct lyd_node* node_child_first(struct lyd_node* parent);
+ /** Given a libyang node, return next sibling or NULL if there isn't any */
+ struct lyd_node* node_child_next(struct lyd_node* prev_child);
+ /** Given a libyang node, return it's name from schema */
+ const char* node_get_name(struct lyd_node* node);
+ /** Given a libyang node, return it's value as a string */
+ const char* node_get_value_str(struct lyd_node* node);
+
]]
end
-- TODO version check so that we can not load new module into an old Knot
--------------------------------------------------------------------------------
--- Data convertsion helpers
--------------------------------------------------------------------------------
-
---- Helper table for converting sr_data_t union type to string based on the provided type
-local _value_to_str_conversion_table = {
- ["SR_UNKNOWN_T"] = function(_) return "unknown value" end,
- ["SR_LIST_T"] = function(_) return "no value (list)" end,
- ["SR_CONTAINER_T"] = function(_) return "no value (container)" end,
- ["SR_CONTAINER_PRESENCE_T"] = function(_) return "container (container presence)" end,
- ["SR_LEAF_EMPTY_T"] = function(_) return "empty (empty leaf)" end,
- ["SR_NOTIFICATION_T"] = function(_) return "no value (notification)" end,
- ["SR_BINARY_T"] = function(val) return ffi.string(val.binary_val) .. " (binary)" end,
- ["SR_BITS_T"] = function(_) return "??? (bits)" end,
- ["SR_BOOL_T"] = function(val) return tostring(val.bool_val) .. " (bool)" end,
- ["SR_DECIMAL64_T"] = function(val) return tostring(val.decimal64_val) .. "(decimal64)" end,
- ["SR_ENUM_T"] = function(val) return ffi.string(val.enum_val) .. " (enum)" end,
- ["SR_IDENTITYREF_T"] = function(val) return ffi.string(val.enum_val) .. " (indentityref)" end,
- ["SR_INSTANCEID_T"] = function(val) return ffi.string(val.enum_val) .. " (instanceid)" end,
- ["SR_INT8_T"] = function(val) return tostring(val.int8_val) .. " (int8)" end,
- ["SR_INT16_T"] = function(val) return tostring(val.int16_val) .. " (int16)" end,
- ["SR_INT32_T"] = function(val) return tostring(val.int32_val) .. " (int32)" end,
- ["SR_INT64_T"] = function(val) return tostring(val.int64_val) .. " (int64)" end,
- ["SR_STRING_T"] = function(val) return ffi.string(val.string_val) .. " (string)" end,
- ["SR_UINT8_T"] = function(val) return tostring(val.uint8_val) .. " (int8)" end,
- ["SR_UINT16_T"] = function(val) return tostring(val.uint16_val) .. " (uint16)" end,
- ["SR_UINT32_T"] = function(val) return tostring(val.uint32_val) .. " (uint32)" end,
- ["SR_UINT64_T"] = function(val) return tostring(val.uint64_val) .. " (uint64)" end,
- ["SR_ANYXML_T"] = function(val) return ffi.string(val.anyxml_val) .. " (anyxml)" end,
- ["SR_ANYDATA_T"] = function(val) return ffi.string(val.anydata_val) .. " (anydata)" end,
-}
-
---- Convert from type sr_type_t into a uppercase string.
--- @param sr_type_t value. In case of wrong type, the function crashes the whole runtime.
--- @return string value of the enum
-local function type_to_str(tp)
- if (tp == "SR_UNKNOWN_T") then return "SR_UNKNOWN_T"
- elseif (tp == "SR_LIST_T") then return "SR_LIST_T"
- elseif (tp == "SR_CONTAINER_T") then return "SR_CONTAINER_T"
- elseif (tp == "SR_CONTAINER_PRESENCE_T") then return "SR_CONTAINER_PRESENCE_T"
- elseif (tp == "SR_LEAF_EMPTY_T") then return "SR_LEAF_EMPTY_T"
- elseif (tp == "SR_NOTIFICATION_T") then return "SR_NOTIFICATION_T"
- elseif (tp == "SR_BINARY_T") then return "SR_BINARY_T"
- elseif (tp == "SR_BITS_T") then return "SR_BITS_T"
- elseif (tp == "SR_BOOL_T") then return "SR_BOOL_T"
- elseif (tp == "SR_DECIMAL64_T") then return "SR_DECIMAL64_T"
- elseif (tp == "SR_ENUM_T") then return "SR_ENUM_T"
- elseif (tp == "SR_IDENTITYREF_T") then return "SR_IDENTITYREF_T"
- elseif (tp == "SR_INSTANCEID_T") then return "SR_INSTANCEID_T"
- elseif (tp == "SR_INT8_T") then return "SR_INT8_T"
- elseif (tp == "SR_INT16_T") then return "SR_INT16_T"
- elseif (tp == "SR_INT32_T") then return "SR_INT32_T"
- elseif (tp == "SR_INT64_T") then return "SR_INT64_T"
- elseif (tp == "SR_STRING_T") then return "SR_STRING_T"
- elseif (tp == "SR_UINT8_T") then return "SR_UINT8_T"
- elseif (tp == "SR_UINT16_T") then return "SR_UINT16_T"
- elseif (tp == "SR_UINT32_T") then return "SR_UINT32_T"
- elseif (tp == "SR_UINT64_T") then return "SR_UINT64_T"
- elseif (tp == "SR_ANYXML_T") then return "SR_ANYXML_T"
- elseif (tp == "SR_ANYDATA_T") then return "SR_ANYDATA_T"
- else
- error("unexpected value of sr_type_t enum")
- end
-end
-
-
-------------------------------------------------------------------------------
-- Callback management
-------------------------------------------------------------------------------
local sysrepo_ffi = {}
function sysrepo_ffi.init(apply_conf_func)
- local cb = create_callback("set_leaf_conf_t", apply_conf_func)
+ local cb = create_callback("apply_conf_f", apply_conf_func)
local res = clib.sysrepo_init(cb)
if res ~= 0 then
error("Initialization failed with error code " .. tostring(res))
end
end
---- Converts from cdata sr_val_t to table (using table is slower, but safer)
-function sysrepo_ffi.sr_val_to_table(val)
- local tbl = {
- dflt = val.dflt,
- xpath = ffi.string(val.xpath),
- type = type_to_str(val.type),
- _value = val.data,
- }
- setmetatable(tbl, {
- __tostring = function(slf)
- return string.format(
- "{\n\txpath = '%s',\n\ttype = '%s',\n\tdflt = '%s',\n\tdata = '%s'\n}",
- slf.xpath,
- slf.type,
- tostring(slf.dflt),
- _value_to_str_conversion_table[type_to_str(slf.type)](slf._value)
- )
- end
- })
- return tbl
+function sysrepo_ffi.get_clib_bindings()
+ return clib
end
return sysrepo_ffi
--- /dev/null
+local debug = require("kres_modules/sysrepo-lua/debug")
+local ffi = require("ffi")
+
+local Node = {}
+Node.__index = Node
+
+local _clib = nil
+local function clib()
+ assert(_clib ~= nil)
+ return _clib
+end
+
+--- Tree node for representing a vertex in configuration model tree
+---
+--- Nodes can be read by node:read(data_node) and written by node:write(parent_data_node)
+---
+--- @param name Name of the vertex for constructing XPath
+--- @param read_func Function which takes self and data node from libyang and applies the configuration to the system
+--- @param write_func Function which takes self and data node from libyang and adds a child to it with data from the system
+function Node:create(name, read_func, write_func)
+ assert(type(name) == "string")
+ assert(type(read_func) == 'function')
+ assert(type(write_func) == 'function')
+
+ local handler = {}
+ setmetatable(handler, Node)
+
+ handler.read = read_func
+ handler.write = write_func
+ handler.name = name
+
+ return handler
+end
+
+local function DummyLeafNode(name, ignore_value)
+ local function dummy_read(self, node)
+ if ignore_value then
+ debug.log(
+ "Dummy read on node named \"{}\", actual name \"{}\"",
+ self.name,
+ ffi.string(clib().node_get_name(node))
+ )
+ else
+ debug.log(
+ "Dummy read on node named \"{}\", actual name \"{}\". Contains value (as a string) \"{}\"",
+ self.name,
+ ffi.string(clib().node_get_name(node)),
+ ffi.string(clib().node_get_value_str(node))
+ )
+ end
+ end
+
+ local function dummy_write(self, node)
+ debug.log("dummy write on node named {}", self.name)
+ end
+
+ return Node:create(name, dummy_read, dummy_write)
+end
+
+local function ContainerNode(name, container_model)
+ -- optimize child lookup by name with table
+ local child_lookup_table = {}
+ for _,v in ipairs(container_model) do
+ child_lookup_table[v.name] = v
+ end
+
+ --- Node's read function
+ local function handle_cont_read(self, node)
+ local node_name = ffi.string(clib().node_get_name(node))
+ debug.log("Attempting to read container \"{}\", it's actual name is \"{}\"", self.name, node_name)
+ assert(node_name == self.name)
+
+ local child = clib().node_child_first(node)
+ while child ~= nil do
+ local nm = ffi.string(clib().node_get_name(child))
+ child_lookup_table[nm]:read(child)
+ child = clib().node_child_next(child)
+ end
+ end
+
+ --- Node's write function
+ local function handle_cont_write(self, parent_node)
+ local node = nil -- TODO get current node from parent_node
+
+ for _,v in ipairs(container_model) do
+ v:write(node)
+ end
+ end
+
+ return Node:create(name, handle_cont_read, handle_cont_write)
+end
+
+
+--- Configuration schema reprezentation
+local model =
+ ContainerNode("dns-resolver", {
+ ContainerNode("cache", {
+ DummyLeafNode("current-size"),
+ DummyLeafNode("max-size"),
+ DummyLeafNode("max-ttl"),
+ DummyLeafNode("min-ttl"),
+ DummyLeafNode("prefill"),
+ }),
+ DummyLeafNode("debugging", true),
+ DummyLeafNode("dns64", true),
+ DummyLeafNode("dnssec", true),
+ DummyLeafNode("garbage-collector", true),
+ DummyLeafNode("logging", true),
+ DummyLeafNode("network", true),
+ DummyLeafNode("resolver", true),
+ DummyLeafNode("server", true),
+ })
+
+
+--- Module constructor
+return function(clib_binding)
+ _clib = clib_binding
+
+ local module = {}
+ function module.serialize_configuration(root_node)
+ model:write(root_node)
+ end
+
+ function module.apply_configuration(root_node)
+ model:read(root_node)
+ end
+
+ return module
+end
+