Skip to content

Unable to provide operational data in mount point #88

@a1egr

Description

@a1egr

I am experiencing an issue with providing operational data for a mounted module (using ietf-yang-schema-mount).
I constructed a minimal example below to demonstrate the problem:

My goal is to write a plugin for module minimal with the following definition:

module minimal {
    yang-version 1.1;
    namespace "urn:minimal";
    prefix "min";

    import ietf-yang-schema-mount {
        prefix yangmnt;
    }
    
    container wrap {
        container root {
            yangmnt:mount-point "root";
        }

        leaf test {
            type string;
            config false;
        }
    }
}

As you can see, the module contains a mount point within a nested container (wrap/root).
Using the program below, I am able to write and read back configuration data to an arbitrary module mounted at label root.
I can also see the changes made to modules in my callback function module_change_cb (tested with ietf-system).

However, if my callback (oper_data_cb) for the operational data of module minimal is called and I return operational data for let's say ietf-interfaces within the mount point, then its is skipped as an "unknown element" and I cannot see it in the response that I receive.

Please find below the program I used to produce the error as well as exemplary calls for netopeer2-cli and console logs.

import logging
import sys
import signal
import sysrepo

MINIMAL_NAME = "minimal"
SCHEMA_MOUNT_NAME = "ietf-yang-schema-mount"


def oper_data_schema_mount_cb(xpath, private_data):
    print(f"{SCHEMA_MOUNT_NAME}: oper_data_cb called for", xpath)
    return {
        "schema-mounts": {
            "mount-point": [
                {
                    # enable mount point "root" in module "minimal"
                    "module": "minimal",
                    "label": "root",
                    "shared-schema": {},
                },
            ],
        }
    }


def module_change_cb(event, req_id, changes, private_data):
    print(f"{MINIMAL_NAME}: module_change_cb called with event '{event}'")

    if event in ("update", "change"):
        print(changes)


def oper_data_cb(xpath, private_data):
    print(f"\n{MINIMAL_NAME}: oper_data_cb called for", xpath)
    return {
        "minimal:wrap": {
            "root": {
                # should not be ignored
                "ietf-interfaces:interfaces-state": {
                    "interface": [
                        {
                            "name": "lo",
                            "type": "iana-if-type:softwareLoopback",
                        }
                    ]
                },
            },
            "foo": "bar",           # should be ignored
            "test": "test-string",  # should not be ignored
        }
    }


def init_plugin(conn):
    session = conn.start_session()
    session.subscribe_oper_data_request(SCHEMA_MOUNT_NAME, f"/{SCHEMA_MOUNT_NAME}:schema-mounts", oper_data_schema_mount_cb)

    # extra session for module with mount point
    session_mp = conn.start_session()
    session_mp.subscribe_module_change(MINIMAL_NAME, None, module_change_cb)
    session_mp.subscribe_oper_data_request(MINIMAL_NAME, f"/{MINIMAL_NAME}:wrap", oper_data_cb)

    return [session, session_mp]


if __name__ == "__main__":
    logging.basicConfig(stream=sys.stdout, level=logging.DEBUG)

    conn = sysrepo.SysrepoConnection()
    sessions = init_plugin(conn)

    def signal_handler(signum, frame):
        for session in sessions:
            session.stop()
        conn.disconnect()
        print("sysrepo connection closed")
        sys.exit(0)

    signal.signal(signal.SIGINT, signal_handler)
    signal.signal(signal.SIGTERM, signal_handler)

    print("setup done")

    signal.pause()

Get-requests to retrieve operational data using netopeer2-cli:

> get --filter-xpath /minimal:wrap/root/ietf-interfaces:interfaces-state
DATA
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"/>

> get --filter-xpath /minimal:wrap
DATA
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
  <wrap xmlns="urn:minimal">
    <test>test-string</test>
  </wrap>
</data>

> get --filter-xpath /minimal:wrap/root
DATA
<data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0">
</data>

Program output for the three requests above:

minimal: oper_data_cb called for /minimal:wrap/root/ietf-interfaces:interfaces-state
WARNING:libyang.data:/minimal:wrap/root: skipping unknown element 'ietf-interfaces:interfaces-state'
WARNING:libyang.data:/minimal:wrap: skipping unknown element 'foo'

minimal: oper_data_cb called for /minimal:wrap
WARNING:libyang.data:/minimal:wrap/root: skipping unknown element 'ietf-interfaces:interfaces-state'
WARNING:libyang.data:/minimal:wrap: skipping unknown element 'foo'

minimal: oper_data_cb called for /minimal:wrap/root
WARNING:libyang.data:/minimal:wrap/root: skipping unknown element 'ietf-interfaces:interfaces-state'
WARNING:libyang.data:/minimal:wrap: skipping unknown element 'foo'

I tried to follow my data through the sysrepo-python code to see where my mount point operational information is getting lost and it seems that the function Module.parse_data_dict of libyang-python ignores the contents of the mount point.
It is called here in the operational data callback handler of syrepo-python.

I am unsure whether this is an unwanted behavior of libyang-python, whether sysrepo-python should use a different function there or whether I have to set up something else before.

Regards

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions