/** Callback to Lua used for reading configuration */
static read_conf_f read_conf = NULL;
static el_subscription_ctx_t *el_subscription_ctx = NULL;
+static const struct lys_node* lys_root = NULL;
/**
* Change callback getting called by sysrepo. Iterates over changed options and passes
return SR_ERR_OK;
}
+static int sysrepo_conf_read_callback(sr_session_ctx_t *session, const char *module_name, const char *path, const char *request_xpath, uint32_t request_id, struct lyd_node **parent, void *private_data) {
+ // *parent is expected to be NULL, because we are subscribed only on the top-level node
+ assert(*parent == NULL);
+ // so we can safely ignore it and overwrite it with our new tree
+ struct lyd_node *res = read_conf();
+ if (res == NULL)
+ return SR_ERR_CALLBACK_FAILED;
+
+ *parent = res;
+ return SR_ERR_OK;
+}
+
void el_subscr_finish_closing(uv_handle_t *handle)
{
el_subscription_ctx_t *el_subscr = handle->data;
sr_process_events(el_subscr->subscription, el_subscr->session, NULL);
}
-int sysrepo_init(apply_conf_f apply_conf_callback)
+int sysrepo_init(apply_conf_f apply_conf_callback, read_conf_f read_conf_callback)
{
// store callback to Lua
apply_conf = apply_conf_callback;
+ read_conf = read_conf_callback;
int sr_err = SR_ERR_OK;
sr_conn_ctx_t *sr_connection = NULL;
if (sr_err != SR_ERR_OK)
goto cleanup;
- /* register sysrepo subscriptions and callbacks
+ /* obtain schema root node, will be used from within Lua */
+ const struct ly_ctx* ly_context = sr_get_context(sr_connection);
+ lys_root = ly_ctx_get_node(ly_context, NULL, XPATH_BASE, 0);
+ assert(lys_root != NULL);
+
+ /* register sysrepo subscription and callback
SR_SUBSCR_NO_THREAD - don't create a thread for handling them
SR_SUBSCR_ENABLED - send us current configuration in a callback just after subscribing
*/
if (sr_err != SR_ERR_OK)
goto cleanup;
+ sr_err = sr_oper_get_items_subscribe(
+ sr_session, YM_COMMON, XPATH_BASE, sysrepo_conf_read_callback,
+ NULL, SR_SUBSCR_CTX_REUSE | SR_SUBSCR_NO_THREAD,
+ &sr_subscription
+ );
+ if (sr_err != SR_ERR_OK)
+ goto cleanup;
+
/* add subscriptions to kres event loop */
el_subscription_ctx =
el_subscription_new(sr_subscription, el_subscr_cb);
int sysrepo_deinit()
{
el_subscription_free(el_subscription_ctx);
+
// remove reference to Lua callback so that it can be free'd safely
apply_conf = NULL;
+ read_conf = NULL;
+
return kr_ok();
}
return ((struct lyd_node_leaf_list*) node)->value_str;
}
+
+KR_EXPORT struct lyd_node* node_new_leaf(struct lyd_node* parent, const struct lys_module* module, const char* name, const char* value) {
+ return lyd_new_leaf(parent, module, name, value);
+}
+KR_EXPORT struct lyd_node* node_new_container(struct lyd_node* parent, const struct lys_module* module, const char* name) {
+ return lyd_new(parent, module, name);
+}
+KR_EXPORT const struct lys_module* schema_get_module(const struct lys_node* schema) {
+ return schema->module;
+}
+
+KR_EXPORT const struct lys_node* schema_child_first(const struct lys_node* parent) {
+ assert(
+ parent->nodetype == LYS_CONTAINER ||
+ parent->nodetype == LYS_LIST ||
+ parent->nodetype == LYS_CHOICE
+ );
+
+ return parent->child;
+}
+
+KR_EXPORT const struct lys_node* schema_child_next(const struct lys_node* prev_child) {
+ return prev_child->next;
+}
+
+KR_EXPORT const char* schema_get_name(const struct lys_node* node) {
+ return node->name;
+}
+
+KR_EXPORT const struct lys_node* schema_root() {
+ assert(lys_root != NULL);
+ return lys_root;
+}
typedef void (*el_subsription_cb)(el_subscription_ctx_t *el_subscr, int status);
/** Callback to Lua for applying configuration */
typedef void (*apply_conf_f)(struct lyd_node *root);
-typedef void (*read_conf_f)(struct lyd_node* root);
+typedef struct lyd_node* (*read_conf_f)();
-KR_EXPORT int sysrepo_init(apply_conf_f set_leaf_conf_cb);
+KR_EXPORT int sysrepo_init(apply_conf_f apply_conf_callback, read_conf_f read_conf_callback);
KR_EXPORT int sysrepo_deinit(void);
/** Given a libyang node, returns it's first child */
KR_EXPORT const char* node_get_name(struct lyd_node* node);
/** Given a libyang node, return it's value as a string */
KR_EXPORT const char* node_get_value_str(struct lyd_node* node);
+/** Create a new libyang leaf node */
+KR_EXPORT struct lyd_node* node_new_leaf(struct lyd_node* parent, const struct lys_module* module, const char* name, const char* value);
+/** Create a new libyang container node */
+KR_EXPORT struct lyd_node* node_new_container(struct lyd_node* parent, const struct lys_module* module, const char* name);
+/** Returns module given a schema node */
+KR_EXPORT const struct lys_module* schema_get_module(const struct lys_node* schema);
+/** Given a libyang schema node, returns it's first child */
+KR_EXPORT const struct lys_node* schema_child_first(const struct lys_node* parent);
+/** Given a libyang schema node, return next sibling or NULL if there isn't any */
+KR_EXPORT const struct lys_node* schema_child_next(const struct lys_node* prev_child);
+/** Given a libyang schema node, return it's name */
+KR_EXPORT const char* schema_get_name(const struct lys_node* node);
+/** Get schema root */
+KR_EXPORT const struct lys_node* schema_root();
--- Definition of `sr_val_t` copied from sysrepo.h on 2020-01-01.
ffi.cdef[[
- 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);
-
+ typedef struct el_subscription_ctx el_subscription_ctx_t;
+ /** Callback for our sysrepo subscriptions */
+ typedef void (*el_subsription_cb)(el_subscription_ctx_t *el_subscr, int status);
+ /** Callback to Lua for applying configuration */
+ typedef void (*apply_conf_f)(struct lyd_node *root);
+ typedef struct lyd_node* (*read_conf_f)();
+
+ int sysrepo_init(apply_conf_f apply_conf_callback, read_conf_f read_conf_callback);
+ 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);
+ /** Create a new libyang leaf node */
+ struct lyd_node* node_new_leaf(struct lyd_node* parent, const struct lys_module* module, const char* name, const char* value);
+ /** Create a new libyang container node */
+ struct lyd_node* node_new_container(struct lyd_node* parent, const struct lys_module* module, const char* name);
+ /** Returns module given a schema node */
+ const struct lys_module* schema_get_module(const struct lys_node* schema);
+ /** Given a libyang schema node, returns it's first child */
+ const struct lys_node* schema_child_first(const struct lys_node* parent);
+ /** Given a libyang schema node, return next sibling or NULL if there isn't any */
+ const struct lys_node* schema_child_next(const struct lys_node* prev_child);
+ /** Given a libyang schema node, return it's name */
+ const char* schema_get_name(const struct lys_node* node);
+ /** Get schema root */
+ const struct lys_node* schema_root();
]]
end
local sysrepo_ffi = {}
-function sysrepo_ffi.init(apply_conf_func)
- local cb = create_callback("apply_conf_f", apply_conf_func)
- local res = clib.sysrepo_init(cb)
+function sysrepo_ffi.init(apply_conf_func, read_conf_func)
+ local apply = create_callback("apply_conf_f", apply_conf_func)
+ local read = create_callback("read_conf_f", read_conf_func)
+ local res = clib.sysrepo_init(apply, read)
if res ~= 0 then
error("Initialization failed with error code " .. tostring(res))
end
end
local function read_configuration()
- return data_model.serialize_model(nil)
+ print("Current configuration requested")
+
+ return data_model.serialize_configuration(nil)
end
function sysrepo.init()
- sysrepo_ffi.init(apply_configuration)
+ sysrepo_ffi.init(apply_configuration, read_configuration)
end
function sysrepo.deinit()
--- 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)
+--- @param apply_func Function which takes self and data node from libyang and applies the configuration to the system
+--- @param read_func Function which takes self and data node from libyang and adds a child to it with data from
+--- the system. Returns a node it added.
+function Node:create(name, apply_func, read_func, initialize_schema_func)
assert(type(name) == "string")
+ assert(type(apply_func) == 'function')
assert(type(read_func) == 'function')
- assert(type(write_func) == 'function')
+ assert(initialize_schema_func == nil or type(initialize_schema_func) == 'function')
local handler = {}
setmetatable(handler, Node)
- handler.read = read_func
- handler.write = write_func
+ handler.apply = apply_func
+ handler.serialize = read_func
handler.name = name
+ handler.module = nil -- must be filled in later by initialize_schema method
+
+ -- default implementation
+ local function schema_init(self, lys_node)
+ assert(ffi.string(clib().schema_get_name(lys_node)) == self.name)
+ self.module = clib().schema_get_module(lys_node)
+ end
+ if initialize_schema_func == nil then
+ initialize_schema_func = schema_init
+ end
+ handler.initialize_schema = initialize_schema_func
return handler
end
local function dummy_write(self, node)
debug.log("dummy write on node named {}", self.name)
+ return nil
end
- return Node:create(name, dummy_read, dummy_write)
+ return Node:create(name, dummy_read, dummy_write, nil)
end
local function ContainerNode(name, container_model)
child_lookup_table[v.name] = v
end
- --- Node's read function
+ --- Node's apply 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)
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_lookup_table[nm]:apply(child)
child = clib().node_child_next(child)
end
end
- --- Node's write function
+ --- Node's serialize function
local function handle_cont_write(self, parent_node)
- local node = nil -- TODO get current node from parent_node
+ local node = clib().node_new_container(parent_node, self.module, self.name)
+
+ for _,v in ipairs(container_model) do
+ _ = v:serialize(node)
+ end
+
+ return node
+ end
+
+ local function schema_init(self, lys_node)
+ assert(ffi.string(clib().schema_get_name(lys_node)) == self.name)
+ self.module = clib().schema_get_module(lys_node)
+
+ local lookup = {}
+ local child = clib().schema_child_first(lys_node)
+ while child ~= nil do
+ local nm = ffi.string(clib().schema_get_name(child))
+ lookup[nm] = child
+ child = clib().schema_child_next(child)
+ end
+ -- apply to all children
for _,v in ipairs(container_model) do
- v:write(node)
+ v:initialize_schema(lookup[v.name])
end
end
- return Node:create(name, handle_cont_read, handle_cont_write)
+ return Node:create(name, handle_cont_read, handle_cont_write, schema_init)
+end
+
+local function StateNode(name, get_val)
+ --- Node's apply function
+ local function handle_apply(self, node)
+ -- do nothing, it's only a state node
+ end
+
+ --- Node's serialize function
+ local function handle_serialize(self, parent_node)
+ return clib().node_new_leaf(parent_node, self.module, self.name, tostring(get_val()))
+ end
+
+ return Node:create(name, handle_apply, handle_serialize, nil)
end
local model =
ContainerNode("dns-resolver", {
ContainerNode("cache", {
- DummyLeafNode("current-size"),
+ StateNode("current-size", function() return cache.current_size end),
DummyLeafNode("max-size"),
DummyLeafNode("max-ttl"),
DummyLeafNode("min-ttl"),
DummyLeafNode("server", true),
})
-
--- Module constructor
return function(clib_binding)
_clib = clib_binding
+ local initialized_schema = false
+ local function init_schema()
+ if not initialized_schema then
+ model:initialize_schema(clib().schema_root())
+ initialized_schema = true
+ end
+ end
+
local module = {}
function module.serialize_configuration(root_node)
- model:write(root_node)
+ init_schema()
+ model:serialize(root_node)
end
function module.apply_configuration(root_node)
- model:read(root_node)
+ init_schema()
+ model:apply(root_node)
end
return module