RFC 8639

Subscription to YANG Notifications

๐Ÿ“… published September 2019, from ietf

RFC 8639: Subscription to YANG Notifications ๐ŸŽ€

Okay bestie, this one is a real main character spec ๐Ÿ’… It's Request for Comments 8639, published by the IETF in September 2019, and its ISSN is 2070-1721. The title is "Subscription to YANG Notifications" โœจ

The authors are E. Voit (Cisco Systems), A. Clemm (Futurewei), A. Gonzalez Prieto (Microsoft), E. Nilsen-Nygaard (Cisco Systems), and A. Tripathy (Cisco Systems) ๐Ÿซถ It's on the Standards Track, so it's an Internet Standards Track document. Its header has no Obsoletes or Updates lines, so it doesn't list anything it replaces or amends ๐Ÿฆ‹

Part 1 of 14 is below, and more is coming, so stay tuned ๐ŸŒธ


Abstract โœจ

Okay so this doc defines a YANG data model plus the mechanisms that let a subscriber ask for its own subscriptions to a publisher's event streams. Once that's in place, the subscriber can request and receive a continuous feed of publisher-generated info that's customized just for them ๐Ÿ’–

Status of This Memo ๐ŸŒˆ

This is an Internet Standards Track document ๐ŸŽ€

It's a product of the Internet Engineering Task Force (IETF), and it represents the consensus of the IETF community. It got public review and was approved for publication by the Internet Engineering Steering Group (IESG). For more on Internet Standards, check Section 2 of RFC 7841 ๐Ÿงš

For the current status, any errata, and how to send feedback, go to https://www.rfc-editor.org/info/rfc8639 ๐Ÿ“

Copyright (c) 2019 IETF Trust and the persons identified as the document authors. All rights reserved.

This document is subject to BCP 78 and the IETF Trust's Legal Provisions Relating to IETF Documents (https://trustee.ietf.org/license-info) as they stand on the publication date. Please read these carefully, because they spell out your rights and restrictions for this document. Code Components pulled out of this document must include Simplified BSD License text as described in Section 4.e of the Trust Legal Provisions, and they come without warranty, as described in the Simplified BSD License ๐Ÿ™ˆ

This document may contain material from IETF Documents or IETF Contributions that were published or made publicly available before November 10, 2008. The people who control the copyright in some of that material may not have given the IETF Trust permission to allow modifications outside the IETF Standards Process. Unless you get an adequate license from those copyright controllers, this document may not be modified outside the IETF Standards Process, and derivative works of it may not be made outside the IETF Standards Process. The exceptions are formatting it for publication as an RFC or translating it into languages other than English ๐Ÿ’…

Table of Contents ๐Ÿ—บ๏ธ

   1. Introduction ....................................................3
      1.1. Motivation .................................................4
      1.2. Terminology ................................................4
      1.3. Solution Overview ..........................................6
      1.4. Relationship to RFC 5277 ...................................7
   2. Solution ........................................................8
      2.1. Event Streams ..............................................8
      2.2. Event Stream Filters .......................................9
      2.3. QoS ........................................................9
      2.4. Dynamic Subscriptions .....................................10
      2.5. Configured Subscriptions ..................................19
      2.6. Event Record Delivery .....................................27
      2.7. Subscription State Change Notifications ...................28
      2.8. Subscription Monitoring ...................................33
      2.9. Support for the "ietf-subscribed-notifications"
           YANG Module ...............................................34
   3. YANG Data Model Tree Diagrams ..................................34
      3.1. The "streams" Container ...................................34
      3.2. The "filters" Container ...................................35
      3.3. The "subscriptions" Container .............................35
   4. Event Notification Subscription YANG Module ....................37
   5. IANA Considerations ............................................66
   6. Implementation Considerations ..................................66
   7. Transport Requirements .........................................67
   8. Security Considerations ........................................68
   9. References .....................................................72
      9.1. Normative References ......................................72
      9.2. Informative References ....................................74
   Appendix A. Example Configured Transport Augmentation .............75
   Acknowledgments ...................................................77
   Authors' Addresses ................................................77

1. Introduction ๐Ÿฆ‹

1.1. Motivation ๐Ÿ’ญ

Things get interesting here because this doc defines a YANG data model and mechanisms that enable subscriber-specific subscriptions to a publisher's event streams. That effectively gives you a "subscribe, then publish" capability: the publisher understands each target receiver's customized information needs and access permissions before it marshals and pushes the subscribed event records ๐Ÿ”ฅ The receiver then gets a continuous, customized feed of publisher-generated information โœจ

Transport-wise, this works with any transport, which is very iykyk-level flexible ๐ŸŒธ Transports like the Network Configuration Protocol (NETCONF) [RFC6241] or RESTCONF [RFC8040] can be used to configure or dynamically signal subscriptions. Bindings for subscribed event record delivery over NETCONF and RESTCONF are defined in [RFC8640] and [RESTCONF-Notif], respectively ๐Ÿ’œ

The YANG data model in this document conforms to the Network Management Datastore Architecture defined in [RFC8342] ๐Ÿงš

The original is pretty clear on this, but to be extra: this doc is the leveled-up version of older subscription stuff ๐Ÿ’–

1.1. Motivation ๐Ÿ’…

Okay so there were various limitations to subscriptions as described in [RFC5277], and the requirements in [RFC7923] alleviated some of them to a degree. Fixing whatever's still broken is the main reason for this work ๐ŸŽ€ The key capabilities this document supports are:

  • multiple subscriptions on a single transport session
  • support for dynamic and configured subscriptions
  • modification of an existing subscription in progress
  • per-subscription operational counters
  • negotiation of subscription parameters (through the use of hints returned as part of declined subscription requests)
  • subscription state change notifications (e.g., publisher-driven suspension, parameter modification)
  • independence from transport

Honestly this list is giving everything a good subscription should be ๐Ÿ˜ญ๐Ÿซถ

1.2. Terminology ๐Ÿ“–

Here's the key word situation ๐Ÿ”ฅ 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 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here ๐Ÿ’€

Now the vocab, bestie ๐Ÿ“

  • Client: Defined in [RFC8342].
  • Configuration: Defined in [RFC8342].
  • Configuration datastore: Defined in [RFC8342].
  • Configured subscription: A subscription installed via configuration into a configuration datastore.
  • Dynamic subscription: A subscription created dynamically by a subscriber via a Remote Procedure Call (RPC).
  • Event: An occurrence of something that may be of interest. Examples include a configuration change, a fault, a change in status, crossing a threshold, or an external input to the system.
  • Event occurrence time: A timestamp matching the time an originating process identified as when an event happened.
  • Event record: A set of information detailing an event.
  • Event stream: A continuous, chronologically ordered set of events aggregated under some context.
  • Event stream filter: Evaluation criteria that may be applied against event records in an event stream. Event records pass the filter when specified criteria are met.
  • Notification message: Information intended for a receiver indicating that one or more events have occurred.
  • Publisher: An entity responsible for streaming notification messages per the terms of a subscription.
  • Receiver: A target to which a publisher pushes subscribed event records. For dynamic subscriptions, the receiver and subscriber are the same entity.
  • Subscriber: A client able to request and negotiate a contract for the generation and push of event records from a publisher. For dynamic subscriptions, the receiver and subscriber are the same entity.
  • Subscription: A contract with a publisher, stipulating the information that one or more receivers wish to have pushed from the publisher without the need for further solicitation.

All YANG tree diagrams used in this document follow the notation defined in [RFC8340] ๐ŸŒˆ


Part 1 of 14 is done, bestie, and part 2 is next ๐Ÿฆ‹๐Ÿ’–

1.3. Solution Overview โœจ

okay so this doc describes a transport-agnostic mechanism ๐Ÿฆ‹ for subscribing to and receiving content from an event stream in a publisher. The whole thing runs on something called a subscription ๐Ÿ’–

There are two types of subscriptions ๐Ÿ“:

  1. Dynamic subscriptions ๐Ÿ’…: a subscriber starts a subscription negotiation with a publisher via an RPC. If the publisher can serve it, it says yes and then starts pushing notification messages back to the subscriber ๐Ÿซถ. If the publisher can't serve it as requested, you get an error response ๐Ÿ˜ญ. That response MAY include hints for subscription parameters that, had they been present, may have enabled the dynamic subscription request to be accepted.
  2. Configured subscriptions ๐ŸŽ€: these let you manage subscriptions through configuration, so a publisher can send notification messages to a receiver. Support is optional, and you can tell if it's there because it's advertised via a YANG feature ๐ŸŒˆ.

Dynamic and configured subscriptions are different in a few lowkey important ways ๐Ÿ‘‘:

  • The lifetime of a dynamic subscription is bound by the transport session used to establish it. For connection-oriented stateful transports like NETCONF, losing the transport session immediately terminates any associated dynamic subscriptions ๐Ÿ’€. For connectionless or stateless transports like HTTP, a lack of receipt acknowledgment of a sequential set of notification messages and/or keep-alives can be used to trigger termination of a dynamic subscription. Now contrast that with configured subscriptions, whose lifetime is driven by relevant configuration being present in the publisher's applied configuration. Being tied to configuration operations implies that (1) configured subscriptions can be configured to persist across reboots and (2) a configured subscription can persist even when its publisher is fully disconnected from any network ๐Ÿงš.
  • Configured subscriptions can be modified by any configuration client with write permission on the configuration of the subscription. Dynamic subscriptions can only be modified via an RPC request made by the original subscriber, or by a change to configuration data referenced by the subscription ๐Ÿ™ˆ.

Note that there is no mixing and matching of dynamic and configured operations on a single subscription ๐Ÿšซ. Specifically, a configured subscription cannot be modified or deleted using RPCs defined in this document. Similarly, a dynamic subscription cannot be directly modified or deleted by configuration operations. It is, however, possible to perform a configuration operation that indirectly impacts a dynamic subscription. By changing the value of a preconfigured filter referenced by an existing dynamic subscription, the selected event records passed to a receiver might change ๐Ÿ‘€.

Also note that transport-specific specifications based on this specification MUST detail the lifecycle of dynamic subscriptions as well as the lifecycle of configured subscriptions (if supported) ๐Ÿ“.

A publisher MAY terminate a dynamic subscription at any time. Similarly, it MAY decide to temporarily suspend the sending of notification messages for any dynamic subscription, or for one or more receivers of a configured subscription. Such termination or suspension is driven by internal considerations of the publisher ๐ŸŒธ.

1.4. Relationship to RFC 5277

this doc is meant to be a superset of the subscription capabilities first defined in [RFC5277] ๐Ÿ’œ. It's important to know what got reused and what got replaced, especially if you're extending an existing implementation that's based on [RFC5277]. The key relationships between the two documents are:

  • This document defines a transport-independent capability; [RFC5277] is specific to NETCONF.
  • For the new operations, the data model defined in this document is used instead of the data model defined in Section 3.4 of [RFC5277].
  • The RPC operations in this document replace the operation <create-subscription> as defined in [RFC5277], Section 4.
  • The <notification> message of [RFC5277], Section 4 is used.
  • The included contents of the "NETCONF" event stream are identical between this document and [RFC5277].
  • A publisher MAY implement both the Notification Management Schema and RPCs defined in [RFC5277] and this document concurrently.
  • Unlike [RFC5277], this document enables a single transport session to intermix notification messages and RPCs for different subscriptions ๐Ÿ”ฅ.
  • A subscription "stop-time" can be specified as part of a notification replay. This supports a capability analogous to the <stopTime> parameter of [RFC5277]. However, in this specification, a "stop-time" parameter can also be applied without replay โฐ.

2. Solution

Per the overview in Section 1.3, this section details the overall context, state machines, and subsystems that may be assembled to allow the subscription of events from a publisher ๐Ÿงฉ.

2.1. Event Streams

An event stream is a named entity on a publisher. This entity exposes a continuously updating set of YANG-defined event records ๐Ÿ“ก. An event record is an instantiation of a "notification" YANG statement. If the "notification" is defined as a child to a data node, the instantiation includes the hierarchy of nodes that identifies the data node in the datastore (see Section 7.16.2 of [RFC7950]). Each event stream is available for subscription. Identifying a) how event streams are defined (other than the NETCONF stream), b) how event records are defined/generated, and c) how event records are assigned to event streams is out of scope for this document ๐Ÿ™….

There is only one reserved event stream name in this document: "NETCONF". The "NETCONF" event stream contains all NETCONF event record information supported by the publisher, except where an event record has explicitly been excluded from the stream. Beyond the "NETCONF" stream, implementations MAY define additional event streams โœจ.

As YANG-defined event records are created by a system, they may be assigned to one or more streams. The event record is distributed to a subscription's receiver(s) where (1) a subscription includes the identified stream and (2) subscription filtering does not exclude the event record from that receiver ๐Ÿ’Œ.

Access control permissions may be used to silently exclude event records from an event stream for which the receiver has no read access. See Section 3.4.6 of [RFC8341] for an example of how this might be accomplished. Note that per Section 2.7 of this document, subscription state change notifications are never filtered out ๐Ÿซฃ.

If no access control permissions are in place for event records on an event stream, then a receiver MUST be allowed access to all the event records. If subscriber permissions change during the lifecycle of a subscription and event stream access is no longer permitted, then the subscription MUST be terminated ๐Ÿšช.

Event records MUST NOT be delivered to a receiver in a different order than the order in which they were placed on an event stream ๐Ÿซถ.

2.2. Event Stream Filters

This document defines an extensible filtering mechanism ๐Ÿ”. The filter itself is a boolean test that is placed on the content of an event record. A "false" filtering result causes the event record to be excluded from delivery to a receiver. A filter never results in information being stripped from an event record prior to that event record being encapsulated in a notification message. The two optional event stream filtering syntaxes supported are [XPATH] and subtree [RFC6241] ๐Ÿ’….

If no event stream filter is provided in a subscription, all event records on an event stream are to be sent ๐Ÿ“ฌ.

2.3. QoS

This document provides for several Quality of Service (QoS) parameters ๐Ÿšฆ. These parameters indicate the treatment of a subscription relative to other traffic between publisher and receiver. Included are:

  • A "dscp" marking to differentiate prioritization of notification messages during network transit.
  • A "weighting" so that bandwidth proportional to this weighting can be allocated to this subscription relative to other subscriptions.
  • A "dependency" upon another subscription.

