diff --git a/modules/ietf-notification-capabilities@2022-02-17.yang b/modules/ietf-notification-capabilities@2022-02-17.yang new file mode 100644 index 00000000..c9385b25 --- /dev/null +++ b/modules/ietf-notification-capabilities@2022-02-17.yang @@ -0,0 +1,262 @@ +module ietf-notification-capabilities { + yang-version 1.1; + namespace + "urn:ietf:params:xml:ns:yang:ietf-notification-capabilities"; + prefix notc; + + import ietf-yang-push { + prefix yp; + description + "This module requires ietf-yang-push to be implemented."; + reference + "RFC 8641: Subscription to YANG Notifications for + Datastore Updates"; + } + import ietf-system-capabilities { + prefix sysc; + description + "This module requires ietf-system-capabilities to be + implemented."; + reference + "RFC 9196: YANG Modules Describing Capabilities for Systems + and Datastore Update Notifications"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: + WG List: + + Editor: Balazs Lengyel + "; + description + "This module specifies publisher capabilities related to + YANG-Push (RFC 8641). + + The module contains: + + - a specification of the data nodes that support 'on-change' or + 'periodic' notifications. + + - capabilities related to the throughput of notification data + that the publisher can support. (Note that for a specific + subscription, the publisher MAY allow only longer periods + or smaller updates depending on, e.g., actual load conditions.) + + Capability values can be specified at the system/publisher + level, at the datastore level, or for specific data nodes of + a specific datastore (and their contained subtrees), as defined + in the ietf-system-capabilities module. + + If different data nodes covered by a single subscription + have different values for a specific capability, then using + values that are only acceptable for some of these data nodes, + but not for others, may result in the rejection of the + subscription. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Revised BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 9196 + (https://www.rfc-editor.org/info/rfc9196); see the RFC itself + for full legal notices."; + + revision 2022-02-17 { + description + "Initial version"; + reference + "RFC 9196: YANG Modules Describing Capabilities for Systems + and Datastore Update Notifications"; + } + + grouping subscription-capabilities { + description + "Capabilities related to YANG-Push subscriptions + and notifications"; + container subscription-capabilities { + description + "Capabilities related to YANG-Push subscriptions + and notifications"; + typedef notification-support { + type bits { + bit config-changes { + description + "The publisher is capable of sending + notifications for 'config true' nodes for the + relevant scope and subscription type."; + } + bit state-changes { + description + "The publisher is capable of sending + notifications for 'config false' nodes for the + relevant scope and subscription type."; + } + } + description + "Type for defining whether 'on-change' or + 'periodic' notifications are supported for all data nodes, + 'config false' data nodes, 'config true' data nodes, or + no data nodes. + + The bits config-changes or state-changes have no effect + when they are set for a datastore or for a set of nodes + that does not contain nodes with the indicated config + value. In those cases, the effect is the same as if no + support was declared. One example of this is indicating + support for state-changes for a candidate datastore that + has no effect."; + } + + leaf max-nodes-per-update { + type uint32 { + range "1..max"; + } + description + "Maximum number of data nodes that can be sent + in an update. The publisher MAY support more data nodes + but SHOULD support at least this number. + + May be used to avoid the 'update-too-big' error + during subscription."; + reference + "RFC 8641: Subscription to YANG Notifications for + Datastore Updates, the 'update-too-big' error/identity"; + } + leaf periodic-notifications-supported { + type notification-support; + description + "Specifies whether the publisher is capable of + sending 'periodic' notifications for the selected + data nodes, including any subtrees that may exist + below them."; + reference + "RFC 8641: Subscription to YANG Notifications for + Datastore Updates, 'periodic' subscription concept"; + } + choice update-period { + description + "Supported update period value or values for + 'periodic' subscriptions."; + leaf minimum-update-period { + type uint32; + units "centiseconds"; + description + "Indicates the minimal update period that is + supported for a 'periodic' subscription. + + A subscription request to the selected data nodes with + a smaller period than what this leaf specifies is + likely to result in a 'period-unsupported' error."; + reference + "RFC 8641: Subscription to YANG Notifications for + Datastore Updates, the period leaf in the ietf-yang-push + YANG module"; + } + leaf-list supported-update-period { + type uint32; + units "centiseconds"; + description + "Supported update period values for a 'periodic' + subscription. + + A subscription request to the selected data nodes with a + period not included in the leaf-list will result in a + 'period-unsupported' error."; + reference + "RFC 8641: Subscription to YANG Notifications for + Datastore Updates, the period leaf in the ietf-yang-push + YANG module"; + } + } + leaf on-change-supported { + if-feature "yp:on-change"; + type notification-support; + description + "Specifies whether the publisher is capable of + sending 'on-change' notifications for the selected + data nodes and the subtree below them."; + reference + "RFC 8641: Subscription to YANG Notifications for Datastore + Updates, on-change concept"; + } + leaf minimum-dampening-period { + if-feature "yp:on-change"; + type uint32; + units "centiseconds"; + description + "The minimum dampening period supported for 'on-change' + subscriptions for the selected data nodes. + + If this value is present and greater than zero, + that implies dampening is mandatory."; + reference + "RFC 8641: Subscription to YANG Notifications for + Datastore Updates, the dampening-period leaf in the + ietf-yang-push YANG module"; + } + leaf-list supported-excluded-change-type { + if-feature "yp:on-change"; + type union { + type enumeration { + enum none { + value -2; + description + "None of the change types can be excluded."; + } + enum all { + value -1; + description + "Any combination of change types can be excluded."; + } + } + type yp:change-type; + } + description + "The change types that can be excluded in + YANG-Push subscriptions for the selected data nodes."; + reference + "RFC 8641: Subscription to YANG Notifications for Datastore + Updates, the change-type typedef in the ietf-yang-push + YANG module"; + } + } + } + + augment "/sysc:system-capabilities" { + description + "Add system level capabilities"; + uses subscription-capabilities { + refine + "subscription-capabilities/supported-excluded-change-type" { + default "none"; + } + } + } + + augment "/sysc:system-capabilities/sysc:datastore-capabilities" + + "/sysc:per-node-capabilities" { + description + "Add datastore and node-level capabilities"; + uses subscription-capabilities { + refine + "subscription-capabilities/supported-excluded-change-type" { + default "none"; + } + } + } +} diff --git a/modules/ietf-system-capabilities@2022-02-17.yang b/modules/ietf-system-capabilities@2022-02-17.yang new file mode 100644 index 00000000..55f959d8 --- /dev/null +++ b/modules/ietf-system-capabilities@2022-02-17.yang @@ -0,0 +1,170 @@ +module ietf-system-capabilities { + yang-version 1.1; + namespace "urn:ietf:params:xml:ns:yang:ietf-system-capabilities"; + prefix sysc; + + import ietf-netconf-acm { + prefix nacm; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + import ietf-yang-library { + prefix yanglib; + description + "This module requires ietf-yang-library to be implemented. + Revision 2019-01-04 or a revision derived from it + is REQUIRED."; + reference + "RFC8525: YANG Library"; + } + + organization + "IETF NETCONF (Network Configuration) Working Group"; + contact + "WG Web: + WG List: + + Editor: Balazs Lengyel + "; + description + "This module specifies a structure to specify system + capabilities for a server or a publisher. System capabilities + may include capabilities of a NETCONF or RESTCONF server or a + notification publisher. + + This module does not contain any specific capabilities; it only + provides a structure where containers containing the actual + capabilities are augmented in. + + Capability values can be specified at the system level, at the + datastore level (by selecting all nodes in the datastore), or + for specific data nodes of a specific datastore (and their + contained subtrees). + Capability values specified for a specific datastore or + node-set override values specified on the system/publisher + level. + + The same grouping MUST be used to define hierarchical + capabilities supported both at the system level and at the + datastore/data-node level. + + To find a capability value for a specific data node in a + specific datastore, the user SHALL: + + 1) search for a datastore-capabilities list entry for + the specific datastore. When stating a specific capability, the + relative path for any specific capability must be the same + under the system-capabilities container and under the + per-node-capabilities list. + + 2) If the datastore entry is found within that entry, process + all per-node-capabilities entries in the order they appear in + the list. The first entry that specifies the specific + capability and has a node-selector selecting the specific data + node defines the capability value. + + 3) If the capability value is not found above and the specific + capability is specified under the system-capabilities container + (outside the datastore-capabilities list), this value shall be + used. + + 4) If no values are found in the previous steps, the + system/publisher is not capable of providing a value. Possible + reasons are that it is unknown, the capability is changing for + some reason, there is no specified limit, etc. In this case, + the system's behavior is unspecified. + + The key words 'MUST', 'MUST NOT', 'REQUIRED', 'SHALL', + 'SHALL NOT', 'SHOULD', 'SHOULD NOT', 'RECOMMENDED', + 'NOT RECOMMENDED', 'MAY', and 'OPTIONAL' in this document + are to be interpreted as described in BCP 14 (RFC 2119) + (RFC 8174) when, and only when, they appear in all + capitals, as shown here. + + Copyright (c) 2022 IETF Trust and the persons identified as + authors of the code. All rights reserved. + + Redistribution and use in source and binary forms, with or + without modification, is permitted pursuant to, and subject to + the license terms contained in, the Revised BSD License set + forth in Section 4.c of the IETF Trust's Legal Provisions + Relating to IETF Documents + (https://trustee.ietf.org/license-info). + + This version of this YANG module is part of RFC 9196 + (https://www.rfc-editor.org/info/rfc9196); see the RFC itself + for full legal notices."; + + revision 2022-02-17 { + description + "Initial version"; + reference + "RFC 9196: YANG Modules Describing Capabilities for Systems + and Datastore Update Notifications"; + } + + container system-capabilities { + config false; + description + "System capabilities. + Capability values specified here at the system level + are valid for all datastores and are used when the + capability is not specified at the datastore level + or for specific data nodes."; + /* + * "Augmentation point for system-level capabilities." + */ + list datastore-capabilities { + key "datastore"; + description + "Capabilities values per datastore. + + For non-NMDA servers/publishers, 'config false' data is + considered as if it were part of the running datastore."; + leaf datastore { + type leafref { + path + "/yanglib:yang-library/yanglib:datastore/yanglib:name"; + } + description + "The datastore for which capabilities are defined. + Only one specific datastore can be specified, + e.g., ds:conventional must not be used, as it + represents a set of configuration datastores."; + } + list per-node-capabilities { + description + "Each list entry specifies capabilities for the selected + data nodes. The same capabilities apply to the data nodes + in the subtree below the selected nodes. + + The system SHALL order the entries according to their + precedence. The order of the entries MUST NOT change + unless the underlying capabilities also change. + + Note that the longest patch matching can be achieved + by ordering more specific matches before less + specific ones."; + choice node-selection { + description + "A method to select some or all nodes within a + datastore."; + leaf node-selector { + type nacm:node-instance-identifier; + description + "Selects the data nodes for which capabilities are + specified. The special value '/' denotes all data + nodes in the datastore, consistent with the path + leaf node on page 41 of [RFC8341]."; + reference + "RFC 8341: Network Configuration Access Control Model"; + } + } + /* + * "Augmentation point for datastore- or data-node-level + * capabilities." + */ + } + } + } +} diff --git a/scripts/common.sh b/scripts/common.sh index 7a5678e8..e56a15d3 100644 --- a/scripts/common.sh +++ b/scripts/common.sh @@ -13,6 +13,8 @@ NP2_MODULES=( "ietf-subscribed-notifications@2019-09-09.yang -e encode-xml -e replay -e subtree -e xpath" "ietf-yang-push@2019-09-09.yang -e on-change" "netopeer-notifications@2025-01-15.yang" +"ietf-system-capabilities@2022-02-17.yang" +"ietf-notification-capabilities@2022-02-17.yang" ) LN2_MODULES=( diff --git a/src/common.c b/src/common.c index 08938588..86e33ce7 100644 --- a/src/common.c +++ b/src/common.c @@ -1824,3 +1824,22 @@ np_reply_err_in_use(const struct ly_ctx *ly_ctx, const char *msg, uint32_t sr_id } return nc_server_reply_err(e); } + +const char * +sub_ntf_ds2ident(sr_datastore_t ds) +{ + switch (ds) { + case SR_DS_STARTUP: + return "ietf-datastores:startup"; + case SR_DS_RUNNING: + return "ietf-datastores:running"; + case SR_DS_CANDIDATE: + return "ietf-datastores:candidate"; + case SR_DS_OPERATIONAL: + return "ietf-datastores:operational"; + case SR_DS_FACTORY_DEFAULT: + return "ietf-factory-default:factory-default"; + } + + return NULL; +} diff --git a/src/common.h b/src/common.h index 7b70c40d..b730845b 100644 --- a/src/common.h +++ b/src/common.h @@ -440,4 +440,13 @@ struct nc_server_reply *np_reply_err_bad_elem(const struct ly_ctx *ly_ctx, const */ struct nc_server_reply *np_reply_err_in_use(const struct ly_ctx *ly_ctx, const char *msg, uint32_t sr_id); +/** + * @brief Transform a datastore into a string identity. + * + * @param[in] str Identity. + * @param[out] ds Datastore. + * @return Sysrepo error value. + */ +const char *sub_ntf_ds2ident(sr_datastore_t ds); + #endif /* NP2SRV_COMMON_H_ */ diff --git a/src/main.c b/src/main.c index 952b460b..fe03c2b0 100644 --- a/src/main.c +++ b/src/main.c @@ -57,6 +57,21 @@ /** @brief flag for main loop */ ATOMIC_T loop_continue; +struct notification_capabilities { + const char *name; + const char *value; +}; + +/* Defines https://datatracker.ietf.org/doc/html/rfc9196#section-5 */ +static const struct notification_capabilities notif_capas[] = { + {"max-nodes-per-update", "4294967295"}, + {"periodic-notifications-supported", "config-changes state-changes"}, + {"minimum-update-period", "0"}, + {"on-change-supported", "config-changes state-changes"}, + {"minimum-dampening-period", "0"}, + {"supported-excluded-change-type", "all"} +}; + static void *worker_thread(void *arg); /** @@ -452,6 +467,124 @@ np2srv_sm_oper_cb(sr_session_ctx_t *session, uint32_t UNUSED(sub_id), const char return rc; } +/** + * @brief Add subscription capabilities to a node + * @param[in,out] node is a part of ietf-system-capabilities. + * @param[in] ly_ctx a libyang context. + */ +static int +np2srv_add_subscription_capabilities(struct lyd_node *node, const struct ly_ctx *ly_ctx) +{ + const struct lys_module *notc_mod; + struct lyd_node *subs_capas; + int rc = SR_ERR_OK; + uint32_t c; + + notc_mod = ly_ctx_get_module_implemented(ly_ctx, "ietf-notification-capabilities"); + if (!notc_mod) { + ERR("Module \"ietf-notification-capabilities\" not implemented in sysrepo."); + rc = -1; + goto cleanup; + } + + /* subscription-capabilities */ + if (lyd_new_path(node, ly_ctx, "ietf-notification-capabilities:subscription-capabilities", + NULL, 0, &subs_capas)) { + ERR("Failed to create subscription-capabilities."); + rc = -1; + goto cleanup; + } + + /* notification capabilities */ + for (c = 0; c < sizeof(notif_capas) / sizeof(*notif_capas); c++) { + if (lyd_new_term(subs_capas, notc_mod, notif_capas[c].name, notif_capas[c].value, 0, + NULL)) { + ERR("Failed to create %s.", notif_capas[c].name); + rc = -1; + goto cleanup; + } + } +cleanup: + return rc; +} + +/** + * @brief SR operational get callback for system-capabilities data. + */ +static int +np2srv_capabilities_oper_cb(sr_session_ctx_t *session, uint32_t sub_id, + const char *module_name, const char *path, const char *request_xpath, + uint32_t request_id, struct lyd_node **parent, void *private_data) +{ + struct lyd_node *sys_capas, *datastore_capas, *per_node_capas; + const struct ly_ctx *ly_ctx; + int rc = SR_ERR_OK; + uint32_t ds; + + (void)sub_id; + (void)module_name; + (void)path; + (void)request_xpath; + (void)request_id; + (void)private_data; + + /* context is locked while the callback is executing */ + ly_ctx = sr_session_acquire_context(session); + if (!ly_ctx) { + ERR("Failed to acquire sysrepo context."); + rc = -1; + goto cleanup; + } + + if (lyd_new_path(NULL, ly_ctx, "/ietf-system-capabilities:system-capabilities", + NULL, 0, &sys_capas)) { + ERR("Failed to create system-capabilities."); + rc = -1; + goto cleanup; + } + + /* datastore-capabilities */ + for (ds = 0; ds < SR_DS_COUNT; ds++) { + if (lyd_new_list(sys_capas, NULL, "datastore-capabilities", + 0, &datastore_capas, sub_ntf_ds2ident(ds))) { + ERR("Failed to create datastore-capabilities."); + rc = -1; + goto cleanup; + } + + if (lyd_new_list(datastore_capas, NULL, "per-node-capabilities", + 0, &per_node_capas, '/')) { + ERR("Failed to create per-node-capabilities."); + rc = -1; + goto cleanup; + } + + /* per datastore capabilities */ + if (np2srv_add_subscription_capabilities(per_node_capas, ly_ctx)) { + ERR("Failed to add per node subscription-capabilities."); + rc = -1; + goto cleanup; + } + } + + /* global capabilities */ + if (np2srv_add_subscription_capabilities(sys_capas, ly_ctx)) { + ERR("Failed to add global subscription-capabilities."); + rc = -1; + goto cleanup; + } + +cleanup: + if (rc) { + lyd_free_tree(sys_capas); + } else { + *parent = sys_capas; + } + + sr_session_release_context(session); + return rc; +} + #ifdef NC_ENABLED_SSH_TLS /** @@ -871,6 +1004,15 @@ server_data_subscribe(void) mod_name = "nc-notifications"; SR_OPER_SUBSCR(mod_name, "/nc-notifications:netconf", np2srv_nc_ntf_oper_cb); + mod_name = "ietf-system-capabilities"; + rc = sr_oper_get_subscribe(np2srv.sr_sess, mod_name, + "/ietf-system-capabilities:system-capabilities", np2srv_capabilities_oper_cb, NULL, + SR_SUBSCR_OPER_MERGE, &np2srv.sr_data_sub); + if (rc != SR_ERR_OK) { + ERR("Subscribing for providing \"%s\" state data failed (%s).", mod_name, sr_strerror(rc)); + goto error; + } + #ifdef NC_ENABLED_SSH_TLS /* set callbacks for supported algorithms oper data */ mod_name = "iana-ssh-public-key-algs"; diff --git a/src/netconf_subscribed_notifications.c b/src/netconf_subscribed_notifications.c index 195f69aa..3b0a8814 100644 --- a/src/netconf_subscribed_notifications.c +++ b/src/netconf_subscribed_notifications.c @@ -811,32 +811,6 @@ sub_ntf_append_params_filter(struct lyd_node *parent, const struct np2srv_sub_nt return rc; } -/** - * @brief Transform a datastore into a string identity. - * - * @param[in] str Identity. - * @param[out] ds Datastore. - * @return Sysrepo error value. - */ -static const char * -sub_ntf_ds2ident(sr_datastore_t ds) -{ - switch (ds) { - case SR_DS_STARTUP: - return "ietf-datastores:startup"; - case SR_DS_RUNNING: - return "ietf-datastores:running"; - case SR_DS_CANDIDATE: - return "ietf-datastores:candidate"; - case SR_DS_OPERATIONAL: - return "ietf-datastores:operational"; - case SR_DS_FACTORY_DEFAULT: - return "ietf-factory-default:factory-default"; - } - - return NULL; -} - /** * @brief Transform yang-push operation into string. *