ncclient / ncclient

Python library for NETCONF clients
Apache License 2.0
672 stars 393 forks source link

create_subscription with filter throwing error of Unknown element "filter". #558

Open vaibhav1713109 opened 1 year ago

vaibhav1713109 commented 1 year ago

Hi @einarnn

When i did create _subscription with filter ncclient throw errot of Unknown element "filter", but the same filter is working with get and get_config.


oc_namespace = {"usr": "urn:o-ran:user-mgmt:1.0"}
oc_select = "/usr:users"
gt = session.get(filter=("xpath", (oc_namespace,oc_select)))
gt
<nc:rpc-reply xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:d888baf1-5efc-4487-aa7c-37257ce691fd"><data xmlns="urn:ietf:params:xml:ns:netconf:base:1.0"><users xmlns="urn:o-ran:user-mgmt:1.0"><user><name>oranuser</name><account-type>PASSWORD</account-type><enabled>true</enabled></user><user><name>operator</name><account-type>PASSWORD</account-type><enabled>true</enabled></user><user><name>observer</name><account-type>PASSWORD</account-type><enabled>true</enabled></user><user><name>installer</name><account-type>PASSWORD</account-type><enabled>true</enabled></user></users></data></nc:rpc-reply>

sub = session.create_subscription(filter=("xpath", (oc_namespace,oc_select)))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/vvdn/.local/lib/python3.6/site-packages/ncclient/manager.py", line 251, in execute
    huge_tree=self._huge_tree).request(*args, **kwds)
  File "/home/vvdn/.local/lib/python3.6/site-packages/ncclient/operations/subscribe.py", line 64, in request
    return self._request(node)
  File "/home/vvdn/.local/lib/python3.6/site-packages/ncclient/operations/rpc.py", line 375, in _request
    raise self._reply.error
ncclient.operations.rpc.RPCError: Unknown element "filter".
sjd-xlnx commented 9 months ago

I also had the same issue. Was there any progress on this?

sjd-xlnx commented 9 months ago

For example, if I try to subscribe with xpath filter of /o-ran-sync:*

This is the debug from ncclient...

<?xml version="1.0" encoding="UTF-8"?>
<nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:5b4262f6-3ef5-47b7-b123-57c432cf0c10">
  <ns0:create-subscription xmlns:ns0="urn:ietf:params:xml:ns:netconf:notification:1.0">
    <nc:filter xmlns:o-ran-sync="urn:o-ran:sync:1.0" type="xpath" select="/o-ran-sync:*"/>
  </ns0:create-subscription>
</nc:rpc>

And the same subscribe request as sent by netopeer2-cli...

<rpc xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="46">
  <create-subscription xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
    <filter type="xpath" xmlns:o-ran-sync="urn:o-ran:sync:1.0" select="/o-ran-sync:*"/>
  </create-subscription>
</rpc>

Apart from slightly different order in the filter tag and the namespace prefixes, they look the same. Yet, the one generated by ncclient gets rejected by the server.

<rpc-reply xmlns="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:5b4262f6-3ef5-47b7-b123-57c432cf0c10">
  <rpc-error>
    <error-type>application</error-type>
    <error-tag>operation-failed</error-tag>
    <error-severity>error</error-severity>
    <error-message xml:lang="en">Node "filter" not found as a child of "create-subscription" node.</error-message>
  </rpc-error>
</rpc-reply>
sjd-xlnx commented 9 months ago

Actually, I think I see the problem now. The filter is using namespace "nc", shouldn't it be namespace "ns0" (i.e. "urn:ietf:params:xml:ns:netconf:notification:1.0")?

<nc:rpc xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" message-id="urn:uuid:5b4262f6-3ef5-47b7-b123-57c432cf0c10">
  <ns0:create-subscription xmlns:ns0="urn:ietf:params:xml:ns:netconf:notification:1.0">
    <nc:filter xmlns:o-ran-sync="urn:o-ran:sync:1.0" type="xpath" select="/o-ran-sync:*"/>
  </ns0:create-subscription>
</nc:rpc>

Actually, looking at the build_filter() function in ncclient/utils.py you can see that the filter can be expressed in a number of ways, including directly as xml (etree) Elements. So, that's what I've done in the below code snippet to get filter working for subscriptions...

