]> git.ipfire.org Git - thirdparty/knot-resolver.git/commitdiff
sysrepo-lua: initial work on reporting to operational datastore
authorVasek Sraier <git@vakabus.cz>
Thu, 5 Mar 2020 15:04:36 +0000 (16:04 +0100)
committerVasek Sraier <git@vakabus.cz>
Thu, 5 Mar 2020 15:04:36 +0000 (16:04 +0100)
modules/sysrepo-lua/cbindings/sysrepo_clib.c
modules/sysrepo-lua/cbindings/sysrepo_clib.h
modules/sysrepo-lua/ffi.lua.in
modules/sysrepo-lua/init.lua
modules/sysrepo-lua/model.lua

index 86b49a46495d171cd39056977633aa46f82bbc56..2adf8dd130de1db332e9f560118047bcc7f8eea7 100644 (file)
@@ -41,6 +41,7 @@ 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;
+static const struct lys_node* lys_root = NULL;
 
 /**
  * Change callback getting called by sysrepo. Iterates over changed options and passes
@@ -85,6 +86,18 @@ static int sysrepo_conf_change_callback(sr_session_ctx_t *session,
        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;
@@ -145,10 +158,11 @@ static void el_subscr_cb(el_subscription_ctx_t *el_subscr, int status)
        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;
@@ -167,7 +181,12 @@ int sysrepo_init(apply_conf_f apply_conf_callback)
        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
         */
@@ -178,6 +197,14 @@ int sysrepo_init(apply_conf_f apply_conf_callback)
        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);
@@ -195,8 +222,11 @@ cleanup:
 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();
 }
 
@@ -226,3 +256,36 @@ KR_EXPORT const char* node_get_value_str(struct lyd_node* node) {
 
        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;
+}
index 1b2bf00c0f754e54633732c8fc4e2d1625fa7ac9..634481ed5ee01a4beda531ab838fcec0c11c63cc 100644 (file)
@@ -45,9 +45,9 @@ typedef struct el_subscription_ctx el_subscription_ctx_t;
 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 */
@@ -58,3 +58,17 @@ KR_EXPORT struct lyd_node* node_child_next(struct lyd_node* prev_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();
index 7bdf2d5b6e377eccce17549ac9482d97a8131f91..146444b6f31368842cb1aff81f4cf8ec2d7810a4 100644 (file)
@@ -11,20 +11,38 @@ local function initialize_ffi()
     --- 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
 
@@ -66,9 +84,10 @@ 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
index 44270a692646f763de01612e30b617721bee1edb..bb29ef3b58cb3a06223620c282df699db76ab5d1 100644 (file)
@@ -11,11 +11,13 @@ local function apply_configuration(root_node)
 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()
index 11d6688b509cd8b2f25cc8b5dee6d7f10d7ce751..479218931d1bafe7ae976d8e6abc13d11bf1707d 100644 (file)
@@ -15,19 +15,32 @@ end
 --- 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
@@ -52,9 +65,10 @@ local function DummyLeafNode(name, ignore_value)
 
     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)
@@ -64,7 +78,7 @@ 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)
@@ -73,21 +87,55 @@ local function ContainerNode(name, container_model)
         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
 
 
@@ -95,7 +143,7 @@ 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"),
@@ -111,18 +159,27 @@ local model =
         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