If the publisher supports the "dscp" feature, then a subscription with a "dscp" leaf MUST result in a corresponding Differentiated Services Code Point (DSCP) marking [RFC2474] being placed in the IP header of any resulting notification messages and subscription state change notifications. A publisher MUST respect the DSCP markings for subscription traffic egressing that publisher ๐Ÿท๏ธ.

Different DSCP code points require different transport connections. As a result, where TCP is used, a publisher that supports the "dscp" feature must ensure that a subscription's notification messages are returned in a single TCP transport session where all traffic shares the subscription's "dscp" leaf value. If this cannot be guaranteed, any "establish-subscription" RPC request SHOULD be rejected with a "dscp-unavailable" error ๐Ÿ™.

For the "weighting" parameter, when concurrently dequeuing notification messages from multiple subscriptions to a receiver, the publisher MUST allocate bandwidth to each subscription proportional to the weights assigned to those subscriptions. "Weighting" is an optional capability of the publisher. Support for it is identified via the "qos" feature โš–๏ธ.

If a subscription has the "dependency" parameter set, then any buffered notification messages containing event records selected by the parent subscription MUST be dequeued prior to the notification messages of the dependent subscription. If notification messages have dependencies on each other, the notification message queued the longest MUST go first. If a "dependency" included in an RPC references a subscription that does not exist or is no longer accessible to that subscriber, that "dependency" MUST be silently removed. "Dependency" is an optional capability of the publisher. Support for it is identified via the "qos" feature ๐Ÿ”—.

"Dependency" and "weighting" parameters will only be respected and enforced between subscriptions that share the same "dscp" leaf value ๐ŸŽฏ.

There are additional types of publisher capacity overload that this specification does not address, as they are out of scope. For example, the prioritization of which subscriptions have precedence when the publisher CPU is overloaded is not discussed. As a result, implementation choices will need to be made to address such considerations ๐Ÿคทโ€โ™€๏ธ.

2.4. Dynamic Subscriptions

Dynamic subscriptions are managed via protocol operations (in the form of RPCs, per Section 7.14 of [RFC7950]) made against targets located in the publisher ๐ŸŽ€. These RPCs have been designed extensibly so that they may be augmented for subscription targets beyond event streams. For examples of such augmentations, see the RPC augmentations in the YANG data model provided in [RFC8641] ๐ŸŒŸ.

2.4.1. Dynamic Subscription State Machine ๐Ÿฆ‹

okay so this is the publisher's state machine for a dynamic subscription ๐Ÿ’… each state gets its own little box. The big tea is that a subscription doesn't exist at the publisher at all until an establish-subscription RPC gets accepted. A subscriber just asking for one isn't enough to make it externally visible ๐Ÿ™ˆ The start and end states are there to show when a subscription gets created and deleted.

                      .........
                      : start :
                      :.......:
                          |
                 establish-subscription
                          |
                          |   .-------modify-subscription--------.
                          v   v                                  |
                    .-----------.                          .-----------.
         .--------. | receiver  |--insufficient CPU, b/w-->| receiver  |
     modify-       '|  active   |                          | suspended |
     subscription   |           |<----CPU, b/w sufficient--|           |
         ---------->'-----------'                          '-----------'
                          |                                      |
               delete/kill-subscription                     delete/kill-
                          |                                 subscription
                          |                                      |
                          v                                      |
                      .........                                  |
                      :  end  :<---------------------------------'
                      :.......:

      Figure 1: Publisher's State Machine for a Dynamic Subscription

Here's what's important in this state machine โœจ

  • Successful establish-subscription or modify-subscription RPCs move the subscription to the "active" state. ๐ŸŽ€
  • Failed modify-subscription RPCs leave the subscription in its previous state, with no visible change to any streaming updates. ๐Ÿ˜Œ
  • A delete-subscription or kill-subscription RPC ends the subscription, and so does reaching a stop-time. ๐Ÿ’€
  • A publisher may choose to suspend a subscription when there isn't sufficient CPU or bandwidth available to service it. The subscriber hears about this through the subscription-suspended subscription state change notification. ๐Ÿซถ
  • A suspended subscription may be modified by the subscriber (for example, to try using fewer resources). A successful modification returns the subscription to the "active" state. ๐ŸŒธ
  • Even without a modify-subscription request, a publisher may return a subscription to the "active" state once sufficient resources are available again. The subscriber gets told about this through the subscription-resumed subscription state change notification. ๐ŸŒˆ

2.4.2. Establishing a Dynamic Subscription ๐Ÿงš

the establish-subscription RPC is how a subscriber asks for a subscription to be created ๐Ÿ’–

these are the input parameters of the operation:

  • A stream name, which identifies the targeted event stream the subscription is applied against. ๐Ÿ“
  • An event stream filter, which may reduce the set of event records pushed. โœจ
  • If the transport used by the RPC supports multiple encodings, an optional encoding for the event records pushed. If no encoding is included, the encoding of the RPC MUST be used. ๐Ÿ’…
  • An optional stop-time for the subscription. If no stop-time is present, notification messages keep getting sent until the subscription is terminated. โญ
  • An optional replay-start-time for the subscription. The replay-start-time MUST be in the past, and it means the subscription is asking for a replay of previously generated information from the event stream. More on replay in Section 2.4.2.1. If there's no replay-start-time, the subscription starts right away. ๐Ÿ”ฅ

If the publisher can satisfy the establish-subscription request, it replies with an identifier for the subscription and then immediately starts streaming notification messages ๐Ÿ‘‘

here's a tree diagram for establish-subscription. Everything in this tree is described in the YANG module in Section 4 ๐ŸŒธ

       +---x establish-subscription
          +---w input
          |  +---w (target)
          |  |  +--:(stream)
          |  |     +---w (stream-filter)?
          |  |     |  +--:(by-reference)
          |  |     |  |  +---w stream-filter-name
          |  |     |  |          stream-filter-ref
          |  |     |  +--:(within-subscription)
          |  |     |     +---w (filter-spec)?
          |  |     |        +--:(stream-subtree-filter)
          |  |     |        |  +---w stream-subtree-filter?   <anydata>
          |  |     |        |          {subtree}?
          |  |     |        +--:(stream-xpath-filter)
          |  |     |           +---w stream-xpath-filter?
          |  |     |                   yang:xpath1.0 {xpath}?
          |  |     +---w stream                               stream-ref
          |  |     +---w replay-start-time?
          |  |             yang:date-and-time {replay}?
          |  +---w stop-time?
          |  |       yang:date-and-time
          |  +---w dscp?                                      inet:dscp
          |  |       {dscp}?
          |  +---w weighting?                                 uint8
          |  |       {qos}?
          |  +---w dependency?
          |  |       subscription-id {qos}?
          |  +---w encoding?                                  encoding
          +--ro output
             +--ro id                            subscription-id
             +--ro replay-start-time-revision?   yang:date-and-time
                     {replay}?

            Figure 2: "establish-subscription" RPC Tree Diagram

A publisher MAY reject the establish-subscription RPC for lots of reasons, as described in Section 2.4.6 ๐Ÿ™…โ€โ™€๏ธ The resulting RPC error response MAY include details on input parameters that, if considered in a subsequent establish-subscription RPC, may result in a successful subscription. Any such hints MUST be transported in a yang-data establish-subscription-stream-error-info container included in the RPC error response. this is the good stuff for retrying ๐Ÿซถ

here's a tree diagram for the establish-subscription-stream-error-info RPC yang-data. Same deal: everything in it is described in the YANG module in Section 4 โœจ

       yang-data establish-subscription-stream-error-info
         +--ro establish-subscription-stream-error-info
            +--ro reason?                   identityref
            +--ro filter-failure-hint?      string

           Figure 3: "establish-subscription-stream-error-info"
                        RPC yang-data Tree Diagram

2.4.2.1. Requesting a Replay of Event Records ๐Ÿ•ฐ๏ธ

replay is giving "time travel but make it networking" ๐Ÿ”ฎ It lets a subscription that's just starting also pass along event records that were generated in the recent past. As the subscription initializes, it sends any event records in the target event stream that meet the filter criteria and have an event time after the replay-start-time and before the stop-time, if there is one. The end of those historical records gets marked by the replay-completed subscription state change notification. Any event records generated since the subscription was established can follow after that. For a particular subscription, all event records are delivered in the order they were placed in the event stream ๐ŸŒˆ

Replay is optional, and it depends on an event stream that supports some form of logging. This document doesn't put any restrictions on the size or form of the log, where it lives in the publisher, or when log entries get purged ๐Ÿ™ˆ

Including a replay-start-time in an establish-subscription RPC is what signals a replay request. If the replay-start-time is earlier than what the publisher's retained history supports, then, if the subscription is accepted, the publisher's actual revised start time MUST be set in the returned replay-start-time-revision object ๐Ÿ’œ

A stop-time can be included in a replay subscription too. For a replay subscription, the stop-time MAY be earlier than the current time, but it MUST be later than the replay-start-time ๐Ÿ“

If the given replay-start-time is later than the time marked in any event records retained in the replay buffer, then the publisher MUST send a replay-completed notification immediately after a successful establish-subscription RPC response. โญ

If an event stream supports replay, the replay-support leaf is present in the /streams/stream list entry for that event stream. An event stream that does support replay isn't expected to have an unlimited supply of saved notifications for any given replay request ๐Ÿ˜ญ To figure out the timeframe available for replay, subscribers can read the leafs replay-log-creation-time and replay-log-aged-time. See Figure 18 for the YANG tree and Section 4 for the YANG module describing these elements. The actual size of the replay log at any given time is a publisher-specific matter. Control parameters for the replay log are outside the scope of this document ๐ŸŽ€

2.4.3. Modifying a Dynamic Subscription โœ๏ธ

Okay so the "modify-subscription" operation lets you change the terms of a subscription that already exists ๐Ÿ’…. You can do it as many times as you want, bestie ๐Ÿ”, but ONLY through a transport session that's connecting to the subscriber, so no sneaking it in from somewhere else ๐Ÿ™…โ€โ™€๏ธ. If the publisher says yes, it confirms success back to you and then immediately starts sending event records based on the new terms โœจ. No cap, that's an instant glow-up ๐Ÿ’–.

Subscriptions made through configuration can't be modified with this RPC ๐Ÿ™ƒ. BUT you can still use configuration to tweak the stuff the subscription points at, like a filter it references ๐Ÿงš.

Here's the tree diagram for "modify-subscription". All the objects in this tree are described in the YANG module in Section 4 ๐Ÿ“š.

       +---x modify-subscription
          +---w input
             +---w id
             |       subscription-id
             +---w (target)
             |  +--:(stream)
             |     +---w (stream-filter)?
             |        +--:(by-reference)
             |        |  +---w stream-filter-name
             |        |          stream-filter-ref
             |        +--:(within-subscription)
             |           +---w (filter-spec)?
             |              +--:(stream-subtree-filter)
             |              |  +---w stream-subtree-filter?   <anydata>
             |              |          {subtree}?
             |              +--:(stream-xpath-filter)
             |                 +---w stream-xpath-filter?
             |                         yang:xpath1.0 {xpath}?
             +---w stop-time?
                     yang:date-and-time

             Figure 4: "modify-subscription" RPC Tree Diagram

If the publisher accepts the changes on a subscription that's currently suspended ๐Ÿ˜ด, the subscription gets resumed right away (aka the modified subscription goes back to the "active" state ๐ŸŸข). The publisher MAY immediately suspend this newly modified subscription through the "subscription-suspended" notification before any event records are sent ๐Ÿ›‘. Plot twist, but it's allowed ๐Ÿ™ˆ.

If the publisher rejects the RPC request, the subscription stays exactly as it was before. Like, the request has zero impact whatsoever ๐Ÿซ . Rejection for any reason shows up as an RPC error, as described in Section 2.4.6. The contents of a rejected RPC MAY include hints on inputs that (if considered) may result in a successfully modified subscription ๐Ÿ’ก. These hints MUST be transported in a yang-data "modify-subscription-stream-error-info" container inserted into the RPC error response ๐ŸŽ€.

Here's the tree diagram for the "modify-subscription-stream-error-info" RPC yang-data. Again, everything in this tree is described in the YANG module in Section 4 ๐Ÿงพ.

       yang-data modify-subscription-stream-error-info
         +--ro modify-subscription-stream-error-info
            +--ro reason?                identityref
            +--ro filter-failure-hint?   string

             Figure 5: "modify-subscription-stream-error-info"
                        RPC yang-data Tree Diagram

2.4.4. Deleting a Dynamic Subscription ๐Ÿ—‘๏ธ

The "delete-subscription" operation is how you cancel an existing subscription ๐Ÿ™…โ€โ™€๏ธ. If the publisher accepts the request and has indicated success, the publisher MUST NOT send any more notification messages for that subscription. Done and dusted ๐Ÿ’€.

Here's the tree diagram for "delete-subscription". The YANG module in Section 4 has the details on all these objects ๐Ÿ“–.

       +---x delete-subscription
          +---w input
             +---w id     subscription-id

             Figure 6: "delete-subscription" RPC Tree Diagram

Dynamic subscriptions can only be deleted with this RPC, and only through a transport session connecting to the subscriber ๐Ÿ”’. Configured subscriptions can't be deleted using RPCs at all ๐Ÿ™ˆ.

2.4.5. Killing a Dynamic Subscription ๐Ÿ”ช

The "kill-subscription" operation lets an operator end a dynamic subscription that is NOT tied to the transport session they used for the RPC ๐Ÿ‘‘. A publisher MUST terminate any dynamic subscription identified by the "id" parameter in the RPC request, if such a subscription exists. Iconic energy โœจ.

Configured subscriptions can't be killed with this RPC. Instead, they get deleted as part of regular configuration operations ๐Ÿ› ๏ธ. Publishers MUST reject any RPC attempt to kill a configured subscription. Nice try, though ๐Ÿ’….

Here's the tree diagram for "kill-subscription". Everything in it is described in the YANG module in Section 4 ๐Ÿงฉ.

       +---x kill-subscription
          +---w input
             +---w id     subscription-id

              Figure 7: "kill-subscription" RPC Tree Diagram

2.4.6. RPC Failures ๐Ÿšจ

Whenever an RPC doesn't work out, the publisher sends back relevant info as part of the RPC error response ๐Ÿ˜ฌ. Transport-level error processing MUST be done before the RPC error processing described in this section. In every case, the RPC error info the publisher returns will use existing transport-layer RPC structures, like the ones you see with NETCONF (Appendix A of [RFC6241]) or RESTCONF (Section 7.1 of [RFC8040]) ๐Ÿงƒ. These structures MUST be able to encode the subscription-specific errors identified below and defined in this document's YANG data model ๐ŸŽฏ.

