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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions