diff --git a/lib/northbound_oper.c b/lib/northbound_oper.c index 4a7a0bb559b7..ab9abdeae2ee 100644 --- a/lib/northbound_oper.c +++ b/lib/northbound_oper.c @@ -499,7 +499,11 @@ static enum nb_error nb_op_ys_init_node_infos(struct nb_op_yield_state *ys) ret = NB_ERR_NOT_FOUND; return ret; } - assert(CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)); + while (node && + !CHECK_FLAG(node->schema->nodetype, LYS_CONTAINER | LYS_LIST)) + node = &node->parent->node; + if (!node) + return NB_ERR_NOT_FOUND; inner = (struct lyd_node_inner *)node; for (len = 1; inner->parent; len++) @@ -1237,9 +1241,18 @@ static enum nb_error __walk(struct nb_op_yield_state *ys, bool is_resume) * `route-entry` element for a query * `.../route-entry/metric` where the list element had * no metric value. + * + * However, if the user query is for a key of a list + * element, then when we reach that list element it will + * have no non-key children, check for this condition + * and do not reap if true. */ if (!list_start && ni->inner && !lyd_child_no_keys(&ni->inner->node) && + /* not the top element with a key match */ + !((darr_ilen(ys->node_infos) == + darr_ilen(ys->schema_path) - 1) && + lysc_is_key((*darr_last(ys->schema_path)))) && /* is this at or below the base? */ darr_ilen(ys->node_infos) <= ys->query_base_level) lyd_free_tree(&ni->inner->node); @@ -1674,7 +1687,6 @@ static enum nb_error nb_op_walk_start(struct nb_op_yield_state *ys) * Get the node_info path (stack) corresponding to the uniquely * resolvable data nodes from the beginning of the xpath query. */ - // I think this moves ret = nb_op_ys_init_node_infos(ys); if (ret != NB_OK) return ret; diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json new file mode 100644 index 000000000000..3988204bb83b --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-name.json @@ -0,0 +1,9 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json new file mode 100644 index 000000000000..3a6eeb853d6b --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-eth0-vrf.json @@ -0,0 +1,10 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "r1-eth0", + "vrf": "default" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/simple-results/result-intf-name.json b/tests/topotests/mgmt_oper/simple-results/result-intf-name.json new file mode 100644 index 000000000000..9d8ea759b9c2 --- /dev/null +++ b/tests/topotests/mgmt_oper/simple-results/result-intf-name.json @@ -0,0 +1,21 @@ +{ + "frr-interface:lib": { + "interface": [ + { + "name": "lo" + }, + { + "name": "r1-eth0" + }, + { + "name": "lo-red" + }, + { + "name": "r1-eth1" + }, + { + "name": "red" + } + ] + } +} diff --git a/tests/topotests/mgmt_oper/test_simple.py b/tests/topotests/mgmt_oper/test_simple.py index f3d64e156a3d..1f9f21b8defb 100644 --- a/tests/topotests/mgmt_oper/test_simple.py +++ b/tests/topotests/mgmt_oper/test_simple.py @@ -46,6 +46,23 @@ def test_oper_simple(tgen): pytest.skip(tgen.errors) query_results = [ + ( + # Non-key query with key specific selection + '/frr-interface:lib/interface[name="r1-eth0"]/vrf', + "simple-results/result-intf-eth0-vrf.json", + ), + # Test machines will have different sets of interfaces so the test results will + # vary and need to be generated dynamically before this test is re-enabled + # ( + # # Key query on generic list + # "/frr-interface:lib/interface/name", + # "simple-results/result-intf-name.json", + # ), + ( + # Key query with key specific selection + '/frr-interface:lib/interface[name="r1-eth0"]/name', + "simple-results/result-intf-eth0-name.json", + ), ("/frr-vrf:lib", "simple-results/result-lib.json"), ("/frr-vrf:lib/vrf", "simple-results/result-lib-vrf-nokey.json"), (