Because there's so much variety, how subscription errors get encoded in an RPC error response depends on the transport ๐ŸŒˆ. Here are the valid errors for each RPC:

   establish-subscription         modify-subscription
   ----------------------         ----------------------
   dscp-unavailable               filter-unsupported
   encoding-unsupported           insufficient-resources
   filter-unsupported             no-such-subscription
   insufficient-resources
   replay-unsupported

   delete-subscription            kill-subscription
   ----------------------         ----------------------
   no-such-subscription           no-such-subscription

For a NETCONF-based example of the "no-such-subscription" error response from the list above, check out [RFC8640], Figure 10 ๐Ÿ”.

There's one final set of transport-independent RPC error elements in the YANG data model defined in this document ๐ŸŽ. These are three yang-data structures that let the publisher pass along any error info that doesn't fit into the existing transport-layer RPC structures:

  1. "establish-subscription-stream-error-info": This MUST be returned with the leaf "reason" populated if an RPC error reason has not been placed elsewhere in the transport portion of a failed "establish-subscription" RPC response. This MUST be sent if hints on how to overcome the RPC error are included. ๐Ÿง 
  2. "modify-subscription-stream-error-info": This MUST be returned with the leaf "reason" populated if an RPC error reason has not been placed elsewhere in the transport portion of a failed "modify-subscription" RPC response. This MUST be sent if hints on how to overcome the RPC error are included. ๐Ÿ› ๏ธ
  3. "delete-subscription-error-info": This MUST be returned with the leaf "reason" populated if an RPC error reason has not been placed elsewhere in the transport portion of a failed "delete-subscription" or "kill-subscription" RPC response. ๐Ÿซถ

2.5. Configured Subscriptions ๐Ÿ“ฎ

A configured subscription is one installed via configuration โš™๏ธ. Any configuration client with the proper permissions can modify configured subscriptions ๐Ÿ”‘. You can modify or terminate them via configuration at any point during their lifetime โณ. Multiple configured subscriptions MUST be supportable over a single transport session ๐Ÿงต.

Configured subscriptions have a few characteristics that set them apart from dynamic subscriptions ๐Ÿ’œ:

  • persistence across publisher reboots, ๐Ÿ”„
  • persistence even when the transport is unavailable, and ๐ŸŒ™
  • an ability to send notification messages to more than one receiver. (Heads up: receivers are unaware of the existence of any other receivers ๐Ÿ™ˆ.)

On the publisher, support for configured subscriptions is optional and advertised using the "configured" feature ๐Ÿฆ‹. On a receiver of a configured subscription, support for dynamic subscriptions is optional. However, if replaying missed event records is required for a configured subscription, support for dynamic subscriptions is highly recommended ๐Ÿ“. In that case, a separate dynamic subscription can be established to retransmit the missing event records ๐Ÿ”.

On top of the subscription parameters available to dynamic subscriptions (see Section 2.4.2), configured subscriptions also get these extra parameters โœจ:

  • A "transport", which identifies the transport protocol to use to connect with all subscription receivers. ๐Ÿšš
  • One or more receivers, each intended as the destination for event records. Note that each individual receiver is identifiable by its "name". ๐Ÿ“ฌ
  • Optional parameters to identify where traffic should egress a publisher: ๐Ÿšช
    • A "source-interface", which identifies the egress interface to use from the publisher. Publisher support for this parameter is optional and advertised using the "interface-designation" feature. ๐Ÿ”Œ
    • A "source-address" address, which identifies the IP address to stamp on notification messages destined for the receiver. ๐Ÿท๏ธ
    • A "source-vrf", which identifies the Virtual Routing and Forwarding (VRF) instance on which to reach receivers. This VRF is a network instance as defined in [RFC8529]. Publisher support for VRFs is optional and advertised using the "supports-vrf" feature. ๐ŸŒ

If none of the above parameters are set, notification messages MUST egress the publisher's default interface ๐Ÿ›ฃ๏ธ.

A tree diagram that includes these parameters is provided in Figure 20 in Section 3.3 ๐Ÿ—บ๏ธ. These parameters are described in the YANG module in Section 4 ๐Ÿ“š๐Ÿ’….

2.5.1. Configured Subscription State Machine ๐Ÿฆ‹

Okay bestie, here's the state machine for a configured subscription on the publisher ๐Ÿ’… It has three states, "valid", "invalid", and "concluded", plus all the transitions between them. The start and end states show when a configured subscription gets created or deleted. Creating or modifying one kicks off an evaluation by the publisher to decide whether it's "valid" or "invalid". The publisher uses its own criteria for that, and if it lands in "valid", the subscription becomes operational. That's (1) in the diagram โœจ

 .........
 : start :-.
 :.......: |
      create  .---modify-----.----------------------------------.
           |  |              |                                  |
           V  V          .-------.         .......         .---------.
  .----[evaluate]--no--->|invalid|-delete->: end :<-delete-|concluded|
  |                      '-------'         :.....:         '---------'
  |-[evaluate]--no-(2).      ^                ^                 ^
  |        ^          |      |                |                 |
 yes       |          '->unsupportable      delete           stop-time
  |      modify         (subscription-   (subscription-   (subscription-
  |        |             terminated*)     terminated*)      concluded*)
  |        |                 |                |                 |
 (1)       |                (3)              (4)               (5)
  |   .---------------------------------------------------------------.
  '-->|                         valid                                 |
      '---------------------------------------------------------------'
 Legend:
   Dotted boxes: subscription added or removed via configuration
   Dashed boxes: states for a subscription
   [evaluate]: decision point on whether the subscription
               is supportable
   (*): resulting subscription state change notification

     Figure 8: Publisher's State Machine for a Configured Subscription

A subscription in "valid" can slide over to "invalid" in two ways ๐Ÿ˜ญ First, it might get modified in a way that fails a re-evaluation (that's (2)). Second, the publisher might decide it's no longer supportable. That could happen because of an unexpected but sustained increase in an event stream's event records, degraded CPU capacity, a more complex referenced filter, or other subscriptions that have usurped resources (that's (3)). Either way, a "subscription-terminated" notification goes out to any receivers in the "active" or "suspended" state ๐Ÿ’€ A "valid" subscription can also move to "concluded" via (5) if a configured stop time has been reached, and then "subscription-concluded" goes to receivers in "active" or "suspended". Last but not least, configuration can delete the subscription (4) ๐Ÿซถ

When a subscription is "valid", the publisher will try to connect with all receivers of that configured subscription and push notification messages ๐ŸŒธ The receiver state machine is fully contained in the configured subscription's state machine, and it only matters while the configured subscription is "valid". Here it is:

     .-----------------------------------------------------------------.
     |                         valid                                   |
     |   .----------.                           .------------.         |
     |   | receiver |---timeout---------------->|  receiver  |         |
     |   |connecting|<----------------reset--(c)|disconnected|         |
     |   |          |<-transport                '------------'         |
     |   '----------'  loss,reset------------------------------.       |
     |      (a)          |                                     |       |
     |  subscription-   (b)                                   (b)      |
     |  started*    .--------.                             .---------. |
     |       '----->|        |(d)-insufficient CPU,------->|         | |
     |              |receiver|    buffer overflow          |receiver | |
     | subscription-| active |                             |suspended| |
     |   modified*  |        |<----CPU, b/w sufficient,-(e)|         | |
     |        '---->'--------'     subscription-modified*  '---------' |
     '-----------------------------------------------------------------'

     Legend:
       Dashed boxes that include the word "receiver" show the possible
       states for an individual receiver of a valid configured
       subscription.

      * indicates a subscription state change notification

      Figure 9: Receiver State Machine for a Configured Subscription
                              on a Publisher

When a configured subscription first hits "valid", the "state" leaf of each receiver starts out at "connecting" ๐Ÿ“ If there's no transport connectivity to any receivers and there's stuff to deliver, a transport session gets set up (e.g., per [RFC8071]). Receivers hop over to "active" once a "subscription-started" notification gets successfully passed to them (a). Event records only go to active receivers. A receiver stays active if (1) transport connectivity to it is active and (2) event records aren't being dropped because the publisher's sending capacity is maxed out โœจ A receiver of a configured subscription MUST be moved to the "connecting" state if it gets reset via the "reset" action (b), (c). For more on the "reset" action, see Section 2.5.5. If transport connectivity can't be achieved while in "connecting", the receiver MAY be moved to "disconnected" ๐Ÿ™ˆ

A receiver of a configured subscription MUST be moved to "suspended" if there's transport connectivity between the publisher and receiver, but (1) delivery of notification messages is failing because the publisher's buffer capacity has been reached, or (2) notification messages can't be generated for that receiver because of insufficient CPU (d). The receiver gets told about this with the "subscription-suspended" subscription state change notification ๐Ÿงš

A receiver of a configured subscription MUST be returned to "active" from "suspended" when notification messages can be generated, bandwidth is sufficient to handle them, and the receiver has successfully been sent a "subscription-resumed" or "subscription-modified" subscription state change notification (e). Which one gets sent depends on whether the subscription was modified during the suspension ๐Ÿ‘‘

Modifying a configured subscription is possible at any time ๐Ÿ’œ A "subscription-modified" notification goes to all active receivers, and it's immediately followed by notification messages that match the new parameters. Suspended receivers get informed of the modification too, but that notification waits until their suspension ends (e). Iconic patience ๐Ÿ’…

The mechanisms above are mirrored in the RPCs and notifications defined in this document. These RPCs and notifications were designed to be extensible, so they can allow subscriptions into targets other than event streams ๐ŸŒˆ For instance, the YANG module defined in Section 5 of [RFC8641] augments /sn:modify-subscription/sn:input/sn:target.

2.5.2. Creating a Configured Subscription ๐ŸŽ€

Configured subscriptions get established using configuration operations against the top-level subscriptions subtree.

Here's the thing: there's no explicit association with an existing transport session, so configuration operations MUST include extra parameters beyond the ones dynamic subscriptions use ๐Ÿซถ Those parameters identify each receiver, how to connect with that receiver, and possibly whether the notification messages need to come from a specific egress interface on the publisher. Receiver-specific transport connectivity parameters MUST be configured via transport-specific augmentations to this specification. See Section 2.5.7 for details ๐ŸŒธ

Once a subscription is successfully established, the publisher immediately sends a "subscription-started" subscription state change notification to each receiver โœจ It's totally possible that, at configuration, reboot, or even during steady-state operations, a transport session isn't available to the receiver yet. In that case, once there's something to transport for an active subscription, transport-specific "call home" operations [RFC8071] get used to set up the connection. When transport connectivity is available, notification messages can then be pushed ๐Ÿฆ‹

With active configured subscriptions, it's allowable to buffer event records even after a "subscription-started" has been sent ๐Ÿ’… However, if events get lost (rather than just delayed) because replay buffer capacity was reached, a new "subscription-started" must be sent. That new "subscription-started" signals an event record discontinuity ๐Ÿ˜ญ

To see an example of subscription creation using configuration operations over NETCONF, check out Appendix A ๐Ÿ’–

2.5.3. Modifying a Configured Subscription ๐ŸŒˆ

Configured subscriptions can be modified using configuration operations against the top-level subscriptions subtree ๐Ÿ“

If the modification involves adding receivers, the added receivers get placed in the "connecting" state. If a receiver is removed, the "subscription-terminated" subscription state change notification is sent to that receiver if it's active or suspended ๐Ÿ’€

If the modification involves changing the policies for the subscription, the publisher sends a "subscription-modified" notification to the currently active receivers โœจ For any suspended receivers, the "subscription-modified" notification gets delayed until that receiver's subscription has been resumed. (Note: in this case, the "subscription-modified" notification tells the receiver that the subscription has been resumed, so no additional "subscription-resumed" needs to be sent. Also note that if multiple modifications happen during the suspension, only the "subscription-modified" notification describing the latest one needs to be sent to the receiver ๐Ÿงš)

2.5.4. Deleting a Configured Subscription ๐Ÿซถ

Subscriptions can be deleted through configuration against the top-level subscriptions subtree ๐Ÿ’…

Immediately after a subscription is successfully deleted, the publisher sends a subscription state change notification to all receivers of that subscription, stating that the subscription has ended (i.e., "subscription-terminated") ๐Ÿ”ฅ

2.5.5. Resetting a Configured Subscription's Receiver

okay so sometimes a configured subscription to a receiver needs a reset ๐Ÿ”„. You do it with the "reset" action in the YANG module at /subscriptions/subscription/receivers/receiver/reset. This comes in handy when a publisher has timed out trying to reach a receiver ๐Ÿ˜ญ. When a reset happens, a transport session gets initiated if it's needed, and a new "subscription-started" notification gets sent ๐Ÿ’Œ. If the connectivity it needs already exists, this action doesn't touch transport connectivity at all โœจ.

2.5.6. Replay for a Configured Subscription

replay works on configured subscriptions too ๐ŸŽ€. You set it up through the "configured-replay" object on the subscription. Turning it on makes the publisher stream the buffered event records for the subscribed event stream ๐ŸŒŠ. Every buffered record that's been kept since the last publisher restart gets sent to each configured receiver ๐Ÿ’œ.

replay of event records created since restart is seriously useful ๐Ÿ™Œ. It lets event records that were generated before transport connectivity was set up still reach a receiver. Setting the restart time as the earliest configured replay time means old records from before the restart can't get resent ๐Ÿšซ. It also makes sure every configured receiver gets the same records, no matter how fast each one's transport connectivity comes up ๐Ÿƒโ€โ™€๏ธ. And by making restart the earliest possible time for event records to show up in notification messages, you get a well-understood timeframe for replay ๐Ÿ“.

so when any configured subscription's receivers become active, the buffered event records get sent right after the "subscription-started" notification ๐Ÿš€. If the publisher knows the last event record a receiver got, and the publisher hasn't rebooted, the next event record on the event stream that matches the filter criteria is the leading event record sent. Otherwise, the leading event record is the first one matching the filter criteria that comes after the latest of three different times: the "replay-log-creation-time", the "replay-log-aged-time", or the most recent publisher boot time ๐Ÿ•ฐ๏ธ. The "replay-log-creation-time" and "replay-log-aged-time" are covered in Section 2.4.2.1. The most recent boot time makes sure duplicate event records don't get replayed from an earlier boot ๐Ÿ™…โ€โ™€๏ธ.