from ncclient import manager
from ncclient.xml_ import to_ele, to_xml, new_ele, new_ele_ns, new_ele_nsmap
from lxml import etree

mgr = manager.connect(host="localhost", port=5678, username="stevend")

# This is the xpath I'm using for test subscription
xpath = "/o-ran-sync:*"

# This is the namespace of the filter that we are using (in netconf.notifications)
filter_ns = "urn:ietf:params:xml:ns:netconf:notification:1.0"

# Create the filter
filter = etree.Element(f"{{{filter_ns}}}filter",
                       {"type": "xpath", "select": xpath},
                       {"o-ran-sync": "urn:o-ran:sync:1.0"})

# Printing it out to check it looks ok
print(etree.tostring(filter))

# Here goes for the subscription
result = mgr.create_subscription(filter=filter)

# It should work :-)
print(result.ok)

The output is...

b'<ns0:filter xmlns:o-ran-sync="urn:o-ran:sync:1.0" xmlns:ns0="urn:ietf:params:xml:ns:netconf:notification:1.0" type="xpath" select="/o-ran-sync:*"/>'
True

There might be a more elegant way to do it (ncclient/xml_.py has a bunch of small functions for doing this kind of thing, but I couldn't see one that worked - it was as easy just to write the etree.Element code directly).

Note, the same code could be used to generate filters for get and get-config, just replace filter_ns with "urn:ietf:params:xml:ns:netconf:base:1.0".

Hope that makes sense, and helps anyone who's seeing this problem.

scott-carrion commented 3 months ago

Hi all,

I also ran into this issue and identified the root cause. As @sjd-xlnx correctly indicated, it seems that the XML namespace for the filter is incorrect.

The Bug

According to IETF RFC5277 Section 4 (XML Schema for Event Notifications), the filter element MUST have the namespace set to exactly the following, or a syntactical equivalent of it:

xmlns='urn:ietf:params:xml:ns:netconf:notification:1.0'

However, as indicated above, the filter element that is generated has the XML namespace as follows (or at least, a syntactical equivalent as detailed by other commenters above): xmlns='urn:ietf:params:xml:ns:netconf:base:1.0'

Reproduction Script

Here's how to reproduce. Basically, if you call this with a tuple, regardless of your chosen type (xpath or subtree), you'll run into this bug:

# This filter should show me alarm event notifications that relate to the resource "my-resource". Semantics of this depend on your YANG model and NETCONF server implementation.
my_filter = """<alarm-notification xmlns='urn:ietf:params:xml:ns:yang:ietf-alarms'><resource>my-resource</resource></alarm-notification>"""

with manager.connect(host=my_host_here, port=my_port_here, username=my_username_here, password=my_password_here, hostkey_verify=False) as m:
     print("Now creating subscription...")
     m.create_subscription(filter=('subtree', my_filter))
     # RPCError will be returned by NETCONF server you are connected to. Complaint will be: "Invalid element: 'filter'" or similar.

     print("Now awaiting notification...")
     notif = m.take_notification()
     print("Notification received! Dumping below:\n" + notif.notification_xml)

The Workaround

When create_subscription is called, it can be called with a tuple like (filter_type, filter_criteria) if the user doesn't want to define the filter element themselves.

Thankfully, create_subscription can also be called with a raw XML, in which the user defines the filter element themselves. So, if you need to create a subscription with a filter, just have to call it with the filter element of the request XML passed in directly.

Example:

# This filter will show me alarm event notifications that relate to the resource "my-resource". Semantics of this depend on your YANG model and NETCONF server implementation.
my_filter = """<filter xmlns='urn:ietf:params:xml:ns:netconf:notification:1.0' type="subtree"><alarm-notification xmlns='urn:ietf:params:xml:ns:yang:ietf-alarms'><resource>my-resource</resource></alarm-notification></filter>"""

with manager.connect(host=my_host_here, port=my_port_here, username=my_username_here, password=my_password_here, hostkey_verify=False) as m:
     print("Now creating subscription...")
     m.create_subscription(filter=my_filter)

     print("Now awaiting notification...")
     notif = m.take_notification()
     print("Notification received! Dumping below:\n" + notif.notification_xml)
scott-carrion commented 3 months ago

Contributors please fix!!!! If no one does I might just have become a contributor myself and do it