11. Mapping the Hybrid Schema to DSDL
As explained in Section 6, the second step of the YANG-to-DSDL mapping takes the hybrid schema and transforms it to various DSDL schemas capable of validating instance XML documents. As an input parameter, this step takes, in the simplest case, just a specification of the NETCONF XML document type that is to be validated. These document types can be, for example, the contents of a datastore, a reply to <nc:get> or <nc:get-config>, contents of other RPC requests/replies and event notifications, and so on. The second mapping step has to accomplish the following three general tasks: 1. Extract the parts of the hybrid schema that are appropriate for the requested document type. For example, if a <nc:get> reply is to be validated, the subtree under <nma:data> has to be selected. 2. The schema must be adapted to the specific encapsulating XML elements mandated by the RPC layer. These are, for example, <nc: rpc> and <nc:data> elements in the case of a <nc:get> reply or <en:notification> for a notification. 3. Finally, NETMOD-specific annotations that are relevant for the schema language of the generated schema must be mapped to the corresponding patterns or rules. These three tasks are together much simpler than the first mapping step and can be effectively implemented using XSL transformations [XSLT]. The following subsections describe the details of the second mapping step for the individual DSDL schema languages. Section 12 then contains a detailed specification for the mapping of all NETMOD- specific annotations.11.1. Generating RELAX NG Schemas for Various Document Types
With one minor exception, obtaining a validating RELAX NG schema from the hybrid schema only means taking appropriate parts of the hybrid schema and assembling them in a new RELAX NG grammar, perhaps after removing all unwanted annotations.
The structure of the resulting RELAX NG schema is similar to that of the hybrid schema: the root grammar contains embedded grammars, one for each input YANG module. However, as explained in Section 8.2, global named pattern definitions (children of the root <rng:grammar> element) MUST be moved to a separate schema file. Depending on the XML document type that is the target for validation, such as <nc:get> or <nc:get-config> reply, RPC operations or notifications, patterns defining corresponding top-level information items MUST be added, such as <nc:rpc-reply> with the @message-id attribute and so on. In order to avoid copying common named pattern definitions for common NETCONF elements and attributes to every single output RELAX NG file, such schema-independent definitions SHOULD be collected in a library file that is then included by the validating RELAX NG schemas. Appendix B has the listing of such a library file. The minor exception mentioned above is the annotation @nma:config, which must be observed if the target document type is a reply to <nc: get-config>. In this case, each element definition that has this attribute with the value of "false" MUST be removed from the schema together with its descendants. See Section 12.1 for more details.11.2. Mapping Semantic Constraints to Schematron
Schematron schemas tend to be much flatter and more uniform compared to RELAX NG. They have exactly four levels of XML hierarchy: <sch: schema>, <sch:pattern>, <sch:rule>, and <sch:assert> or <sch:report>. In a Schematron schema generated by the second mapping step, the basic unit of organization is a rule represented by the <sch:rule> element. The following NETMOD-specific annotations from the hybrid schema (henceforth called "semantic annotations") are mapped to corresponding Schematron rules: <nma:must>, @nma:key, @nma:unique, @nma:max-elements, @nma:min-elements, @nma:when, @nma:leafref, @nma: leaf-list, and also @nma:mandatory appearing as an attribute of <rng: choice> (see Section 11.2.1). Each input YANG module is mapped to a Schematron pattern whose @id attribute is set to the module name. Every <rng:element> pattern containing at least one of the above-mentioned semantic annotations is then mapped to a Schematron rule: <sch:rule context="XELEM"> ... </sch:rule>
The value of the mandatory @context attribute of <sch:rule> (denoted as XELEM) MUST be set to the absolute path of the context element in the data tree. The <sch:rule> element contains the mappings of all contained semantic annotations in the form of Schematron asserts or reports. Semantic annotations appearing inside a named pattern definition (i.e., having <rng:define> among its ancestors) require special treatment because they may be potentially used in different contexts. This is accomplished by using Schematron abstract patterns that use the "$pref" variable in place of the local namespace prefix. The value of the @id attribute of such an abstract pattern MUST be set to the name of the named pattern definition that is being mapped (i.e., the mangled name of the original YANG grouping). When the abstract pattern is instantiated, the values of the following two parameters MUST be provided: o pref: the actual namespace prefix, o start: XPath expression defining the context in which the grouping is used. EXAMPLE. Consider the following YANG module: module example4 { namespace "http://example.com/ns/example4"; prefix ex4; uses sorted-leaf-list; grouping sorted-leaf-list { leaf-list sorted-entry { must "not(preceding-sibling::sorted-entry > .)" { error-message "Entries must appear in ascending order."; } type uint8; } } }
The resulting Schematron schema for a reply to <nc:get> is then as follows: <?xml version="1.0" encoding="utf-8"?> <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> <sch:ns uri="http://example.com/ns/example4" prefix="ex4"/> <sch:ns uri="urn:ietf:params:xml:ns:netconf:base:1.0" prefix="nc"/> <sch:pattern abstract="true" id="_example4__sorted-leaf-list"> <sch:rule context="$start/$pref:sorted-entry"> <sch:report test=". = preceding-sibling::$pref:sorted-entry"> Duplicate leaf-list entry "<sch:value-of select="."/>". </sch:report> <sch:assert test="not(preceding-sibling::$pref:sorted-entry > .)"> Entries must appear in ascending order. </sch:assert> </sch:rule> </sch:pattern> <sch:pattern id="example4"/> <sch:pattern id="id2573371" is-a="_example4__sorted-leaf-list"> <sch:param name="start" value="/nc:rpc-reply/nc:data"/> <sch:param name="pref" value="ex4"/> </sch:pattern> </sch:schema> The "sorted-leaf-list" grouping from the input module is mapped to an abstract pattern with an @id value of "_example4__sorted-leaf-list" in which the 'must' statement corresponds to the <sch:assert> element. The abstract pattern is the instantiated by the pattern with an @id value of "id2573371", which sets the "start" and "pref" parameters to appropriate values. Note that another Schematron element, <sch:report>, was automatically added, checking for duplicate leaf-list entries. The mapping from the hybrid schema to Schematron proceeds in the following steps: 1. First, the active subtree(s) of the hybrid schema must be selected depending on the requested target document type. This procedure is identical to the RELAX NG case, including the handling of @nma:config if the target document type is <nc:get- config> reply.
2. Namespaces of all input YANG modules, together with the namespaces of base NETCONF ("nc" prefix) or notifications ("en" prefix) MUST be declared using the <sch:ns> element, for example: <sch:ns uri="http://example.com/ns/example4" prefix="ex4"/> 3. One pattern is created for every input module. In addition, an abstract pattern is created for every named pattern definition containing one or more semantic annotations. 4. A <sch:rule> element is created for each element pattern containing semantic annotations. 5. Every such annotation is then mapped to an <sch:assert> or <sch: report> element, which is installed as a child of the <sch:rule> element.11.2.1. Constraints on Mandatory Choice
In order to fully represent the semantics of YANG's 'choice' statement with the "mandatory true;" substatement, the RELAX NG grammar has to be combined with a special Schematron rule. EXAMPLE. Consider the following module: module example5 { namespace "http://example.com/ns/example5"; prefix ex5; choice foobar { mandatory true; case foo { leaf foo1 { type uint8; } leaf foo2 { type uint8; } } leaf bar { type uint8; } } }
In this module, all three leaf nodes in both case branches are optional but because of the "mandatory true;" statement, at least one of them must be present in a valid configuration. The 'choice' statement from this module is mapped to the following fragment of the RELAX NG schema: <rng:choice> <rng:interleave> <rng:optional> <rng:element name="ex5:foo1"> <rng:data type="unsignedByte"/> </rng:element> </rng:optional> <rng:optional> <rng:element name="ex5:foo2"> <rng:data type="unsignedByte"/> </rng:element> </rng:optional> </rng:interleave> <rng:element name="ex5:bar"> <rng:data type="unsignedByte"/> </rng:element> </rng:choice> In the second case branch, the "ex5:bar" element is defined as mandatory so that this element must be present in a valid configuration if this branch is selected. However, the two elements in the first branch "foo" cannot be both declared as mandatory since each of them alone suffices for a valid configuration. As a result, the above RELAX NG fragment would successfully validate configurations where none of the three leaf elements are present. Therefore, mandatory choices, which can be recognized in the hybrid schema as <rng:choice> elements with the @nma:mandatory annotation, have to be handled in a special way: for each mandatory choice where at least one of the cases contains more than one node, a Schematron rule MUST be added enforcing the presence of at least one element from any of the cases. (RELAX NG schema guarantees that elements from different cases cannot be mixed together, that all mandatory nodes are present, etc.). For the example module above, the Schematron rule will be as follows: <sch:rule context="/nc:rpc-reply/nc:data"> <sch:assert test="ex5:foo1 or ex5:foo2 or ex5:bar"> Node(s) from at least one case of choice "foobar" must exist. </sch:assert> </sch:rule>
11.3. Mapping Default Values to DSRL
DSRL is the only component of DSDL that is allowed to change the information set of the validated XML document. While DSRL also has other functions, YANG-to-DSDL mapping uses it only for specifying and applying default contents. For XML instance documents based on YANG data models, insertion of default contents may potentially take place for all implicit nodes identified by the rules in Section 9.1.2. In DSRL, the default contents of an element are specified using the <dsrl:default-content> element, which is a child of <dsrl:element- map>. Two sibling elements of <dsrl:default-content> determine the context for the application of the default contents, see [DSRL]: o the <dsrl:parent> element contains an XSLT pattern specifying the parent element; the default contents are applied only if the parent element exists in the instance document; o the <dsrl:name> contains the XML name of the element that, if missing or empty, is inserted together with the contents of <dsrl: default-content>. The <dsrl:parent> element is optional in a general DSRL schema but, for the purpose of the YANG-to-DSDL mapping, this element MUST be always present, in order to guarantee a proper application of default contents. DSRL mapping only deals with <rng:element> patterns in the hybrid schema that define implicit nodes (see Section 9.1.2). Such element patterns are distinguished by having NETMOD-specific annotation attributes @nma:default or @nma:implicit, i.e., either: <rng:element name="ELEM" nma:default="DEFVALUE"> ... </rng:element> or <rng:element name="ELEM" nma:implicit="true"> ... </rng:element> The former case applies to leaf nodes having the 'default' substatement, but also to leaf nodes that obtain their default value from a typedef, if this typedef is expanded according to the rules in Section 9.2.2 so that the @nma:default annotation is attached directly to the leaf's element pattern.
The latter case is used for all implicit containers (see Section 9.1) and for leafs that obtain the default value from a typedef and don't have the @nma:default annotation. In the simplest case, both element patterns are mapped to the following DSRL element map: <dsrl:element-map> <dsrl:parent>XPARENT</dsrl:parent> <dsrl:name>ELEM</dsrl:name> <dsrl:default-content>DEFCONT</dsrl:default-content> </dsrl:element-map> where XPARENT is the absolute XPath of ELEM's parent element in the data tree and DEFCONT is constructed as follows: o If the implicit node ELEM is a leaf and has the @nma:default attribute, DEFCONT is set to the value of this attribute (denoted above as DEFVALUE). o If the implicit node ELEM is a leaf and has the @nma:implicit attribute with the value of "true", the default value has to be determined from the @nma:default attribute of the definition of ELEM's type (perhaps recursively) and used in place of DEFCONT in the above DSRL element map. See also Section 9.2.2. o Otherwise, the implicit node ELEM is a container and DEFCONT is constructed as an XML fragment containing all descendant elements of ELEM that have either the @nma:implicit or the @nma:default attribute. In addition, when mapping the default case of a choice, it has to be guaranteed that the default contents are not applied if any node from any non-default case is present. This is accomplished by setting <dsrl:parent> in a special way: <dsrl:parent>XPARENT[not (ELEM1|ELEM2|...|ELEMn)]</dsrl:parent> where ELEM1, ELEM2, ... ELEMn are the names of all top-level nodes from all non-default cases. The rest of the element map is exactly as before.
EXAMPLE. Consider the following YANG module: module example6 { namespace "http://example.com/ns/example6"; prefix ex6; container outer { leaf leaf1 { type uint8; default 1; } choice one-or-two { default "one"; container one { leaf leaf2 { type uint8; default 2; } } leaf leaf3 { type uint8; default 3; } } } }
The DSRL schema generated for the "get-reply" target document type will be: <?xml version="1.0" encoding="utf-8"?> <dsrl:maps xmlns:dsrl="http://purl.oclc.org/dsdl/dsrl" xmlns:ex6="http://example.com/ns/example6" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> <dsrl:element-map> <dsrl:parent>/nc:rpc-reply/nc:data</dsrl:parent> <dsrl:name>ex6:outer</dsrl:name> <dsrl:default-content> <ex6:leaf1>1</ex6:leaf1> <ex6:one> <ex6:leaf2>2</ex6:leaf2> </ex6:one> </dsrl:default-content> </dsrl:element-map> <dsrl:element-map> <dsrl:parent>/nc:rpc-reply/nc:data/ex6:outer</dsrl:parent> <dsrl:name>ex6:leaf1</dsrl:name> <dsrl:default-content>1</dsrl:default-content> </dsrl:element-map> <dsrl:element-map> <dsrl:parent> /nc:rpc-reply/nc:data/ex6:outer[not(ex6:leaf3)] </dsrl:parent> <dsrl:name>ex6:one</dsrl:name> <dsrl:default-content> <ex6:leaf2>2</ex6:leaf2> </dsrl:default-content> </dsrl:element-map> <dsrl:element-map> <dsrl:parent> /nc:rpc-reply/nc:data/ex6:outer/ex6:one </dsrl:parent> <dsrl:name>ex6:leaf2</dsrl:name> <dsrl:default-content>2</dsrl:default-content> </dsrl:element-map> </dsrl:maps> Note that the default value for "leaf3" defined in the YANG module is ignored because "leaf3" represents a non-default alternative of a choice and as such never becomes an implicit element.
12. Mapping NETMOD-Specific Annotations to DSDL Schema Languages
This section contains the mapping specification for the individual NETMOD-specific annotations. In each case, the result of the mapping must be inserted into an appropriate context of the target DSDL schema as described in Section 11. The context is determined by the element pattern in the hybrid schema to which the annotation is attached. In the rest of this section, CONTELEM will denote the name of this context element properly qualified with its namespace prefix.12.1. The @nma:config Annotation
If this annotation is present with the value of "false", the following rules MUST be observed for DSDL schemas of <nc:get-config> reply: o When generating RELAX NG, the contents of the CONTELEM definition MUST be changed to <rng:notAllowed>. o When generating Schematron or DSRL, the CONTELEM definition and all its descendants in the hybrid schema MUST be ignored.12.2. The @nma:default Annotation
This annotation is used for generating the DSRL schema as described in Section 11.3.12.3. The <nma:error-app-tag> Annotation
This annotation currently has no mapping defined.12.4. The <nma:error-message> Annotation
This annotation is handled within <nma:must>, see Section 12.13.12.5. The @if-feature Annotation
The information about available features MAY be supplied as an input parameter to an implementation. In this case, the following changes MUST be performed for all features that are considered unavailable: o When generating RELAX NG, the contents of the CONTELEM definition MUST be changed to <rng:notAllowed>. o When generating Schematron or DSRL, the CONTELEM definition and all its descendants in the hybrid schema MUST be ignored.
12.6. The @nma:implicit Annotation
This annotation is used for generating the DSRL schema as described in Section 11.3.12.7. The <nma:instance-identifier> Annotation
If this annotation element has the @require-instance attribute with the value of "false", it is ignored. Otherwise, it is mapped to the following Schematron assert: <sch:assert test="nmf:evaluate(.)"> The element pointed to by "CONTELEM" must exist. </sch:assert> The nmf:evaluate() function is an XSLT extension function (see Extension Functions in [XSLT]) that evaluates an XPath expression at run time. Such an extension function is available in Extended XSLT (EXSLT) or provided as a proprietary extension by some XSLT processors, for example Saxon.12.8. The @nma:key Annotation
Assume this annotation attribute contains "k_1 k_2 ... k_n", i.e., specifies n children of CONTELEM as list keys. The annotation is then mapped to the following Schematron report: <sch:report test="CONDITION"> Duplicate key of list "CONTELEM" </sch:report> where CONDITION has this form: preceding-sibling::CONTELEM[C_1 and C_2 and ... and C_n] Each sub-expression C_i, for i=1,2,...,n, specifies the condition for violated uniqueness of the key k_i, namely k_i=current()/k_i12.9. The @nma:leaf-list Annotation
This annotation is mapped to the following Schematron rule, which detects duplicate entries of a leaf-list: <sch:report test=". = preceding-sibling::PREFIX:sorted-entry"> Duplicate leaf-list entry "<sch:value-of select="."/>". </sch:report>
See Section 11.2 for a complete example.12.10. The @nma:leafref Annotation
This annotation is mapped to the following assert: <sch:assert test="PATH=."> Leaf "PATH" does not exist for leafref value "VALUE" </sch:assert> where PATH is the value of @nma:leafref and VALUE is the value of the context element in the instance document for which the referred leaf doesn't exist.12.11. The @nma:min-elements Annotation
This annotation is mapped to the following Schematron assert: <sch:assert test="count(../CONTELEM)>=MIN"> List "CONTELEM" - item count must be at least MIN </sch:assert> where MIN is the value of @nma:min-elements.12.12. The @nma:max-elements Annotation
This annotation is mapped to the following Schematron assert: <sch:assert test="count(../CONTELEM)<=MAX or preceding-sibling::../CONTELEM"> Number of list items must be at most MAX </sch:assert> where MAX is the value of @nma:min-elements.12.13. The <nma:must> Annotation
This annotation is mapped to the following Schematron assert: <sch:assert test="EXPRESSION"> MESSAGE </sch:assert> where EXPRESSION is the value of the mandatory @assert attribute of <nma:must>. If the <nma:error-message> subelement exists, MESSAGE is set to its contents; otherwise, it is set to the default message "Condition EXPRESSION must be true".
12.14. The <nma:ordered-by> Annotation
This annotation currently has no mapping defined.12.15. The <nma:status> Annotation
This annotation currently has no mapping defined.12.16. The @nma:unique Annotation
The mapping of this annotation is almost identical as for @nma:key, see Section 12.8, with two small differences: o The value of @nma:unique is a list of descendant schema node identifiers rather than simple leaf names. However, the XPath expressions specified in Section 12.8 work without any modifications if the descendant schema node identifiers are substituted for k_1, k_2, ..., k_n. o The message appearing as the text of <sch:report> is different: "Violated uniqueness for list CONTELEM".12.17. The @nma:when Annotation
This annotation is mapped to the following Schematron assert: <sch:assert test="EXPRESSION"> Node "CONTELEM" is only valid when "EXPRESSION" is true. </sch:assert> where EXPRESSION is the value of @nma:when.
13. IANA Considerations
This document requests the following two registrations of namespace URIs in the IETF XML registry [RFC3688]: ----------------------------------------------------- URI: urn:ietf:params:xml:ns:netmod:dsdl-annotations:1 Registrant Contact: The IESG. XML: N/A, the requested URI is an XML namespace. ----------------------------------------------------- ----------------------------------------------------- URI: urn:ietf:params:xml:ns:netmod:xpath-extensions:1 Registrant Contact: The IESG. XML: N/A, the requested URI is an XML namespace. -----------------------------------------------------14. Security Considerations
This document defines a procedure that maps data models expressed in the YANG language to a coordinated set of DSDL schemas. The procedure itself has no security impact on the Internet. DSDL schemas obtained by the mapping procedure may be used for validating the contents of NETCONF messages or entire datastores, and thus they provide additional validity checks above those performed by NETCONF server and client implementations supporting YANG data models. The strictness of this validation is directly derived from the source YANG modules that the validated XML data adhere to.15. Contributors
The following people contributed significantly to the initial version of this document: o Rohan Mahy o Sharon Chisholm (Ciena)
16. Acknowledgments
The editor wishes to thank the following individuals who provided helpful suggestions and/or comments on various versions of this document: Andy Bierman, Martin Bjorklund, Jirka Kosek, Juergen Schoenwaelder, and Phil Shafer.17. References
17.1. Normative References
[DSDL] ISO/IEC, "Document Schema Definition Languages (DSDL) - Part 1: Overview", ISO/IEC 19757-1, November 2004. [DSRL] ISO/IEC, "Information Technology - Document Schema Definition Languages (DSDL) - Part 8: Document Semantics Renaming Language - DSRL", ISO/ IEC 19757-8:2008(E), December 2008. [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997. [RFC3688] Mealling, M., "The IETF XML Registry", BCP 81, RFC 3688, January 2004. [RFC4741] Enns, R., "NETCONF Configuration Protocol", RFC 4741, December 2006. [RFC6020] Bjorklund, M., Ed., "YANG - A Data Modeling Language for Network Configuration Protocol (NETCONF)", RFC 6020, October 2010. [RFC6021] Schoenwaelder, J., Ed., "Common YANG Data Types", RFC 6021, October 2010. [RNG] ISO/IEC, "Information Technology - Document Schema Definition Languages (DSDL) - Part 2: Regular-Grammar- Based Validation - RELAX NG. Second Edition.", ISO/ IEC 19757-2:2008(E), December 2008. [RNG-CS] ISO/IEC, "Information Technology - Document Schema Definition Languages (DSDL) - Part 2: Regular-Grammar- Based Validation - RELAX NG. AMENDMENT 1: Compact Syntax", ISO/IEC 19757-2:2003/Amd. 1:2006(E), January 2006.
[RNG-DTD] Clark, J., Ed. and M. Murata, Ed., "RELAX NG DTD Compatibility", OASIS Committee Specification, 3 December 2001. [Schematron] ISO/IEC, "Information Technology - Document Schema Definition Languages (DSDL) - Part 3: Rule-Based Validation - Schematron", ISO/IEC 19757-3:2006(E), June 2006. [XML] Bray, T., Paoli, J., Sperberg-McQueen, C., Maler, E., and F. Yergeau, "Extensible Markup Language (XML) 1.0 (Fifth Edition)", World Wide Web Consortium Recommendation REC-xml-20081126, November 2008, <http://www.w3.org/TR/2006/REC-xml-20060816>. [XML-INFOSET] Tobin, R. and J. Cowan, "XML Information Set (Second Edition)", World Wide Web Consortium Recommendation REC-xml-infoset-20040204, February 2004, <http://www.w3.org/TR/2004/REC-xml-infoset-20040204>. [XPath] Clark, J. and S. DeRose, "XML Path Language (XPath) Version 1.0", World Wide Web Consortium Recommendation REC-xpath-19991116, November 1999, <http://www.w3.org/TR/1999/REC-xpath-19991116>. [XSD-D] Biron, P. and A. Malhotra, "XML Schema Part 2: Datatypes Second Edition", World Wide Web Consortium Recommendation REC-xmlschema-2-20041028, October 2004, <http://www.w3.org/TR/2004/REC-xmlschema-2-20041028>. [XSLT] Clark, J., "XSL Transformations (XSLT) Version 1.0", World Wide Web Consortium Recommendation REC-xslt- 19991116, November 1999.17.2. Informative References
[DHCPtut] Bjorklund, M., "DHCP Tutorial", November 2007, <http:/ /www.yang-central.org/twiki/bin/view/Main/ DhcpTutorial>. [RFC1157] Case, J., Fedor, M., Schoffstall, M., and J. Davin, "Simple Network Management Protocol (SNMP)", RFC 1157, May 1990.
[RFC2578] McCloghrie, K., Ed., Perkins, D., Ed., and J. Schoenwaelder, Ed., "Structure of Management Information Version 2 (SMIv2)", STD 58, RFC 2578, April 1999. [RFC5013] Kunze, J., "The Dublin Core Metadata Element Set", RFC 5013, August 2007. [RFC5277] Chisholm, S. and H. Trevino, "NETCONF Event Notifications", RFC 5277, July 2008. [Trang] Clark, J., "Trang: Multiformat schema converter based on RELAX NG", 2008, <http://www.thaiopensource.com/relaxng/trang.html>. [Vli04] van der Vlist, E., "RELAX NG", O'Reilly, 2004. [XSD] Thompson, H., Beech, D., Maloney, M., and N. Mendelsohn, "XML Schema Part 1: Structures Second Edition", World Wide Web Consortium Recommendation REC-xmlschema-1-20041028, October 2004, <http://www.w3.org/TR/2004/REC-xmlschema-1-20041028>. [pyang] Bjorklund, M. and L. Lhotka, "pyang: An extensible YANG validator and converter in Python", 2010, <http://code.google.com/p/pyang/>.
Appendix A. RELAX NG Schema for NETMOD-Specific Annotations
This appendix defines the content model for all NETMOD-specific annotations in the form of RELAX NG named pattern definitions. <CODE BEGINS> file "nmannot.rng" <?xml version="1.0" encoding="UTF-8"?> <grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:nma="urn:ietf:params:xml:ns:netmod:dsdl-annotations:1" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <define name="config-attribute"> <attribute name="nma:config"> <data type="boolean"/> </attribute> </define> <define name="data-element"> <element name="nma:data"> <ref name="__anyxml__"/> </element> </define> <define name="default-attribute"> <attribute name="nma:default"> <data type="string"/> </attribute> </define> <define name="error-app-tag-element"> <element name="nma:error-app-tag"> <text/> </element> </define> <define name="error-message-element"> <element name="nma:error-message"> <text/> </element> </define>
<define name="if-feature-attribute"> <attribute name="nma:if-feature"> <list> <data type="QName"/> </list> </attribute> </define> <define name="implicit-attribute"> <attribute name="nma:implicit"> <data type="boolean"/> </attribute> </define> <define name="instance-identifier-element"> <element name="nma:instance-identifier"> <optional> <attribute name="nma:require-instance"> <data type="boolean"/> </attribute> </optional> </element> </define> <define name="key-attribute"> <attribute name="nma:key"> <list> <data type="QName"/> </list> </attribute> </define> <define name="leaf-list-attribute"> <attribute name="nma:leaf-list"> <data type="boolean"/> </attribute> </define> <define name="leafref-attribute"> <attribute name="nma:leafref"> <data type="string"/> </attribute> </define>
<define name="mandatory-attribute"> <attribute name="nma:mandatory"> <data type="Name"/> </attribute> </define> <define name="max-elements-attribute"> <attribute name="nma:max-elements"> <data type="nonNegativeInteger"/> </attribute> </define> <define name="min-elements-attribute"> <attribute name="nma:min-elements"> <data type="nonNegativeInteger"/> </attribute> </define> <define name="module-attribute"> <attribute name="nma:module"> <data type="Name"/> </attribute> </define> <define name="must-element"> <element name="nma:must"> <attribute name="assert"> <data type="string"/> </attribute> <interleave> <optional> <ref name="error-app-tag-element"/> </optional> <optional> <ref name="error-message-element"/> </optional> </interleave> </element> </define>
<define name="notifications-element"> <element name="nma:notifications"> <zeroOrMore> <element name="nma:notification"> <ref name="__anyxml__"/> </element> </zeroOrMore> </element> </define> <define name="rpcs-element"> <element name="nma:rpcs"> <zeroOrMore> <element name="nma:rpc"> <interleave> <element name="nma:input"> <ref name="__anyxml__"/> </element> <optional> <element name="nma:output"> <ref name="__anyxml__"/> </element> </optional> </interleave> </element> </zeroOrMore> </element> </define> <define name="ordered-by-attribute"> <attribute name="nma:ordered-by"> <choice> <value>user</value> <value>system</value> </choice> </attribute> </define>
<define name="status-attribute"> <optional> <attribute name="nma:status"> <choice> <value>current</value> <value>deprecated</value> <value>obsolete</value> </choice> </attribute> </optional> </define> <define name="unique-attribute"> <optional> <attribute name="nma:unique"> <list> <data type="token"/> </list> </attribute> </optional> </define> <define name="units-attribute"> <optional> <attribute name="nma:units"> <data type="string"/> </attribute> </optional> </define> <define name="when-attribute"> <optional> <attribute name="nma:when"> <data type="string"/> </attribute> </optional> </define>
<define name="__anyxml__"> <zeroOrMore> <choice> <attribute> <anyName/> </attribute> <element> <anyName/> <ref name="__anyxml__"/> </element> <text/> </choice> </zeroOrMore> </define> </grammar> <CODE ENDS>Appendix B. Schema-Independent Library
In order to avoid copying the common named pattern definitions to every RELAX NG schema generated in the second mapping step, the definitions are collected in a library file -- schema-independent library -- which is included by the validating schemas under the file name "relaxng-lib.rng" (XML syntax) and "relaxng-lib.rnc" (compact syntax). The included definitions cover patterns for common elements from base NETCONF [RFC4741] and event notifications [RFC5277]. <CODE BEGINS> file "relaxng-lib.rng" <?xml version="1.0" encoding="UTF-8"?> <grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0" xmlns:en="urn:ietf:params:xml:ns:netconf:notification:1.0" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <define name="message-id-attribute"> <attribute name="message-id"> <data type="string"> <param name="maxLength">4095</param> </data> </attribute> </define>
<define name="ok-element"> <element name="nc:ok"> <empty/> </element> </define> <define name="eventTime-element"> <element name="en:eventTime"> <data type="dateTime"/> </element> </define> </grammar> <CODE ENDS>Appendix C. Mapping DHCP Data Model - A Complete Example
This appendix demonstrates both steps of the YANG-to-DSDL mapping applied to the "canonical" DHCP tutorial [DHCPtut] data model. The input YANG module is shown in Appendix C.1 and the output schemas in the following two subsections. The hybrid schema was obtained by the "dsdl" plugin of the pyang tool [pyang] and the validating DSDL schemas were obtained by XSLT stylesheets that are also part of pyang distribution. Due to the limit of 72 characters per line, a few long strings required manual editing, in particular the regular expression patterns for IP addresses, etc. These were replaced by the placeholder string "... regex pattern ...". Also, line breaks were added to several documentation strings and Schematron messages. Other than that, the results of the automatic translations were not changed.C.1. Input YANG Module
module dhcp { namespace "http://example.com/ns/dhcp"; prefix dhcp; import ietf-yang-types { prefix yang; } import ietf-inet-types { prefix inet; }
organization "yang-central.org"; description "Partial data model for DHCP, based on the config of the ISC DHCP reference implementation."; container dhcp { description "configuration and operational parameters for a DHCP server."; leaf max-lease-time { type uint32; units seconds; default 7200; } leaf default-lease-time { type uint32; units seconds; must '. <= ../max-lease-time' { error-message "The default-lease-time must be less than max-lease-time"; } default 600; } uses subnet-list; container shared-networks { list shared-network { key name; leaf name { type string; } uses subnet-list; } } container status { config false; list leases { key address;
leaf address { type inet:ip-address; } leaf starts { type yang:date-and-time; } leaf ends { type yang:date-and-time; } container hardware { leaf type { type enumeration { enum "ethernet"; enum "token-ring"; enum "fddi"; } } leaf address { type yang:phys-address; } } } } } grouping subnet-list { description "A reusable list of subnets"; list subnet { key net; leaf net { type inet:ip-prefix; } container range { presence "enables dynamic address assignment"; leaf dynamic-bootp { type empty; description "Allows BOOTP clients to get addresses in this range"; }
leaf low { type inet:ip-address; mandatory true; } leaf high { type inet:ip-address; mandatory true; } } container dhcp-options { description "Options in the DHCP protocol"; leaf-list router { type inet:host; ordered-by user; reference "RFC 2132, sec. 3.8"; } leaf domain-name { type inet:domain-name; reference "RFC 2132, sec. 3.17"; } } leaf max-lease-time { type uint32; units seconds; default 7200; } } } }C.2. Hybrid Schema
<?xml version="1.0" encoding="UTF-8"?> <grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:nma="urn:ietf:params:xml:ns:netmod:dsdl-annotations:1" xmlns:dc="http://purl.org/dc/terms" xmlns:a="http://relaxng.org/ns/compatibility/annotations/1.0" xmlns:dhcp="http://example.com/ns/dhcp" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <dc:creator>Pyang 1.0a, DSDL plugin</dc:creator> <dc:date>2010-06-17</dc:date> <start> <grammar nma:module="dhcp" ns="http://example.com/ns/dhcp"> <dc:source>YANG module 'dhcp'</dc:source> <start>
<nma:data> <optional> <element nma:implicit="true" name="dhcp:dhcp"> <interleave> <a:documentation> configuration and operational parameters for a DHCP server. </a:documentation> <optional> <element nma:default="7200" name="dhcp:max-lease-time" nma:units="seconds"> <data type="unsignedInt"/> </element> </optional> <optional> <element nma:default="600" name="dhcp:default-lease-time" nma:units="seconds"> <data type="unsignedInt"/> <nma:must assert=". <= ../dhcp:max-lease-time"> <nma:error-message> The default-lease-time must be less than max-lease-time </nma:error-message> </nma:must> </element> </optional> <ref name="_dhcp__subnet-list"/> <optional> <element name="dhcp:shared-networks"> <zeroOrMore> <element nma:key="dhcp:name" name="dhcp:shared-network"> <element name="dhcp:name"> <data type="string"/> </element> <ref name="_dhcp__subnet-list"/> </element> </zeroOrMore> </element> </optional> <optional> <element name="dhcp:status" nma:config="false"> <zeroOrMore> <element nma:key="dhcp:address" name="dhcp:leases"> <element name="dhcp:address"> <ref name="ietf-inet-types__ip-address"/> </element>
<interleave> <optional> <element name="dhcp:starts"> <ref name="ietf-yang-types__date-and-time"/> </element> </optional> <optional> <element name="dhcp:ends"> <ref name="ietf-yang-types__date-and-time"/> </element> </optional> <optional> <element name="dhcp:hardware"> <interleave> <optional> <element name="dhcp:type"> <choice> <value>ethernet</value> <value>token-ring</value> <value>fddi</value> </choice> </element> </optional> <optional> <element name="dhcp:address"> <ref name="ietf-yang-types__phys-address"/> </element> </optional> </interleave> </element> </optional> </interleave> </element> </zeroOrMore> </element> </optional> </interleave> </element> </optional> </nma:data> <nma:rpcs/> <nma:notifications/> </start> </grammar> </start> <define name="ietf-yang-types__phys-address"> <data type="string"> <param name="pattern">([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?</param>
</data> </define> <define name="ietf-inet-types__ipv6-address"> <data type="string"> <param name="pattern">... regex pattern ...</param> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="ietf-inet-types__ip-prefix"> <choice> <ref name="ietf-inet-types__ipv4-prefix"/> <ref name="ietf-inet-types__ipv6-prefix"/> </choice> </define> <define name="ietf-inet-types__host"> <choice> <ref name="ietf-inet-types__ip-address"/> <ref name="ietf-inet-types__domain-name"/> </choice> </define> <define name="ietf-yang-types__date-and-time"> <data type="string"> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="_dhcp__subnet-list"> <a:documentation>A reusable list of subnets</a:documentation> <zeroOrMore> <element nma:key="net" name="subnet"> <element name="net"> <ref name="ietf-inet-types__ip-prefix"/> </element> <interleave> <optional> <element name="range"> <interleave> <optional> <element name="dynamic-bootp"> <a:documentation> Allows BOOTP clients to get addresses in this range </a:documentation> <empty/> </element> </optional> <element name="low"> <ref name="ietf-inet-types__ip-address"/> </element> <element name="high">
<ref name="ietf-inet-types__ip-address"/> </element> </interleave> </element> </optional> <optional> <element name="dhcp-options"> <interleave> <a:documentation> Options in the DHCP protocol </a:documentation> <zeroOrMore> <element nma:leaf-list="true" name="router" nma:ordered-by="user"> <a:documentation> See: RFC 2132, sec. 3.8 </a:documentation> <ref name="ietf-inet-types__host"/> </element> </zeroOrMore> <optional> <element name="domain-name"> <a:documentation> See: RFC 2132, sec. 3.17 </a:documentation> <ref name="ietf-inet-types__domain-name"/> </element> </optional> </interleave> </element> </optional> <optional> <element nma:default="7200" name="max-lease-time" nma:units="seconds"> <data type="unsignedInt"/> </element> </optional> </interleave> </element> </zeroOrMore> </define> <define name="ietf-inet-types__domain-name"> <data type="string"> <param name="pattern">... regex pattern ...</param> <param name="minLength">1</param> <param name="maxLength">253</param> </data> </define>
<define name="ietf-inet-types__ipv4-prefix"> <data type="string"> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="ietf-inet-types__ipv4-address"> <data type="string"> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="ietf-inet-types__ipv6-prefix"> <data type="string"> <param name="pattern">... regex pattern ...</param> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="ietf-inet-types__ip-address"> <choice> <ref name="ietf-inet-types__ipv4-address"/> <ref name="ietf-inet-types__ipv6-address"/> </choice> </define> </grammar>C.3. Final DSDL Schemas
This appendix contains DSDL schemas that were obtained from the hybrid schema in Appendix C.2 by XSL transformations. These schemas can be directly used for validating a reply to unfiltered <nc:get> with the contents corresponding to the DHCP data model. The RELAX NG schema (in two parts, as explained in Section 8.2) also includes the schema-independent library from Appendix B.C.3.1. Main RELAX NG Schema for <nc:get> Reply
<?xml version="1.0" encoding="utf-8"?> <grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:nma="urn:ietf:params:xml:ns:netmod:dsdl-annotations:1" xmlns:dhcp="http://example.com/ns/dhcp" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes" ns="urn:ietf:params:xml:ns:netconf:base:1.0"> <include href="relaxng-lib.rng"/> <start> <element name="rpc-reply"> <ref name="message-id-attribute"/> <element name="data">
<interleave> <grammar ns="http://example.com/ns/dhcp"> <include href="dhcp-gdefs.rng"/> <start> <optional> <element name="dhcp:dhcp"> <interleave> <optional> <element name="dhcp:max-lease-time"> <data type="unsignedInt"/> </element> </optional> <optional> <element name="dhcp:default-lease-time"> <data type="unsignedInt"/> </element> </optional> <ref name="_dhcp__subnet-list"/> <optional> <element name="dhcp:shared-networks"> <zeroOrMore> <element name="dhcp:shared-network"> <element name="dhcp:name"> <data type="string"/> </element> <ref name="_dhcp__subnet-list"/> </element> </zeroOrMore> </element> </optional> <optional> <element name="dhcp:status"> <zeroOrMore> <element name="dhcp:leases"> <element name="dhcp:address"> <ref name="ietf-inet-types__ip-address"/> </element> <interleave> <optional> <element name="dhcp:starts"> <ref name="ietf-yang-types__date-and-time"/> </element> </optional> <optional> <element name="dhcp:ends"> <ref name="ietf-yang-types__date-and-time"/> </element> </optional>
<optional> <element name="dhcp:hardware"> <interleave> <optional> <element name="dhcp:type"> <choice> <value>ethernet</value> <value>token-ring</value> <value>fddi</value> </choice> </element> </optional> <optional> <element name="dhcp:address"> <ref name="ietf-yang-types__phys-address"/> </element> </optional> </interleave> </element> </optional> </interleave> </element> </zeroOrMore> </element> </optional> </interleave> </element> </optional> </start> </grammar> </interleave> </element> </element> </start> </grammar>C.3.2. RELAX NG Schema - Global Named Pattern Definitions
<?xml version="1.0" encoding="utf-8"?> <grammar xmlns="http://relaxng.org/ns/structure/1.0" xmlns:nma="urn:ietf:params:xml:ns:netmod:dsdl-annotations:1" xmlns:dhcp="http://example.com/ns/dhcp" datatypeLibrary="http://www.w3.org/2001/XMLSchema-datatypes"> <define name="ietf-yang-types__phys-address"> <data type="string"> <param name="pattern"> ([0-9a-fA-F]{2}(:[0-9a-fA-F]{2})*)?
</param> </data> </define> <define name="ietf-inet-types__ipv6-address"> <data type="string"> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="ietf-inet-types__ip-prefix"> <choice> <ref name="ietf-inet-types__ipv4-prefix"/> <ref name="ietf-inet-types__ipv6-prefix"/> </choice> </define> <define name="ietf-inet-types__host"> <choice> <ref name="ietf-inet-types__ip-address"/> <ref name="ietf-inet-types__domain-name"/> </choice> </define> <define name="ietf-yang-types__date-and-time"> <data type="string"> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="_dhcp__subnet-list"> <zeroOrMore> <element name="subnet"> <element name="net"> <ref name="ietf-inet-types__ip-prefix"/> </element> <interleave> <optional> <element name="range"> <interleave> <optional> <element name="dynamic-bootp"> <empty/> </element> </optional> <element name="low"> <ref name="ietf-inet-types__ip-address"/> </element> <element name="high"> <ref name="ietf-inet-types__ip-address"/> </element> </interleave> </element>
</optional> <optional> <element name="dhcp-options"> <interleave> <zeroOrMore> <element name="router"> <ref name="ietf-inet-types__host"/> </element> </zeroOrMore> <optional> <element name="domain-name"> <ref name="ietf-inet-types__domain-name"/> </element> </optional> </interleave> </element> </optional> <optional> <element name="max-lease-time"> <data type="unsignedInt"/> </element> </optional> </interleave> </element> </zeroOrMore> </define> <define name="ietf-inet-types__domain-name"> <data type="string"> <param name="pattern">... regex pattern ...</param> <param name="minLength">1</param> <param name="maxLength">253</param> </data> </define> <define name="ietf-inet-types__ipv4-prefix"> <data type="string"> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="ietf-inet-types__ipv4-address"> <data type="string"> <param name="pattern">... regex pattern ...</param> </data> </define> <define name="ietf-inet-types__ipv6-prefix"> <data type="string"> <param name="pattern">... regex pattern ...</param> <param name="pattern">... regex pattern ...</param> </data>
</define> <define name="ietf-inet-types__ip-address"> <choice> <ref name="ietf-inet-types__ipv4-address"/> <ref name="ietf-inet-types__ipv6-address"/> </choice> </define> </grammar>C.3.3. Schematron Schema for <nc:get> Reply
<?xml version="1.0" encoding="utf-8"?> <sch:schema xmlns:sch="http://purl.oclc.org/dsdl/schematron"> <sch:ns uri="http://example.com/ns/dhcp" prefix="dhcp"/> <sch:ns uri="urn:ietf:params:xml:ns:netconf:base:1.0" prefix="nc"/> <sch:pattern abstract="true" id="_dhcp__subnet-list"> <sch:rule context="$start/$pref:subnet"> <sch:report test="preceding-sibling::$pref:subnet [$pref:net=current()/$pref:net]"> Duplicate key "net" </sch:report> </sch:rule> <sch:rule context="$start/$pref:subnet/$pref:dhcp-options/$pref:router"> <sch:report test=".=preceding-sibling::router"> Duplicate leaf-list value "<sch:value-of select="."/>" </sch:report> </sch:rule> </sch:pattern> <sch:pattern id="dhcp"> <sch:rule context="/nc:rpc-reply/nc:data/dhcp:dhcp/dhcp:default-lease-time"> <sch:assert test=". <= ../dhcp:max-lease-time"> The default-lease-time must be less than max-lease-time </sch:assert> </sch:rule> <sch:rule context="/nc:rpc-reply/nc:data/dhcp:dhcp/ dhcp:shared-networks/dhcp:shared-network"> <sch:report test="preceding-sibling::dhcp:shared-network [dhcp:name=current()/dhcp:name]"> Duplicate key "dhcp:name" </sch:report> </sch:rule> <sch:rule context="/nc:rpc-reply/nc:data/dhcp:dhcp/ dhcp:status/dhcp:leases"> <sch:report test="preceding-sibling::dhcp:leases [dhcp:address=current()/dhcp:address]"> Duplicate key "dhcp:address"
</sch:report> </sch:rule> </sch:pattern> <sch:pattern id="id2768196" is-a="_dhcp__subnet-list"> <sch:param name="start" value="/nc:rpc-reply/nc:data/dhcp:dhcp"/> <sch:param name="pref" value="dhcp"/> </sch:pattern> <sch:pattern id="id2768215" is-a="_dhcp__subnet-list"> <sch:param name="start" value="/nc:rpc-reply/nc:data/dhcp:dhcp/ dhcp:shared-networks/dhcp:shared-network"/> <sch:param name="pref" value="dhcp"/> </sch:pattern> </sch:schema>C.3.4. DSRL Schema for <nc:get> Reply
<?xml version="1.0" encoding="utf-8"?> <dsrl:maps xmlns:dsrl="http://purl.oclc.org/dsdl/dsrl" xmlns:dhcp="http://example.com/ns/dhcp" xmlns:nc="urn:ietf:params:xml:ns:netconf:base:1.0"> <dsrl:element-map> <dsrl:parent>/nc:rpc-reply/nc:data</dsrl:parent> <dsrl:name>dhcp:dhcp</dsrl:name> <dsrl:default-content> <dhcp:max-lease-time>7200</dhcp:max-lease-time> <dhcp:default-lease-time>600</dhcp:default-lease-time> </dsrl:default-content> </dsrl:element-map> <dsrl:element-map> <dsrl:parent>/nc:rpc-reply/nc:data/dhcp:dhcp</dsrl:parent> <dsrl:name>dhcp:max-lease-time</dsrl:name> <dsrl:default-content>7200</dsrl:default-content> </dsrl:element-map> <dsrl:element-map> <dsrl:parent>/nc:rpc-reply/nc:data/dhcp:dhcp</dsrl:parent> <dsrl:name>dhcp:default-lease-time</dsrl:name> <dsrl:default-content>600</dsrl:default-content> </dsrl:element-map> <dsrl:element-map> <dsrl:parent> /nc:rpc-reply/nc:data/dhcp:dhcp/dhcp:subnet </dsrl:parent> <dsrl:name>dhcp:max-lease-time</dsrl:name> <dsrl:default-content>7200</dsrl:default-content> </dsrl:element-map> <dsrl:element-map>
<dsrl:parent> /nc:rpc-reply/nc:data/dhcp:dhcp/dhcp:shared-networks/ dhcp:shared-network/dhcp:subnet </dsrl:parent> <dsrl:name>dhcp:max-lease-time</dsrl:name> <dsrl:default-content>7200</dsrl:default-content> </dsrl:element-map> </dsrl:maps>Author's Address
Ladislav Lhotka (editor) CESNET EMail: lhotka@cesnet.cz