it's totally possible a receiver wants event records from before the latest boot ๐Ÿ‘€. If those records exist and there's a configured replay, the publisher MUST send the time of the event record right before the "replay-start-time" in the "replay-previous-event-time" leaf ๐ŸŽฏ. That "replay-previous-event-time" leaf is how the receiver finds out that earlier events from before the reboot exist ๐Ÿง. And if the subscriber was already getting event records with the same subscription "id", the receiver can tell whether there was a gap where records generated on the publisher never arrived ๐Ÿ•ณ๏ธ. With that info, the receiver can decide to dynamically subscribe to pull down any event records that landed in the event stream before the most recent boot time ๐Ÿ“ฅ.

All other replay functionality stays the same as it is for dynamic subscriptions, as described in Section 2.4.2.1 ๐Ÿ’….

2.5.7. Transport Connectivity for a Configured Subscription

this spec doesn't care what transport you use ๐Ÿคทโ€โ™€๏ธ. Still, supporting a configured subscription usually means setting up transport connectivity, and the parameters for that are transport specific ๐Ÿ”Œ. So the YANG module in Section 4 can't directly define or expose those transport parameters on its own.

An implementation has to support the connection establishment process ๐Ÿ› ๏ธ. To make that work, the YANG data model in this document has a node where transport-specific parameters for a particular receiver can be augmented: "/subscriptions/subscription/receivers/receiver" ๐Ÿงฉ. By augmenting transport parameters onto that node, system developers can bring in the YANG objects they need for transport connectivity establishment ๐Ÿ’–.

which leads to this requirement ๐Ÿ“ฃ: a publisher that supports the feature "configured" MUST also support at least one YANG data model that augments transport connectivity parameters on "/subscriptions/subscription/receivers/receiver". For an example of that kind of augmentation, see Appendix A ๐Ÿ“–.

2.6. Event Record Delivery

whether a subscription is dynamic or configured, once it's set up, the publisher streams event records through notification messages based on the subscription's terms ๐ŸŒŸ. For dynamic subscriptions, the notification messages go over the session that was used to set up the subscription ๐Ÿ’ฌ. For configured subscriptions, they go over the connections specified by the transport and by each receiver of the configured subscription ๐Ÿ›ฃ๏ธ.

a notification message gets sent to a receiver when an event record isn't blocked by the specified filter criteria or by receiver permissions ๐Ÿšฆ. This notification message MUST include an <eventTime> object, as shown in [RFC5277], Section 4 โฐ. That <eventTime> MUST be at the top level of a YANG structured event record ๐Ÿ”.

here's an example of a compliant message, adapted from Section 4.2.10 of [RFC7950] and written in XML [W3C.REC-xml-20081126] ๐Ÿงพ:

  <notification
         xmlns="urn:ietf:params:xml:ns:netconf:notification:1.0">
      <eventTime>2007-09-01T10:00:00Z</eventTime>
      <link-failure xmlns="https://acme.example.com/system">
          <if-name>so-1/2/3.0</if-name>
          <if-admin-status>up</if-admin-status>
          <if-oper-status>down</if-oper-status>
      </link-failure>
  </notification>

Figure 10: Subscribed Notification Message

[RFC5277], Section 2.2.1 says a notification message is sent to a subscriber that started a <create-subscription> ๐Ÿ“œ. With this document, that statement from [RFC5277] should be read more broadly ๐Ÿ”. It means notification messages can also go to a subscriber that started an "establish-subscription", and to a configured receiver that was sent a "subscription-started" ๐ŸŽ‰.

when a dynamic subscription has been started or modified with "establish-subscription" or "modify-subscription", respectively, event records matching the newly applied filter criteria MUST NOT be sent until after the RPC reply has been sent ๐Ÿ™…โ€โ™€๏ธ.

when a configured subscription has been started or modified, event records matching the newly applied filter criteria MUST NOT be sent until after the "subscription-started" or "subscription-modified" notification has been sent, respectively ๐Ÿ›‘.

2.7. Subscription State Change Notifications

on top of sending event records to receivers, a publisher MUST also send subscription state change notifications whenever subscription-management events happen ๐Ÿ“ข.

these notifications are different from every other notification ๐Ÿ’…. They're never included in any event stream. Instead, they get inserted (as defined in this section) into the sequence of notification messages sent to a particular receiver ๐Ÿ“ฌ. Subscription state change notifications can't be dropped or filtered out, they can't be stored in replay buffers, and they're only delivered to the receivers a subscription actually affects ๐Ÿ”’. You can easily spot them among other notification messages because they're tagged with the YANG extension "subscription-state-notif" ๐Ÿท๏ธ.

the full set of subscription state change notifications is described in the subsections below ๐Ÿ—‚๏ธ.

2.7.1. "subscription-started"

this notification says a configured subscription has started and event records may be sent ๐Ÿš€. It includes all of the subscription's parameters, except for (1) transport connection information for one or more receivers and (2) origin information showing where notification messages will leave the publisher ๐Ÿ™ˆ. If the subscription used a referenced filter from the "filters" container, the notification still includes that filter's contents under the "within-subscription" subtree ๐Ÿงš.

Heads up: no "subscription-started" notifications are ever sent for dynamic subscriptions ๐Ÿ™…โ€โ™€๏ธ.

Here's the tree diagram for "subscription-started". Every object in this tree is described in the YANG module in Section 4 ๐ŸŒณ.

    +---n subscription-started {configured}?
       +--ro id
       |       subscription-id
       +--ro (target)
       |  +--:(stream)
       |     +--ro (stream-filter)?
       |     |  +--:(by-reference)
       |     |  |  +--ro stream-filter-name
       |     |  |          stream-filter-ref
       |     |  +--:(within-subscription)
       |     |     +--ro (filter-spec)?
       |     |        +--:(stream-subtree-filter)
       |     |        |  +--ro stream-subtree-filter?   <anydata>
       |     |        |          {subtree}?
       |     |        +--:(stream-xpath-filter)
       |     |           +--ro stream-xpath-filter?     yang:xpath1.0
       |     |                   {xpath}?
       |     +--ro stream                               stream-ref
       |     +--ro replay-start-time?
       |     |       yang:date-and-time {replay}?
       |     +--ro replay-previous-event-time?
       |             yang:date-and-time {replay}?
       +--ro stop-time?
       |       yang:date-and-time
       +--ro dscp?                                      inet:dscp
       |       {dscp}?
       +--ro weighting?                                 uint8 {qos}?
       +--ro dependency?
       |       subscription-id {qos}?
       +--ro transport?                                 transport
       |       {configured}?
       +--ro encoding?                                  encoding
       +--ro purpose?                                   string
               {configured}?

Figure 11: "subscription-started" Notification Tree Diagram

2.7.2. "subscription-modified" ๐Ÿ”ง

okay so this one is the "hey, something about your sub just changed" notification ๐Ÿ’… It shows up right after the last event records that used the old subscription parameters, and before any event records that use the new ones. Clean handoff, no mix-ups โœจ

Here's the tree for "subscription-modified". Everything inside it is defined in the YANG module in Section 4 ๐ŸŽ€

    +---n subscription-modified
       +--ro id
       |       subscription-id
       +--ro (target)
       |  +--:(stream)
       |     +--ro (stream-filter)?
       |     |  +--:(by-reference)
       |     |  |  +--ro stream-filter-name
       |     |  |          stream-filter-ref
       |     |  +--:(within-subscription)
       |     |     +--ro (filter-spec)?
       |     |        +--:(stream-subtree-filter)
       |     |        |  +--ro stream-subtree-filter?   <anydata>
       |     |        |          {subtree}?
       |     |        +--:(stream-xpath-filter)
       |     |           +--ro stream-xpath-filter?     yang:xpath1.0
       |     |                   {xpath}?
       |     +--ro stream                               stream-ref
       |     +--ro replay-start-time?
       |             yang:date-and-time {replay}?
       +--ro stop-time?
       |       yang:date-and-time
       +--ro dscp?                                      inet:dscp
       |       {dscp}?
       +--ro weighting?                                 uint8 {qos}?
       +--ro dependency?
       |       subscription-id {qos}?
       +--ro transport?                                 transport
       |       {configured}?
       +--ro encoding?                                  encoding
       +--ro purpose?                                   string
               {configured}?

Figure 12: "subscription-modified" Notification Tree Diagram

Publishers usually send this right after someone changes a config parameter that affects a configured subscription. It can also pop up at two other moments:

  1. If a configured subscription gets modified while a receiver is suspended, the notification waits until that suspension is lifted. Then it tells the receiver that the subscription has been both modified and resumed ๐Ÿฆ‹
  2. A "subscription-modified" subscription state change notification MUST be sent if the contents of the filter pointed to by the subscription's "stream-filter-ref" leaf have changed. This one goes out for any filter change that hits an active receiver of a configured or dynamic subscription ๐Ÿซถ

2.7.3. "subscription-terminated" ๐Ÿ’€

This one says "bye bye, no more event records for this subscription, don't wait up" ๐Ÿ˜ญ A publisher can stop sending event records to a receiver for these reasons:

  1. A configuration change removes a configured subscription, or a "kill-subscription" RPC ends a dynamic subscription. Both are identified with the reason "no-such-subscription".
  2. A referenced filter is no longer accessible. This is identified by the "filter-unavailable" identity.
  3. The event stream a subscription references is no longer accessible to the receiver. This is identified by the "stream-unavailable" identity.
  4. A suspended subscription has gone over some timeout. This is identified by the "suspension-timeout" identity.

Every reason above derives from the "subscription-terminated-reason" base identity, which is specified in the YANG data model in this document ๐ŸŒธ

Here's the tree for "subscription-terminated". Everything in it is described in the YANG module in Section 4 ๐Ÿ’œ

    +---n subscription-terminated
       +--ro id        subscription-id
       +--ro reason    identityref

Figure 13: "subscription-terminated" Notification Tree Diagram

Note: This subscription state change notification MUST be sent to a dynamic subscription's receiver when the subscription ends unexpectedly. That can happen when a "kill-subscription" RPC succeeds, or when something else (not reaching the subscription's "stop-time") makes the publisher end the subscription. It's giving "surprise, we're done" ๐Ÿ™ˆ

2.7.4. "subscription-suspended" โธ๏ธ

This notification means the publisher has paused sending event records to a receiver, and it also flags that events might get lost ๐Ÿ˜ฌ Suspension happens when capacity limits stop a publisher from serving a valid subscription. There are two conditions where that can happen:

  1. "insufficient-resources", when the publisher can't produce the requested event stream of notification messages, and
  2. "unsupportable-volume", when the bandwidth needed to deliver generated notification messages to a receiver goes over a threshold.

Both conditions are encoded in the "reason" object. No more notifications get sent until the subscription resumes or gets terminated ๐Ÿงš

Here's the tree for "subscription-suspended". Everything in it is described in the YANG module in Section 4 ๐Ÿ“

    +---n subscription-suspended
       +--ro id        subscription-id
       +--ro reason    identityref

Figure 14: "subscription-suspended" Notification Tree Diagram

2.7.5. "subscription-resumed" โ–ถ๏ธ

This is the "we're back, baby" notification โœจ It means a previously suspended subscription has resumed under the same terms it had before. Event records generated after this notification is issued can now be sent again ๐ŸŒˆ

Here's the tree for "subscription-resumed". Everything in it is described in the YANG module in Section 4 ๐Ÿ’–

    +---n subscription-resumed
       +--ro id    subscription-id

Figure 15: "subscription-resumed" Notification Tree Diagram

2.7.6. "subscription-completed" ๐Ÿ

This one is the victory lap ๐Ÿ† A subscription that has a "stop-time" has successfully finished passing event records once it reached that time.

Here's the tree for "subscription-completed". Everything in it is described in the YANG module in Section 4 ๐ŸŽ‰

    +---n subscription-completed {configured}?
       +--ro id    subscription-id

Figure 16: "subscription-completed" Notification Tree Diagram

2.7.7. "replay-completed" โช

This notification says "all the old event records up to now have been delivered to the receiver" ๐Ÿ“ผ It gets sent before any notification message that carries an event record with a timestamp later than either (1) the "stop-time" or (2) the subscription's start time.

If a subscription has no "stop-time", or its "stop-time" hasn't been reached yet, then once "replay-completed" has been sent, new event records keep coming in sequence as they naturally happen on the publisher โœจ

Here's the tree for "replay-completed". Everything in it is described in the YANG module in Section 4 ๐Ÿฆ‹

    +---n replay-completed {replay}?
       +--ro id    subscription-id

Figure 17: "replay-completed" Notification Tree Diagram

2.8. Subscription Monitoring ๐Ÿ‘€

In the operational state datastore, the "subscriptions" container keeps track of the state of every dynamic subscription and every configured subscription ๐Ÿ“‹ You can use datastore retrieval operations [RFC8641], or subscribe to the "subscriptions" container (Section 3.3), to monitor subscription state and how each one is connected to its receivers.

Every subscription in the operational state datastore is a list element. That list includes event counters for each receiver, the state of each receiver, and the subscription parameters currently in effect. If the leaf "configured-subscription-state" shows up, that means the subscription came into being through configuration. This leaf also tells you whether the subscription's current state is "valid", "invalid", or "concluded" ๐Ÿ’…

To follow the flow of event records in a subscription, there are two counters for each receiver. The first is "sent-event-records", which counts the events identified for sending to a receiver. The second is "excluded-event-records", which counts the event records that were not sent to a receiver. "excluded-event-records" shows the combined results of both access control and per-subscription filtering. For configured subscriptions, these counters get reset whenever the subscription's state is evaluated as "valid" (see (1) in Figure 8) ๐Ÿ”„

Dynamic subscriptions are removed from the operational state datastore once they expire (reaching "stop-time") or when they are terminated. A lot of subscription objects look configurable, but dynamic subscriptions only live in the operational state datastore, so they can't be configured ๐Ÿ™ƒ

2.9. Support for the "ietf-subscribed-notifications" YANG Module ๐Ÿ“š

Publishers that support this document MUST indicate support of the YANG module "ietf-subscribed-notifications" in the publisher's YANG library. On top of that, if supported, the optional features "encode-xml", "encode-json", "configured", "supports-vrf", "qos", "xpath", "subtree", "interface-designation", "dscp", and "replay" MUST be indicated ๐ŸŽ€

3. YANG Data Model Tree Diagrams ๐ŸŒณ

This section has the tree diagrams for nodes defined in Section 4. For the tree diagrams of subscription state change notifications, check Section 2.7. For the RPC tree diagrams, check Section 2.4 ๐Ÿ’œ

3.1. The "streams" Container ๐ŸŒŠ

A publisher keeps a list of available event streams as operational data. That list includes both standardized and vendor-specific event streams. This is how subscribers figure out which streams a publisher supports ๐Ÿ”

Here's the tree for the "streams" container. Everything in it is described in the YANG module in Section 4 ๐Ÿงœโ€โ™€๏ธ

  +--ro streams
     +--ro stream* [name]
        +--ro name                        string
        +--ro description                 string
        +--ro replay-support?             empty {replay}?
        +--ro replay-log-creation-time    yang:date-and-time
        |       {replay}?
        +--ro replay-log-aged-time?       yang:date-and-time
                {replay}?

Figure 18: "streams" Container Tree Diagram

3.2. The "filters" Container ๐Ÿงฉ

The "filters" container keeps a list of all subscription filters that live on beyond the lifecycle of a single subscription. That means predefined filters can be referenced by more than one subscription ๐Ÿซง

Here's the tree for the "filters" container. Everything in it is described in the YANG module in Section 4 ๐ŸŒท

  +--rw filters
     +--rw stream-filter* [name]
        +--rw name                           string
        +--rw (filter-spec)?
           +--:(stream-subtree-filter)
           |  +--rw stream-subtree-filter?   <anydata> {subtree}?
           +--:(stream-xpath-filter)
              +--rw stream-xpath-filter?     yang:xpath1.0 {xpath}?

Figure 19: "filters" Container Tree Diagram

3.3. The "subscriptions" Container ๐Ÿ“‹

okay so the "subscriptions" container is basically the master list ๐Ÿ’… of every subscription on a publisher. that includes the configured ones AND the dynamic ones ๐Ÿฆ‹. you can use it to pull up info about all the subscriptions a publisher is currently serving, so it's giving full overview energy โœจ

here's the tree diagram for it! every object in this tree gets described in the YANG module in Section 4 ๐Ÿงš

  +--rw subscriptions
     +--rw subscription* [id]
        +--rw id
        |       subscription-id
        +--rw (target)
        |  +--:(stream)
        |     +--rw (stream-filter)?
        |     |  +--:(by-reference)
        |     |  |  +--rw stream-filter-name
        |     |  |          stream-filter-ref
        |     |  +--:(within-subscription)
        |     |     +--rw (filter-spec)?
        |     |        +--:(stream-subtree-filter)
        |     |        |  +--rw stream-subtree-filter?   <anydata>
        |     |        |          {subtree}?
        |     |        +--:(stream-xpath-filter)
        |     |           +--rw stream-xpath-filter?
        |     |                   yang:xpath1.0 {xpath}?
        |     +--rw stream                               stream-ref
        |     +--ro replay-start-time?
        |     |       yang:date-and-time {replay}?
        |     +--rw configured-replay?                   empty
        |             {configured,replay}?
        +--rw stop-time?
        |       yang:date-and-time
        +--rw dscp?                                      inet:dscp
        |       {dscp}?
        +--rw weighting?                                 uint8 {qos}?
        +--rw dependency?
        |       subscription-id {qos}?
        +--rw transport?                                 transport
        |       {configured}?
        +--rw encoding?                                  encoding
        +--rw purpose?                                   string
        |       {configured}?
        +--rw (notification-message-origin)? {configured}?
        |  +--:(interface-originated)
        |  |  +--rw source-interface?
        |  |          if:interface-ref {interface-designation}?
        |  +--:(address-originated)
        |     +--rw source-vrf?
        |     |       -> /ni:network-instances/network-instance/name
        |     |       {supports-vrf}?
        |     +--rw source-address?
        |             inet:ip-address-no-zone
        +--ro configured-subscription-state?             enumeration
        |       {configured}?
        +--rw receivers
           +--rw receiver* [name]
              +--rw name                      string
              +--ro sent-event-records?
              |       yang:zero-based-counter64
              +--ro excluded-event-records?
              |       yang:zero-based-counter64
              +--ro state                     enumeration
              +---x reset {configured}?
                 +--ro output
                    +--ro time    yang:date-and-time

Figure 20: "subscriptions" Container Tree Diagram

4. Event Notification Subscription YANG Module โœจ

okay so this module imports typedefs from [RFC6991], [RFC8343], [RFC8341], [RFC8529], and [RFC8040] ๐Ÿซถ and it references [RFC6241], [XPATH] ("XML Path Language (XPath) Version 1.0"), [RFC7049], [RFC8259], [RFC7950], [RFC7951], and [RFC7540] ๐Ÿ’œ

<CODE BEGINS> file "ietf-subscribed-notifications@2019-09-09.yang"
module ietf-subscribed-notifications {
  yang-version 1.1;
  namespace "urn:ietf:params:xml:ns:yang:ietf-subscribed-notifications";
  prefix sn;

  import ietf-inet-types {
    prefix inet;
    reference
      "RFC 6991: Common YANG Data Types";
  }
  import ietf-interfaces {
    prefix if;
    reference
      "RFC 8343: A YANG Data Model for Interface Management";

  }
  import ietf-netconf-acm {
    prefix nacm;
    reference
      "RFC 8341: Network Configuration Access Control Model";
  }
  import ietf-network-instance {
    prefix ni;
    reference
      "RFC 8529: YANG Data Model for Network Instances";
  }
  import ietf-restconf {
    prefix rc;
    reference
      "RFC 8040: RESTCONF Protocol";
  }
  import ietf-yang-types {
    prefix yang;
    reference
      "RFC 6991: Common YANG Data Types";
  }

  organization
    "IETF NETCONF (Network Configuration) Working Group";
  contact
    "WG Web:  <https:/datatracker.ietf.org/wg/netconf/>
     WG List: <mailto:netconf@ietf.org>

     Author:  Alexander Clemm
              <mailto:ludwig@clemm.org>

     Author:  Eric Voit
              <mailto:evoit@cisco.com>

     Author:  Alberto Gonzalez Prieto
              <mailto:alberto.gonzalez@microsoft.com>

     Author:  Einar Nilsen-Nygaard
              <mailto:einarnn@cisco.com>

     Author:  Ambika Prasad Tripathy
              <mailto:ambtripa@cisco.com>";
  description
    "This module defines a YANG data model for subscribing to event
     records and receiving matching content in notification messages.

     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) 2019 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 Simplified 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 8639; see the
     RFC itself for full legal notices.";

  revision 2019-09-09 {
    description
      "Initial version.";
    reference
      "RFC 8639: A YANG Data Model for Subscriptions to
                 Event Notifications";
  }

  /*
   * FEATURES
   */

  feature configured {
    description
      "This feature indicates that configuration of subscriptions is
       supported.";
  }

  feature dscp {
    description
      "This feature indicates that a publisher supports the ability
       to set the Differentiated Services Code Point (DSCP) value in
       outgoing packets.";
  }

  feature encode-json {
    description
      "This feature indicates that JSON encoding of notification
       messages is supported.";
  }
  feature encode-xml {
    description
      "This feature indicates that XML encoding of notification
       messages is supported.";
  }

  feature interface-designation {
    description
      "This feature indicates that a publisher supports sourcing all
       receiver interactions for a configured subscription from a
       single designated egress interface.";
  }

  feature qos {
    description
      "This feature indicates that a publisher supports absolute
       dependencies of one subscription's traffic over another
       as well as weighted bandwidth sharing between subscriptions.
       Both of these are Quality of Service (QoS) features that allow
       differentiated treatment of notification messages between a
       publisher and a specific receiver.";
  }

  feature replay {
    description
      "This feature indicates that historical event record replay is
       supported.  With replay, it is possible for past event records
       to be streamed in chronological order.";
  }

  feature subtree {
    description
      "This feature indicates support for YANG subtree filtering.";
    reference
      "RFC 6241: Network Configuration Protocol (NETCONF),
                 Section 6";
  }

  feature supports-vrf {
    description
      "This feature indicates that a publisher supports VRF
       configuration for configured subscriptions.  VRF support for
       dynamic subscriptions does not require this feature.";
    reference
      "RFC 8529: YANG Data Model for Network Instances,
                 Section 6";
  }
  feature xpath {
    description
      "This feature indicates support for XPath filtering.";
    reference
      "XML Path Language (XPath) Version 1.0
       (https://www.w3.org/TR/1999/REC-xpath-19991116)";
  }

  /*
   * EXTENSIONS
   */

  extension subscription-state-notification {
    description
      "This statement applies only to notifications.  It indicates
       that the notification is a subscription state change
       notification.  Therefore, it does not participate in a regular
       event stream and does not need to be specifically subscribed
       to in order to be received.  This statement can only occur as
       a substatement of the YANG 'notification' statement.  This
       statement is not for use outside of this YANG module.";
  }

  /*
   * IDENTITIES
   */
  /* Identities for RPC and notification errors */

  identity delete-subscription-error {
    description
      "Base identity for the problem found while attempting to
       fulfill either a 'delete-subscription' RPC request or a
       'kill-subscription' RPC request.";
  }

  identity establish-subscription-error {
    description
      "Base identity for the problem found while attempting to
       fulfill an 'establish-subscription' RPC request.";
  }

  identity modify-subscription-error {
    description
      "Base identity for the problem found while attempting to
       fulfill a 'modify-subscription' RPC request.";
  }

  identity subscription-suspended-reason {

    description
      "Base identity for the problem condition communicated to a
       receiver as part of a 'subscription-suspended'
       notification.";
  }

  identity subscription-terminated-reason {
    description
      "Base identity for the problem condition communicated to a
       receiver as part of a 'subscription-terminated'
       notification.";
  }

  identity dscp-unavailable {
    base establish-subscription-error;
    if-feature "dscp";
    description
      "The publisher is unable to mark notification messages with
       prioritization information in a way that will be respected
       during network transit.";
  }

  identity encoding-unsupported {
    base establish-subscription-error;
    description
      "Unable to encode notification messages in the desired
       format.";
  }

  identity filter-unavailable {
    base subscription-terminated-reason;
    description
      "Referenced filter does not exist.  This means a receiver is
       referencing a filter that doesn't exist or to which it
       does not have access permissions.";
  }

  identity filter-unsupported {
    base establish-subscription-error;
    base modify-subscription-error;
    description
      "Cannot parse syntax in the filter.  This failure can be from
       a syntax error or a syntax too complex to be processed by the
       publisher.";
  }

  identity insufficient-resources {
    base establish-subscription-error;
    base modify-subscription-error;
    base subscription-suspended-reason;
    description
      "The publisher does not have sufficient resources to support
       the requested subscription.  An example might be that
       allocated CPU is too limited to generate the desired set of
       notification messages.";
  }

  identity no-such-subscription {
    base modify-subscription-error;
    base delete-subscription-error;
    base subscription-terminated-reason;
    description
      "Referenced subscription doesn't exist.  This may be as a
       result of a nonexistent subscription ID, an ID that belongs to
       another subscriber, or an ID for a configured subscription.";
  }

okay so we're still deep in the YANG module, and this bit is giving me everything ๐Ÿฆ‹โœจ first up are the extra failure reasons (replay can't happen, the stream isn't subscribable, a suspended sub timed out, the publisher ran out of bandwidth) ๐Ÿ’€, then the encoding and transport identities, then a few typedefs and groupings that tie it all together ๐ŸŽ€

  identity replay-unsupported {
    base establish-subscription-error;
    if-feature "replay";
    description
      "Replay cannot be performed for this subscription.  This means
       the publisher will not provide the requested historic
       information from the event stream via replay to this
       receiver.";
  }

  identity stream-unavailable {
    base subscription-terminated-reason;
    description
      "Not a subscribable event stream.  This means the referenced
       event stream is not available for subscription by the
       receiver.";
  }

  identity suspension-timeout {
    base subscription-terminated-reason;
    description
      "Termination of a previously suspended subscription.  The
       publisher has eliminated the subscription, as it exceeded a
       time limit for suspension.";
  }

  identity unsupportable-volume {
    base subscription-suspended-reason;
    description

      "The publisher does not have the network bandwidth needed to
       get the volume of generated information intended for a
       receiver.";
  }

  /* Identities for encodings */

  identity configurable-encoding {
    description
      "If a transport identity derives from this identity, it means
       that it supports configurable encodings.  An example of a
       configurable encoding might be a new identity such as
       'encode-cbor'.  Such an identity could use
       'configurable-encoding' as its base.  This would allow a
       dynamic subscription encoded in JSON (RFC 8259) to request
       that notification messages be encoded via the Concise Binary
       Object Representation (CBOR) (RFC 7049).  Further details for
       any specific configurable encoding would be explored in a
       transport document based on this specification.";
    reference
      "RFC 8259: The JavaScript Object Notation (JSON) Data
                 Interchange Format
       RFC 7049: Concise Binary Object Representation (CBOR)";
  }

  identity encoding {
    description
      "Base identity to represent data encodings.";
  }

  identity encode-xml {
    base encoding;
    if-feature "encode-xml";
    description
      "Encode data using XML as described in RFC 7950.";
    reference
      "RFC 7950: The YANG 1.1 Data Modeling Language";
  }

  identity encode-json {
    base encoding;
    if-feature "encode-json";
    description
      "Encode data using JSON as described in RFC 7951.";
    reference
      "RFC 7951: JSON Encoding of Data Modeled with YANG";
  }
  /* Identities for transports */

  identity transport {
    description
      "An identity that represents the underlying mechanism for
       passing notification messages.";
  }

  /*
   * TYPEDEFs
   */

  typedef encoding {
    type identityref {
      base encoding;
    }
    description
      "Specifies a data encoding, e.g., for a data subscription.";
  }

  typedef stream-filter-ref {
    type leafref {
      path "/sn:filters/sn:stream-filter/sn:name";
    }
    description
      "This type is used to reference an event stream filter.";
  }

  typedef stream-ref {
    type leafref {
      path "/sn:streams/sn:stream/sn:name";
    }
    description
      "This type is used to reference a system-provided
       event stream.";
  }

  typedef subscription-id {
    type uint32;
    description
      "A type for subscription identifiers.";
  }

  typedef transport {
    type identityref {
      base transport;
    }
    description

      "Specifies the transport used to send notification messages
       to a receiver.";
  }

  /*
   * GROUPINGS
   */

  grouping stream-filter-elements {
    description
      "This grouping defines the base for filters applied to event
       streams.";
    choice filter-spec {
      description
        "The content filter specification for this request.";
      anydata stream-subtree-filter {
        if-feature "subtree";
        description
          "Event stream evaluation criteria encoded in the syntax of
           a subtree filter as defined in RFC 6241, Section 6.

           The subtree filter is applied to the representation of
           individual, delineated event records as contained in the
           event stream.

           If the subtree filter returns a non-empty node set, the
           filter matches the event record, and the event record is
           included in the notification message sent to the
           receivers.";
        reference
          "RFC 6241: Network Configuration Protocol (NETCONF),
                     Section 6";
      }
      leaf stream-xpath-filter {
        if-feature "xpath";
        type yang:xpath1.0;
        description
          "Event stream evaluation criteria encoded in the syntax of
           an XPath 1.0 expression.

           The XPath expression is evaluated on the representation of
           individual, delineated event records as contained in
           the event stream.

           The result of the XPath expression is converted to a
           boolean value using the standard XPath 1.0 rules.  If the
           boolean value is 'true', the filter matches the event
           record, and the event record is included in the
           notification message sent to the receivers.

           The expression is evaluated in the following XPath
           context:

              o  The set of namespace declarations is the set of
                 prefix and namespace pairs for all YANG modules
                 implemented by the server, where the prefix is the
                 YANG module name and the namespace is as defined by
                 the 'namespace' statement in the YANG module.

                 If the leaf is encoded in XML, all namespace
                 declarations in scope on the 'stream-xpath-filter'
                 leaf element are added to the set of namespace
                 declarations.  If a prefix found in the XML is
                 already present in the set of namespace
                 declarations, the namespace in the XML is used.

              o  The set of variable bindings is empty.

              o  The function library is comprised of the core
                 function library and the XPath functions defined in
                 Section 10 in RFC 7950.

              o  The context node is the root node.";
        reference
          "XML Path Language (XPath) Version 1.0
           (https://www.w3.org/TR/1999/REC-xpath-19991116)
           RFC 7950: The YANG 1.1 Data Modeling Language,
                     Section 10";
      }
    }
  }

  grouping update-qos {
    description
      "This grouping describes QoS information concerning a
       subscription.  This information is passed to lower layers
       for transport prioritization and treatment.";
    leaf dscp {
      if-feature "dscp";
      type inet:dscp;
      default "0";
      description
        "The desired network transport priority level.  This is the
         priority set on notification messages encapsulating the
         results of the subscription.  This transport priority is
         shared for all receivers of a given subscription.";

    }
    leaf weighting {
      if-feature "qos";
      type uint8 {
        range "0 .. 255";
      }
      description
        "Relative weighting for a subscription.  Larger weights get
         more resources.  Allows an underlying transport layer to
         perform informed load-balance allocations between various
         subscriptions.";
      reference
        "RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2),
                   Section 5.3.2";
    }
    leaf dependency {
      if-feature "qos";
      type subscription-id;
      description
        "Provides the 'subscription-id' of a parent subscription.
         The parent subscription has absolute precedence should
         that parent have push updates ready to egress the publisher.
         In other words, there should be no streaming of objects from
         the current subscription if the parent has something ready
         to push.

         If a dependency is asserted via configuration or via an RPC
         but the referenced 'subscription-id' does not exist, the
         dependency is silently discarded.  If a referenced
         subscription is deleted, this dependency is removed.";
      reference
        "RFC 7540: Hypertext Transfer Protocol Version 2 (HTTP/2),
                   Section 5.3.1";
    }
  }

  grouping subscription-policy-modifiable {
    description
      "This grouping describes all objects that may be changed
       in a subscription.";
    choice target {
      mandatory true;
      description
        "Identifies the source of information against which a
         subscription is being applied as well as specifics on the
         subset of information desired from that source.";
      case stream {
        choice stream-filter {

          description
            "An event stream filter can be applied to a subscription.
             That filter will either come referenced from a global
             list or be provided in the subscription itself.";
          case by-reference {
            description
              "Apply a filter that has been configured separately.";
            leaf stream-filter-name {
              type stream-filter-ref;
              mandatory true;
              description
                "References an existing event stream filter that is
                 to be applied to an event stream for the
                 subscription.";
            }
          }
          case within-subscription {
            description
              "A local definition allows a filter to have the same
               lifecycle as the subscription.";
            uses stream-filter-elements;
          }
        }
      }
    }
    leaf stop-time {
      type yang:date-and-time;
      description
        "Identifies a time after which notification messages for a
         subscription should not be sent.  If 'stop-time' is not
         present, the notification messages will continue until the
         subscription is terminated.  If 'replay-start-time' exists,
         'stop-time' must be for a subsequent time.  If
         'replay-start-time' doesn't exist, 'stop-time', when
         established, must be for a future time.";
    }
  }

  grouping subscription-policy-dynamic {
    description
      "This grouping describes the only information concerning a
       subscription that can be passed over the RPCs defined in this
       data model.";
    uses subscription-policy-modifiable {
      augment "target/stream" {
        description
          "Adds additional objects that can be modified by an RPC.";
        leaf stream {

          type stream-ref {
            require-instance false;
          }
          mandatory true;
          description
            "Indicates the event stream to be considered for
             this subscription.";
        }
        leaf replay-start-time {
          if-feature "replay";
          type yang:date-and-time;
          config false;
          description
            "Used to trigger the 'replay' feature for a dynamic
             subscription, where event records that are selected
             need to be at or after the specified starting time.  If
             'replay-start-time' is not present, this is not a replay
             subscription and event record push should start
             immediately.  It is never valid to specify start times
             that are later than or equal to the current time.";
        }
      }
    }
    uses update-qos;
  }

okay the groupings at the bottom are where the bits come together ๐Ÿงš the filter stuff can be a reference or live right inside the subscription, the QoS knobs ride along, and the stop-time and replay-start-time stuff got its own vibe ๐Ÿ’œ

grouping subscription-policy { description "This grouping describes the full set of policy information concerning both dynamic and configured subscriptions, with the exclusion of both receivers and networking information specific to the publisher, such as what interface should be used to transmit notification messages."; uses subscription-policy-dynamic; leaf transport { if-feature "configured"; type transport; description "For a configured subscription, this leaf specifies the transport used to deliver messages destined for all receivers of that subscription."; } leaf encoding { when 'not(../transport) or derived-from(../transport, "sn:configurable-encoding")'; type encoding; description "The type of encoding for notification messages. For a

dynamic subscription, if not included as part of an 'establish-subscription' RPC, the encoding will be populated with the encoding used by that RPC. For a configured subscription, if not explicitly configured, the encoding will be the default encoding for an underlying transport."; } leaf purpose { if-feature "configured"; type string; description "Open text allowing a configuring entity to embed the originator or other specifics of this subscription."; } }

/*

  • RPCs */

rpc establish-subscription { description "This RPC allows a subscriber to create (and possibly negotiate) a subscription on its own behalf. If successful, the subscription remains in effect for the duration of the subscriber's association with the publisher or until the subscription is terminated. If an error occurs or the publisher cannot meet the terms of a subscription, an RPC error is returned, and the subscription is not created. In that case, the RPC reply's 'error-info' MAY include suggested parameter settings that would have a higher likelihood of succeeding in a subsequent 'establish-subscription' request."; input { uses subscription-policy-dynamic; leaf encoding { type encoding; description "The type of encoding for the subscribed data. If not included as part of the RPC, the encoding MUST be set by the publisher to be the encoding used by this RPC."; } } output { leaf id { type subscription-id; mandatory true; description "Identifier used for this subscription.";

} leaf replay-start-time-revision { if-feature "replay"; type yang:date-and-time; description "If a replay has been requested, this object represents the earliest time covered by the event buffer for the requested event stream. The value of this object is the 'replay-log-aged-time' if it exists. Otherwise, it is the 'replay-log-creation-time'. All buffered event records after this time will be replayed to a receiver. This object will only be sent if the starting time has been revised to be later than the time requested by the subscriber."; } } }

rc:yang-data establish-subscription-stream-error-info { container establish-subscription-stream-error-info { description "If any 'establish-subscription' RPC parameters are unsupportable against the event stream, a subscription is not created and the RPC error response MUST indicate the reason why the subscription failed to be created. This yang-data MAY be inserted as structured data in a subscription's RPC error response to indicate the reason for the failure. This yang-data MUST be inserted if hints are to be provided back to the subscriber."; leaf reason { type identityref { base establish-subscription-error; } description "Indicates the reason why the subscription has failed to be created to a targeted event stream."; } leaf filter-failure-hint { type string; description "Information describing where and/or why a provided filter was unsupportable for a subscription. The syntax and semantics of this hint are implementation specific."; } } } rpc modify-subscription { description "This RPC allows a subscriber to modify a dynamic subscription's parameters. If successful, the changed subscription parameters remain in effect for the duration of the subscription, until the subscription is again modified, or until the subscription is terminated. In the case of an error or an inability to meet the modified parameters, the subscription is not modified and the original subscription parameters remain in effect. In that case, the RPC error MAY include 'error-info' suggested parameter hints that would have a high likelihood of succeeding in a subsequent 'modify-subscription' request. A successful 'modify-subscription' will return a suspended subscription to the 'active' state."; input { leaf id { type subscription-id; mandatory true; description "Identifier to use for this subscription."; } uses subscription-policy-modifiable; } }

rc:yang-data modify-subscription-stream-error-info { container modify-subscription-stream-error-info { description "This yang-data MAY be provided as part of a subscription's RPC error response when there is a failure of a 'modify-subscription' RPC that has been made against an event stream. This yang-data MUST be used if hints are to be provided back to the subscriber."; leaf reason { type identityref { base modify-subscription-error; } description "Information in a 'modify-subscription' RPC error response that indicates the reason why the subscription to an event stream has failed to be modified."; } leaf filter-failure-hint { type string; description "Information describing where and/or why a provided filter was unsupportable for a subscription. The syntax and semantics of this hint are implementation specific."; } } }

rpc delete-subscription { description "This RPC allows a subscriber to delete a subscription that was previously created by that same subscriber using the 'establish-subscription' RPC.

If an error occurs, the server replies with an 'rpc-error' where the 'error-info' field MAY contain a 'delete-subscription-error-info' structure."; input { leaf id { type subscription-id; mandatory true; description "Identifier of the subscription that is to be deleted. Only subscriptions that were created using 'establish-subscription' from the same origin as this RPC can be deleted via this RPC."; } } }

rpc kill-subscription { nacm:default-deny-all; description "This RPC allows an operator to delete a dynamic subscription without restrictions on the originating subscriber or underlying transport session.

If an error occurs, the server replies with an 'rpc-error' where the 'error-info' field MAY contain a 'delete-subscription-error-info' structure."; input { leaf id { type subscription-id; mandatory true; description "Identifier of the subscription that is to be deleted. Only subscriptions that were created using 'establish-subscription' can be deleted via this RPC."; } }

}

rc:yang-data delete-subscription-error-info { container delete-subscription-error-info { description "If a 'delete-subscription' RPC or a 'kill-subscription' RPC fails, the subscription is not deleted and the RPC error response MUST indicate the reason for this failure. This yang-data MAY be inserted as structured data in a subscription's RPC error response to indicate the reason for the failure."; leaf reason { type identityref { base delete-subscription-error; } mandatory true; description "Indicates the reason why the subscription has failed to be deleted."; } } }

/*

  • NOTIFICATIONS */

notification replay-completed { sn:subscription-state-notification; if-feature "replay"; description "This notification is sent to indicate that all of the replay notifications have been sent."; leaf id { type subscription-id; mandatory true; description "This references the affected subscription."; } }

notification subscription-completed { sn:subscription-state-notification; if-feature "configured"; description "This notification is sent to indicate that a subscription has finished passing event records, as the 'stop-time' has been reached.";

leaf id { type subscription-id; mandatory true; description "This references the gracefully completed subscription."; } }

notification subscription-modified { sn:subscription-state-notification; description "This notification indicates that a subscription has been modified. Notification messages sent from this point on will conform to the modified terms of the subscription. For completeness, this subscription state change notification includes both modified and unmodified aspects of a subscription."; leaf id { type subscription-id; mandatory true; description "This references the affected subscription."; } uses subscription-policy { refine "target/stream/stream-filter/within-subscription" { description "Filter applied to the subscription. If the 'stream-filter-name' is populated, the filter in the subscription came from the 'filters' container. Otherwise, it is populated in-line as part of the subscription."; } } }

notification subscription-resumed { sn:subscription-state-notification; description "This notification indicates that a subscription that had previously been suspended has resumed. Notifications will once again be sent. In addition, a 'subscription-resumed' indicates that no modification of parameters has occurred since the last time event records have been sent."; leaf id { type subscription-id; mandatory true; description "This references the affected subscription.";

} }

notification subscription-started { sn:subscription-state-notification; if-feature "configured"; description "This notification indicates that a subscription has started and notifications will now be sent."; leaf id { type subscription-id; mandatory true; description "This references the affected subscription."; } uses subscription-policy { refine "target/stream/replay-start-time" { description "Indicates the time that a replay is using for the streaming of buffered event records. This will be populated with the most recent of the following: the event time of the previous event record sent to a receiver, the 'replay-log-creation-time', the 'replay-log-aged-time', or the most recent publisher boot time."; } refine "target/stream/stream-filter/within-subscription" { description "Filter applied to the subscription. If the 'stream-filter-name' is populated, the filter in the subscription came from the 'filters' container. Otherwise, it is populated in-line as part of the subscription."; } augment "target/stream" { description "This augmentation adds additional parameters specific to a 'subscription-started' notification."; leaf replay-previous-event-time { when '../replay-start-time'; if-feature "replay"; type yang:date-and-time; description "If there is at least one event in the replay buffer prior to 'replay-start-time', this gives the time of the event generated immediately prior to the 'replay-start-time'.

If a receiver previously received event records for this configured subscription, it can compare this time to the last event record previously received. If the two are not the same (perhaps due to a reboot), then a dynamic replay can be initiated to acquire any missing event records."; } } } }

notification subscription-suspended { sn:subscription-state-notification; description "This notification indicates that a suspension of the subscription by the publisher has occurred. No further notifications will be sent until the subscription resumes. This notification shall only be sent to receivers of a subscription; it does not constitute a general-purpose notification."; leaf id { type subscription-id; mandatory true; description "This references the affected subscription."; } leaf reason { type identityref { base subscription-suspended-reason; } mandatory true; description "Identifies the condition that resulted in the suspension."; } }

notification subscription-terminated { sn:subscription-state-notification; description "This notification indicates that a subscription has been terminated."; leaf id { type subscription-id; mandatory true; description "This references the affected subscription."; } leaf reason {

type identityref { base subscription-terminated-reason; } mandatory true; description "Identifies the condition that resulted in the termination."; } }

/*

  • DATA NODES */

container streams { config false; description "Contains information on the built-in event streams provided by the publisher."; list stream { key "name"; description "Identifies the built-in event streams that are supported by the publisher."; leaf name { type string; description "A handle for a system-provided event stream made up of a sequential set of event records, each of which is characterized by its own domain and semantics."; } leaf description { type string; description "A description of the event stream, including such information as the type of event records that are available in this event stream."; } leaf replay-support { if-feature "replay"; type empty; description "Indicates that event record replay is available on this event stream."; } leaf replay-log-creation-time { when '../replay-support'; if-feature "replay"; type yang:date-and-time; mandatory true; description "The timestamp of the creation of the log used to support the replay function on this event stream. This time might be earlier than the earliest available information contained in the log. This object is updated if the log resets for some reason."; } leaf replay-log-aged-time { when '../replay-support'; if-feature "replay"; type yang:date-and-time; description "The timestamp associated with the last event record that has been aged out of the log. This timestamp identifies how far back in history this replay log extends, if it doesn't extend back to the 'replay-log-creation-time'. This object MUST be present if replay is supported and any event records have been aged out of the log."; } } } container filters { description "Contains a list of configurable filters that can be applied to subscriptions. This facilitates the reuse of complex filters once defined."; list stream-filter { key "name"; description "A list of preconfigured filters that can be applied to subscriptions."; leaf name { type string; description "A name to differentiate between filters."; } uses stream-filter-elements; } } container subscriptions { description "Contains the list of currently active subscriptions, i.e., subscriptions that are currently in effect, used for subscription management and monitoring purposes. This includes subscriptions that have been set up via RPC primitives as well as subscriptions that have been established via configuration.";

list subscription { key "id"; description "The identity and specific parameters of a subscription. Subscriptions in this list can be created using a control channel or RPC or can be established through configuration.

         If the 'kill-subscription' RPC or configuration operations
         are used to delete a subscription, a
         'subscription-terminated' message is sent to any active or
         suspended receivers.";
      leaf id {
        type subscription-id;
        description
          "Identifier of a subscription; unique in a given
           publisher.";
      }
      uses subscription-policy {
        refine "target/stream/stream" {
          description
            "Indicates the event stream to be considered for this
             subscription.  If an event stream has been removed
             and can no longer be referenced by an active
             subscription, send a 'subscription-terminated'
             notification with 'stream-unavailable' as the reason.
             If a configured subscription refers to a nonexistent
             event stream, move that subscription to the
             'invalid' state.";
        }
        refine "transport" {
          description
            "For a configured subscription, this leaf specifies the
             transport used to deliver messages destined for all
             receivers of that subscription.  This object is
             mandatory for subscriptions in the configuration
             datastore.  This object (1) is not mandatory for dynamic
             subscriptions in the operational state datastore and
             (2) should not be present for other types of dynamic
             subscriptions.";
        }
        augment "target/stream" {
          description
            "Enables objects to be added to a configured stream
             subscription.";
          leaf configured-replay {
            if-feature "configured";
            if-feature "replay";
            type empty;
            description
              "The presence of this leaf indicates that replay for
               the configured subscription should start at the
               earliest time in the event log or at the publisher
               boot time, whichever is later.";
          }
        }
      }
      choice notification-message-origin {
        if-feature "configured";
        description
          "Identifies the egress interface on the publisher
           from which notification messages are to be sent.";
        case interface-originated {
          description
            "When notification messages are to egress a specific,
             designated interface on the publisher.";
          leaf source-interface {
            if-feature "interface-designation";
            type if:interface-ref;
            description
              "References the interface for notification messages.";
          }
        }
        case address-originated {
          description
            "When notification messages are to depart from a
             publisher using a specific originating address and/or
             routing context information.";
          leaf source-vrf {
            if-feature "supports-vrf";
            type leafref {
              path "/ni:network-instances/ni:network-instance/ni:name";
            }
            description
              "VRF from which notification messages should egress a
               publisher.";
          }
          leaf source-address {
            type inet:ip-address-no-zone;
            description
              "The source address for the notification messages.
               If a source VRF exists but this object doesn't, a
               publisher's default address for that VRF must
               be used.";
          }
        }
      }
      leaf configured-subscription-state {
        if-feature "configured";
        type enumeration {
          enum valid {
            value 1;
            description
              "The subscription is supportable with its current
               parameters.";
          }
          enum invalid {
            value 2;
            description
              "The subscription as a whole is unsupportable with its
               current parameters.";
          }
          enum concluded {
            value 3;
            description
              "A subscription is inactive, as it has hit a
               stop time.  It no longer has receivers in the
               'active' or 'suspended' state, but the subscription
               has not yet been removed from configuration.";
          }
        }
        config false;
        description
          "The presence of this leaf indicates that the subscription
           originated from configuration, not through a control
           channel or RPC.  The value indicates the state of the
           subscription as established by the publisher.";
      }
      container receivers {
        description
          "Set of receivers in a subscription.";
        list receiver {
          key "name";
          min-elements 1;
          description
            "A host intended as a recipient for the notification
             messages of a subscription.  For configured
             subscriptions, transport-specific network parameters
             (or a leafref to those parameters) may be augmented to a
             specific receiver in this list.";
          leaf name {
            type string;
            description
              "Identifies a unique receiver for a subscription.";
          }
          leaf sent-event-records {
            type yang:zero-based-counter64;
            config false;
            description
              "The number of event records sent to the receiver.  The
               count is initialized when a dynamic subscription is
               established or when a configured receiver
               transitions to the 'valid' state.";
          }
          leaf excluded-event-records {
            type yang:zero-based-counter64;
            config false;
            description
              "The number of event records explicitly removed via
               either an event stream filter or an access control
               filter so that they are not passed to a receiver.
               This count is set to zero each time
               'sent-event-records' is initialized.";
          }
          leaf state {
            type enumeration {
              enum active {
                value 1;
                description
                  "The receiver is currently being sent any
                   applicable notification messages for the
                   subscription.";
              }
              enum suspended {
                value 2;
                description
                  "The receiver state is 'suspended', so the
                   publisher is currently unable to provide
                   notification messages for the subscription.";
              }
              enum connecting {
                value 3;
                if-feature "configured";
                description
                  "A subscription has been configured, but a
                   'subscription-started' subscription state change
                   notification needs to be successfully received
                   before notification messages are sent.

                   If the 'reset' action is invoked for a receiver of
                   an active configured subscription, the state
                   must be moved to 'connecting'.";
              }
              enum disconnected {
                value 4;
                if-feature "configured";
                description
                  "A subscription has failed to send a
                   'subscription-started' state change to the
                   receiver.  Additional connection attempts are not
                   currently being made.";
              }
            }
            config false;
            mandatory true;
            description
              "Specifies the state of a subscription from the
               perspective of a particular receiver.  With this
               information, it is possible to determine whether a
               publisher is currently generating notification
               messages intended for that receiver.";
          }
          action reset {
            if-feature "configured";
            description
              "Allows the reset of this configured subscription's
               receiver to the 'connecting' state.  This enables the
               connection process to be reinitiated.";
            output {
              leaf time {
                type yang:date-and-time;
                mandatory true;
                description
                  "Time at which a publisher returned the receiver to
                   the 'connecting' state.";
              }
            }
          }
        }
      }
    }
  }
}
<CODE ENDS>

5. IANA Considerations ๐Ÿงš

okay so IANA (the folks who keep the official registries ๐Ÿ“‹) registered one URI in the "ns" subregistry of the "IETF XML Registry" [RFC3688], which lives at <https://www.iana.org/assignments/xml-registry>. The registration followed the format in [RFC3688], and it looks like this:

URI: urn:ietf:params:xml:ns:yang:ietf-subscribed-notifications
Registrant Contact: The NETCONF WG of the IETF.
XML: N/A; the requested URI is an XML namespace.

IANA also registered one YANG module in the "YANG Module Names" registry [RFC6020], maintained at <https://www.iana.org/assignments/yang-parameters>. That registration followed the format in [RFC6020] too, and it's giving very organized energy ๐Ÿ’…:

Name: ietf-subscribed-notifications
Namespace: urn:ietf:params:xml:ns:yang:ietf-subscribed-notifications
Prefix: sn
Reference: RFC 8639

6. Implementation Considerations ๐Ÿ’–

okay bestie, this part is all about the stuff that makes deployments run smoothly โœจ If you've got both configured and dynamic subscriptions in the same box, it's recommended that you split the subscription id domain into a static half and a dynamic half ๐ŸŽ€. That way, a configured subscription trying to set a subscription-id can never collide with one that was already dynamically allocated. The best practice is to use the lower half of the id integer space whenever an external entity assigns that id (like with a configured subscription). That leaves the upper half free for the publisher to hand out dynamically ๐Ÿฆ‹.

If a subscription can't turn a series of filtered event records into transmittable notification messages, the receiver should be suspended with the reason "unsupportable-volume" ๐Ÿ˜ญ.

For configured subscriptions, operations are done against the whole set of receivers, using the subscription id as a handle for that set ๐Ÿงš. But streaming updates are different: subscription state change notifications are local to each receiver ๐ŸŒธ. In this spec, receivers don't get any info from the publisher about the other receivers existing at all. Still, if a network operator wants the receivers to be able to correlate their results, it's useful to reuse the subscription id across all of them ๐Ÿ”ฅ. Heads up though: because access control permissions can differ per receiver, each receiver might actually end up with a different set of event records ๐Ÿ’€.

For configured replay subscriptions, the receiver is protected from duplicated events being pushed after a publisher reboots ๐Ÿซถ. However, a receiver might still want event records that failed to be delivered right before the reboot. You can get those by using the <eventTime> [RFC5277] from the last event record received before the "subscription-started" subscription state change notification arrived ๐Ÿ‘‘. With that <eventTime> and the replay-start-time from the "subscription-started" notification, you can set up an independent dynamic subscription that pulls any event records that were generated but never sent to the receiver ๐ŸŒˆ. Main character energy, literally ๐Ÿ’œ.

7. Transport Requirements โœจ

okay bestie, this section is the transport checklist ๐Ÿ“‹ It lays out what any transport built on this solution needs to be able to do ๐Ÿ’…

The transport the subscriber picks to reach the publisher MUST be able to support multiple "establish-subscription" requests made in the same transport session ๐Ÿฆ‹

For both configured and dynamic subscriptions, the publisher MUST authenticate a receiver via some transport-level mechanism before sending any event records that the receiver is authorized to see. And the receiver MUST authenticate the publisher at the transport level too ๐Ÿซถ So the result is mutual authentication, both sides checking each other, no cap ๐Ÿ”

A secure transport is highly recommended ๐Ÿ’– Beyond that, the publisher MUST ensure that the receiver has sufficient authorization to perform the function it is requesting against the specific subset of content involved. Access check, periodt ๐Ÿ‘‘

A specification for a transport built on this document may or may not choose to require the same logical channel for the RPCs and the event records. That part is up to them ๐ŸŒธ But the event records and the subscription state change notifications MUST be sent on the same transport session so they arrive in properly ordered delivery. Order matters, bestie ๐Ÿ˜ญ

A specification for a transport MUST identify any encodings that are supported. If a configured subscription's transport allows different encodings, the specification MUST identify the default encoding ๐ŸŽ€

A subscriber that includes a dscp leaf in an establish-subscription request will need to understand and consider what the corresponding DSCP value represents in the domain of the publisher ๐Ÿงš Same number, different meaning, iykyk.

Additional transport requirements will be dictated by the choice of transport used with a subscription. For an example of such requirements, see [RFC8640] โœจ


8. Security Considerations ๐Ÿ”ฅ

This part is serious, so I'm putting on my detective hat ๐Ÿ•ต๏ธโ€โ™€๏ธ

The YANG module in this document defines a schema for data that is designed to be accessed via network management protocols such as NETCONF [RFC6241] or RESTCONF [RFC8040]. The lowest NETCONF layer is the secure transport layer, and the mandatory-to-implement secure transport is Secure Shell (SSH) [RFC6242]. The lowest RESTCONF layer is HTTPS, and the mandatory-to-implement secure transport is TLS [RFC5246] ๐Ÿ”

The Network Configuration Access Control Model (NACM) [RFC8341] gives you the tools to restrict access for particular NETCONF or RESTCONF users to a preconfigured subset of all available NETCONF or RESTCONF protocol operations and content ๐Ÿ’…

With configured subscriptions, one or more publishers could be used to overwhelm a receiver ๐Ÿ˜ญ To counter this, notification messages SHOULD NOT be sent to any receiver that does not support this specification. Receivers that do not want notification messages only need to terminate or refuse any transport sessions from the publisher ๐Ÿ™ˆ

When a receiver of a configured subscription gets a new "subscription-started" message for a known subscription where it is already consuming events, it may indicate that an attacker has momentarily disrupted receiver connectivity ๐Ÿ‘€ To grab the events lost during that gap, the receiver SHOULD retrieve any event records generated since the last event record was received. One way to do that is a separate dynamic replay subscription with the same filtering criteria, as long as the publisher supports the "replay" feature ๐Ÿฆ‹

For dynamic subscriptions, implementations need to protect against malicious or buggy subscribers that may send a large number of "establish-subscription" requests and use up system resources. Operators SHOULD monitor for this, and if they spot it, take remedial action to limit the resources used, like suspending or terminating a subset of the subscriptions or, if the underlying transport is session based, terminating the underlying transport session ๐Ÿšจ

The replay mechanisms described in Sections 2.4.2.1 and 2.5.6 provide access to historical event records. By design, the access control model that protects these records could let subscribers view data they weren't authorized to see at the time of collection. Yikes ๐Ÿ’€

Using DNS names for configured subscription's receiver "name" lookups can cause situations where the name resolves differently than expected on the publisher, so the recipient would be different than expected ๐Ÿงญ

An attacker that can cause the publisher to use an incorrect time can induce message replay by setting the time in the past, and can introduce a risk of message loss by setting the time in the future โฐ

There are a number of data nodes defined in this YANG module that are writable, creatable, and deletable (that's config true, which is the default) ๐Ÿ“ These data nodes may be considered sensitive or vulnerable in some network environments. Write operations (e.g., edit-config) to these data nodes without proper protection can have a negative effect on network operations. Here are the subtrees and data nodes and their sensitivity/vulnerability:

Container: "/filters"

  • "stream-subtree-filter": Updating a filter could increase the computational complexity of all referencing subscriptions.
  • "stream-xpath-filter": Updating a filter could increase the computational complexity of all referencing subscriptions.

Container: "/subscriptions"

The following considerations are only relevant for configuration operations made upon configured subscriptions:

  • "configured-replay": Can be used to send a large number of event records to a receiver.
  • "dependency": Can be used to force important traffic to be queued behind updates that are not as important.
  • "dscp": If unvalidated, can result in the sending of traffic with a higher-priority marking than warranted.
  • "id": Can overwrite an existing subscription, perhaps one configured by another entity.
  • "name": Adding a new key entry can be used to attempt to send traffic to an unwilling receiver.
  • "replay-start-time": Can be used to push very large logs, wasting resources.
  • "source-address": The configured address might not be able to reach a desired receiver.
  • "source-interface": The configured interface might not be able to reach a desired receiver.
  • "source-vrf": Can place a subscription in a virtual network where receivers are not entitled to view the subscribed content.
  • "stop-time": Could be used to terminate content at an inopportune time.
  • "stream": Could set a subscription to an event stream that does not contain content permitted for the targeted receivers.
  • "stream-filter-name": Could be set to a filter that is not relevant to the event stream.
  • "stream-subtree-filter": A complex filter can increase the computational resources for this subscription.
  • "stream-xpath-filter": A complex filter can increase the computational resources for this subscription.
  • "weighting": Allocating a large weight can overwhelm the dequeuing of other subscriptions.

Some of the readable data nodes in this YANG module may be considered sensitive or vulnerable in some network environments ๐Ÿ‘€ It's thus important to control read access (e.g., via get, get-config, or notification) to these data nodes. Here are the subtrees and data nodes and their sensitivity/vulnerability:

Container: "/streams"

  • "name": If access control is not properly configured, can expose system internals to those who should not have access to this information.
  • "replay-support": If access control is not properly configured, can expose logs to those who should not have access.

Container: "/subscriptions"

  • "excluded-event-records": This leaf can provide information about filtered event records. A network operator should have the proper permissions to know about such filtering. However, exposing the count of excluded events to a receiver could leak information about the presence of access control filters that might be in place for that receiver ๐Ÿคซ
  • "subscription": Different operational teams might have a desire to set varying subsets of subscriptions. Access control should be designed to permit read access to just the allowed set.

Some of the RPC operations in this YANG module may be considered sensitive or vulnerable in some network environments, so it's important to control access to these operations. Here are the operations and their sensitivity/vulnerability:

RPC: all

  • If a malicious or buggy subscriber sends an unexpectedly large number of RPCs, the result might be excessive use of system resources on the publisher just to determine that these subscriptions should be declined. In such a situation, subscription interactions MAY be terminated by terminating the transport session ๐Ÿšซ

RPC: "delete-subscription"

  • No special considerations โœจ

RPC: "establish-subscription"

  • Subscriptions could overload a publisher's resources. For this reason, publishers MUST ensure that they have sufficient resources to fulfill this request; otherwise, they MUST reject the request ๐Ÿ’…

RPC: "kill-subscription"

  • The "kill-subscription" RPC MUST be secured so that only connections with administrative rights are able to invoke this RPC ๐Ÿ‘‘

RPC: "modify-subscription"

  • Subscriptions could overload a publisher's resources. For this reason, publishers MUST ensure that they have sufficient resources to fulfill this request; otherwise, they MUST reject the request ๐Ÿ™

9. References ๐Ÿ“š

9.1. Normative References ๐Ÿ“š

these are the must-reads, the specs this one leans on the hardest ๐Ÿ’…

[RFC2119]  Bradner, S., "Key words for use in RFCs to Indicate
           Requirement Levels", BCP 14, RFC 2119,
           DOI 10.17487/RFC2119, March 1997,
           <https://www.rfc-editor.org/info/rfc2119>.

[RFC2474]  Nichols, K., Blake, S., Baker, F., and D. Black,
           "Definition of the Differentiated Services Field (DS
           Field) in the IPv4 and IPv6 Headers", RFC 2474,
           DOI 10.17487/RFC2474, December 1998,
           <https://www.rfc-editor.org/info/rfc2474>.

[RFC3688]  Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688,
           DOI 10.17487/RFC3688, January 2004,
           <https://www.rfc-editor.org/info/rfc3688>.

[RFC5246]  Dierks, T. and E. Rescorla, "The Transport Layer Security
           (TLS) Protocol Version 1.2", RFC 5246,
           DOI 10.17487/RFC5246, August 2008,
           <https://www.rfc-editor.org/info/rfc5246>.

[RFC5277]  Chisholm, S. and H. Trevino, "NETCONF Event
           Notifications", RFC 5277, DOI 10.17487/RFC5277, July 2008,
           <https://www.rfc-editor.org/info/rfc5277>.

[RFC6020]  Bjorklund, M., Ed., "YANG - A Data Modeling Language for
           the Network Configuration Protocol (NETCONF)", RFC 6020,
           DOI 10.17487/RFC6020, October 2010,
           <https://www.rfc-editor.org/info/rfc6020>.

[RFC6241]  Enns, R., Ed., Bjorklund, M., Ed., Schoenwaelder, J., Ed.,
           and A. Bierman, Ed., "Network Configuration Protocol
           (NETCONF)", RFC 6241, DOI 10.17487/RFC6241, June 2011,
           <https://www.rfc-editor.org/info/rfc6241>.

[RFC6242]  Wasserman, M., "Using the NETCONF Protocol over Secure
           Shell (SSH)", RFC 6242, DOI 10.17487/RFC6242, June 2011,
           <https://www.rfc-editor.org/info/rfc6242>.

[RFC6991]  Schoenwaelder, J., Ed., "Common YANG Data Types",
           RFC 6991, DOI 10.17487/RFC6991, July 2013,
           <https://www.rfc-editor.org/info/rfc6991>.

[RFC7950]  Bjorklund, M., Ed., "The YANG 1.1 Data Modeling Language",
           RFC 7950, DOI 10.17487/RFC7950, August 2016,
           <https://www.rfc-editor.org/info/rfc7950>.

[RFC7951]  Lhotka, L., "JSON Encoding of Data Modeled with YANG",
           RFC 7951, DOI 10.17487/RFC7951, August 2016,
           <https://www.rfc-editor.org/info/rfc7951>.

[RFC8040]  Bierman, A., Bjorklund, M., and K. Watsen, "RESTCONF
           Protocol", RFC 8040, DOI 10.17487/RFC8040, January 2017,
           <https://www.rfc-editor.org/info/rfc8040>.

[RFC8174]  Leiba, B., "Ambiguity of Uppercase vs Lowercase in
           RFC 2119 Key Words", BCP 14, RFC 8174,
           DOI 10.17487/RFC8174, May 2017,
           <https://www.rfc-editor.org/info/rfc8174>.

[RFC8341]  Bierman, A. and M. Bjorklund, "Network Configuration
           Access Control Model", STD 91, RFC 8341,
           DOI 10.17487/RFC8341, March 2018,
           <https://www.rfc-editor.org/info/rfc8341>.

[RFC8342]  Bjorklund, M., Schoenwaelder, J., Shafer, P., Watsen, K.,
           and R. Wilton, "Network Management Datastore Architecture
           (NMDA)", RFC 8342, DOI 10.17487/RFC8342, March 2018,
           <https://www.rfc-editor.org/info/rfc8342>.

[RFC8343]  Bjorklund, M., "A YANG Data Model for Interface
           Management", RFC 8343, DOI 10.17487/RFC8343, March 2018,
           <https://www.rfc-editor.org/info/rfc8343>.

[RFC8529]  Berger, L., Hopps, C., Lindem, A., Bogdanovic, D., and X.
           Liu, "YANG Data Model for Network Instances", RFC 8529,
           DOI 10.17487/RFC8529, March 2019,
           <https://www.rfc-editor.org/info/rfc8529>.

[W3C.REC-xml-20081126]
           Bray, T., Paoli, J., Sperberg-McQueen, M., Maler, E., and
           F. Yergeau, "Extensible Markup Language (XML) 1.0 (Fifth
           Edition)", World Wide Web Consortium Recommendation
           REC-xml-20081126, November 2008,
           <https://www.w3.org/TR/2008/REC-xml-20081126>.

[XPATH]    Clark, J. and S. DeRose, "XML Path Language (XPath)
           Version 1.0", November 1999,
           <https://www.w3.org/TR/1999/REC-xpath-19991116>.

9.2. Informative References ๐Ÿงš

these ones are the extra-credit reading, good to know but not the core stuff โœจ

[RESTCONF-Notif]
           Voit, E., Rahman, R., Nilsen-Nygaard, E., Clemm, A.,
           and A. Bierman, "Dynamic subscription to YANG Events
           and Datastores over RESTCONF", Work in Progress,
           draft-ietf-netconf-restconf-notif-15, June 2019.

[RFC7049]  Bormann, C. and P. Hoffman, "Concise Binary Object
           Representation (CBOR)", RFC 7049, DOI 10.17487/RFC7049,
           October 2013, <https://www.rfc-editor.org/info/rfc7049>.

[RFC7540]  Belshe, M., Peon, R., and M. Thomson, Ed., "Hypertext
           Transfer Protocol Version 2 (HTTP/2)", RFC 7540,
           DOI 10.17487/RFC7540, May 2015,
           <https://www.rfc-editor.org/info/rfc7540>.

[RFC7923]  Voit, E., Clemm, A., and A. Gonzalez Prieto, "Requirements
           for Subscription to YANG Datastores", RFC 7923,
           DOI 10.17487/RFC7923, June 2016,
           <https://www.rfc-editor.org/info/rfc7923>.

[RFC8071]  Watsen, K., "NETCONF Call Home and RESTCONF Call Home",
           RFC 8071, DOI 10.17487/RFC8071, February 2017,
           <https://www.rfc-editor.org/info/rfc8071>.

[RFC8259]  Bray, T., Ed., "The JavaScript Object Notation (JSON) Data
           Interchange Format", STD 90, RFC 8259,
           DOI 10.17487/RFC8259, December 2017,
           <https://www.rfc-editor.org/info/rfc8259>.

[RFC8340]  Bjorklund, M. and L. Berger, Ed., "YANG Tree Diagrams",
           BCP 215, RFC 8340, DOI 10.17487/RFC8340, March 2018,
           <https://www.rfc-editor.org/info/rfc8340>.

[RFC8640]  Voit, E., Clemm, A., Gonzalez Prieto, A., Nilsen-Nygaard,
           E., and A. Tripathy, "Dynamic Subscription to YANG Events
           and Datastores over NETCONF", RFC 8640,
           DOI 10.17487/RFC8640, September 2019,
           <https://www.rfc-editor.org/info/rfc8640>.

[RFC8641]  Clemm, A. and E. Voit, "Subscription to YANG Notifications
           for Datastore Updates", RFC 8641, DOI 10.17487/RFC8641,
           September 2019, <https://www.rfc-editor.org/info/rfc8641>.

Appendix A. Example Configured Transport Augmentation โœจ

okay this appendix is a non-normative example, so it's more of a vibe check than a rule ๐Ÿฆ‹ it shows how the YANG module from Section 4 could be glowed up with the config parameters needed for the transport connection process. it's not a complete transport model, bestie ๐Ÿ™ˆ the star here is an imaginary transport type called "foo". if you want the big-picture goals behind configuring transport connectivity for a configured subscription, check out Section 2.5.7.

the example module in this appendix has two main pieces ๐Ÿ’œ. first, there's a transport identity called "foo", which lets a configuration agent pick "foo" as the transport for a subscription. second, there's a YANG case augmentation also called "foo", plugged into the "/subscriptions/subscription/receivers/receiver" node from Section 4. inside it live the transport parameters "address" and "port", which are needed to actually connect to the receiver ๐Ÿ”ฅ

module example-foo-subscribed-notifications {
  yang-version 1.1;
  namespace
    "urn:example:foo-subscribed-notifications";

  prefix fsn;

  import ietf-subscribed-notifications {
    prefix sn;
  }
  import ietf-inet-types {
    prefix inet;
  }

  description
    "Defines 'foo' as a supported type of configured transport for
     subscribed event notifications.";

  identity foo {
    base sn:transport;
    description
      "Transport type 'foo' is available for use as a configured
       subscription's transport protocol for subscribed
       notifications.";
  }
  augment
    "/sn:subscriptions/sn:subscription/sn:receivers/sn:receiver" {
    when 'derived-from(../../../transport, "fsn:foo")';
    description
      "This augmentation makes transport parameters specific to 'foo'
       available for a receiver.";
    leaf address {
      type inet:host;
      mandatory true;
      description
        "Specifies the address to use for messages destined for a
         receiver.";
    }
    leaf port {
      type inet:port-number;
      mandatory true;
      description
        "Specifies the port number to use for messages destined for a
         receiver.";
    }
  }
}
              Figure 21: Example Transport Augmentation
                  for the Fictitious Protocol "foo"

this "foo" module is never gonna show up in a real-world deployment ๐Ÿ’€ but if you're deploying an actual transport technology for real, you'll need a similar YANG module. it must be defined ๐Ÿซถ

Acknowledgments ๐Ÿ’–

huge thank yous to everyone who dropped valuable comments, discussions, and feedback ๐ŸŒธ this includes Andy Bierman, Tim Jenkins, Martin Bjorklund, Kent Watsen, Balazs Lengyel, Robert Wilton, Sharon Chisholm, Hector Trevino, Susan Hares, Michael Scharf, and Guangying Zheng. literal legends ๐Ÿ‘‘

Authors' Addresses ๐Ÿฆ‹

Eric Voit
Cisco Systems

Email: evoit@cisco.com


Alexander Clemm
Futurewei

Email: ludwig@clemm.org


Alberto Gonzalez Prieto
Microsoft

Email: alberto.gonzalez@microsoft.com


Einar Nilsen-Nygaard
Cisco Systems

Email: einarnn@cisco.com


Ambika Prasad Tripathy
Cisco Systems

Email: ambtripa@cisco.com
๐Ÿ“ฆ where this page comes from