180 likes | 286 Views
Using DSDL plus annotations for Netconf (+) data modeling. Rohan Mahy rohan@ekabal.com draft-mahy-canmod-dsdl-01. What is DSDL?. DSDL = Document Schema Data Languages http://dsdl.org An ISO family of standards DSDL uses a modular approach to schema languages
E N D
Using DSDL plus annotations for Netconf (+) data modeling Rohan Mahy rohan@ekabal.com draft-mahy-canmod-dsdl-01
What is DSDL? • DSDL = Document Schema Data Languages http://dsdl.org • An ISO family of standards • DSDL uses a modular approach to schema languages • Part 2 = Relax NG - traditional schema language: structure • Part 3 = Schematron - rule based validation • Part 4 = NVRL - validate parts of instance doc with different schemas/languages • Part 5 = DataType Library Language • etc … • Annotations are encouraged as part of modularity • My usage of “Data Model” • A model for describing the semantics and syntax of (configuration and operational) data. Does not include verbs.
Why Relax NG for syntax? • Easy to read and learn (Compact or XML) • Used by W3C (XHTML, SVG, XML Sig, RDF) • Better XML support than XSD: • robust native support for XML namespaces • unifies elements and attributes • unordered content supported • describes which pattern is root element • Convertible to XSD • Patterns can be scoped to prevent conflicts from one module
Schematron - rule-based validation • Rule-based validation based on XPath expressions. • Supports reusable abstract patterns • Validation can be split in phases based on context • Implementer can use a Schematron validator or write their own native code to perform an equivalent validation check. <pattern> <rule context="//dhcp:dhcp"> <assert test="dhcp:default-lease-time <= dhcp:max-lease-time"> Default lease time cannot be larger than maximum lease time </assert> </rule> </pattern>
Semantic Annotations • Important annotations: • keys to uniquely identify elements in a list • define relational integrity constraints • insure data is relevant to operation containing it • These annotations can be implemented in native code on device or converted to additional Schematron rules by XSLT
Relax NG has a nice pattern-based extensibility mechanism redefine patterns completely combine by choice combine by interleave Can include external grammars and keep scopes separate (ex: both dhcp.rnc and interfaces.rnc have an element-interface pattern) # config-root.rnc default namespace = "http://example.org/ns/root" start = element-config element-config = element config { external "interfaces.rnc" ? & external "dhcp+extensions.rnc" ? } # dhcp+extensions.rnc include "dhcp.rnc" # base module include "dhcp-tz.rnc" # timezone extension include "dhcp-wins.rnc" # WINS server ext # dhcp-tz.rnc namespace tz = "http://example.org/ns/dhcp/timezone" element-dhcp-option &= element tz:timezone { token }? Extensibility
Met nearly all requirements in RCDML draft • Met ALL Netconf-specific requirements (Section 3.1) • Some highlighted unique features • Compound keys / Deep keys <subnet> <prefix> <network>192.168.24.0</network> <prefix-length>24</prefix-length> </prefix> <range> <low>192.168.24.20</low> <high>192.168.24.250</high> </range> </subnet> • Implementation Defaults • Most defaults are valid per version of the schema. • Formal validation w/ Schematron • Can implement these rules via custom code from assert text, or automatically using a Schematron validator (free libraries in C++, Java, C and Python)
What’s nice about this approach • Reuse of appropriate schema languages and tools is a good thing. • Modular architecture makes incremental implementation/deployment and additional reuse easier (add other parts of DSDL, use subset of Netconf solution for another IETF protocol). • Being able to add data model information for existing IETF schemas (LDAP-enabled directories, SIP phone config files, early Netconf users) is a good thing. • Making Schematron available for formal machine-readable validation to clients that want it is a good thing.
Schema Plus vs. Model Plus approach • Pure Semantics • UML / XMI • OWL (without a specific serialization) • Semantics + some syntax (top down) • Yang • Kalua • Schema + some semantics (bottom up) • DSDL + annotations • XSD + annotations
Semantics vs. Syntax dilemma • Pure semantic approaches can represent things we cannot easily represent in Netconf or even in XML syntax • Many to many relationships (graph vs tree) • Top down approaches can constrain legitimately good syntax and blur semantics: • element vs. attribute representation of terminal nodes • not distinguishing semantically ordered content (sequence) from unordered content (bags) • Bottom up approaches are not easily convertible to radically different syntax (ex: CLI, MIBs). Can't represent inheritance/classes natively, but can generate equivalent instance documents.
Upgrading schema to data model • There are lots of XML-based schema in the wild (XSD and Relax NG). • It seems very beneficial to be able to take an existing schema and turn it into a data model while maintaining backward syntax compatibility • Folks who defined XSD for use with Netconf. • Other IETF WGs that already defined syntax of their schema but want data modeling. • Other schemas defined elsewhere that we want to import for Netconf.
Start with basic Relax NG start = element-dhcp element-dhcp = element dhcp { [ compat:documentation ["DHCP top level element"] ] element subnet { element network { ipv4-address-content }, element prefix-length { xsd:short { minInclusive = "0" maxInclusive = "32" } } element-range?, element leases { element-lease* }?, }* } element-range = element range { element low { ipv4-address-content }, element high { ipv4-address-content } >> compat:documentation ["range of dynamic addresses"] } element-lease = element lease { attribute ip-address { ipv4-address-content }, element starts { xsd:dateTime }, element ends { xsd:dateTime }, element mac-address { mac-address-content } } Syntax describes hierarchy, cardinality, types, range and pattern restrictions Relax NG Compact and XML forms are 2-way convertible Add annotations:initial and following types
infoType used to validate that data is sent in an appropriate operation element-leases = element leases { element lease { attribute ip-address { ipv4-address-content }, element starts { xsd:dateTime }, element ends { xsd:dateTime }, element mac-address { mac-address-content } }* >> dml:infoType ["status"] } can be in a get can be in RPC‘revoke-lease’ request revokeLeaseType = element revoke-lease { element address { ipv4-address-content }+ >> dml:infoType [ op="rpc/revoke-lease" "action"]} revokeLeaseResponseType = element result { ( "ok" | "no-such-lease" ) >> dml:infoType [ op="rpc-response#revoke-lease" "status"]} can be in RPCreply for a ‘revoke-lease’
Keys and Keyrefs element-subnet = element subnet { element network { ipv4-address-content }, element prefix-length { xsd:short { minInclusive = "0" maxInclusive = "32" } }, element interface-filter { element interface { xsd:token >> dml:keyref ["//int:interface"] }+ } } element-interfaces = element int:interfaces { element int:interface { element int:ifIndex { xsd:token }, element int:ifType { xsd:token } >> dml:key ["int:ifIndex"] }+ } <config:config xmlns="http://example.org/ns/dhcp" xmlns:config="http://example.org/ns/config" xmlns:dhcp="http://example.org/ns/dhcp" xmlns:int="http://example.org/ns/int"> <dhcp> <subnet> <network>10.1.1.0</network> <prefix-length>24</prefix-length> <interface-filter> <interface>lo0</interface> <interface>en1</interface> </interface-filter> </subnet> </dhcp> <int:interfaces> <int:interface><int:ifIndex>lo0</int:ifIndex> </int:interface> <int:interface><int:ifIndex>en2</int:ifIndex> </int:interface> </int:interfaces> </config:config>
XSLT converts this to Schematron <pattern abstract="true" id="key"> <rule context="$context"> <assert test="count($context[$key=current()/$key])=1"> The key "$key" needs to be unique within the list at: $context </assert> </rule> </pattern> <pattern is-a="key" id="interface"> <param name="context" value="//int:interfaces/int:interface"/> <param name="key" value="int:key"/> </pattern> <pattern abstract="true" id="keyref"> <rule context="$keyref-context"> <assert test="$key-context[$key=current()]"> The contents of "$keyref-context" must be a <name/> with the key "$key" in this context: $key-context. </assert> </rule> </pattern> <pattern is-a="keyref"> <param name="keyref-context" value="//dhcp:interface-filter/dhcp:interface"/> <param name="key-context" value="//int:interfaces/int:interface"/> <param name="key" value="int:ifIndex"/> </pattern>
start = element-dhcp element-dhcp = element dhcp { global-timer-elements, element-subnet*, element-shared-network* >> dc:title [ "Example schema for DHCP server" ] >> dml:version ["1.0"] >> dc:type ["Dataset"] >> dc:creator [ "Rohan Mahy" ] >> dml:organization [ "as an individual" ] >> dml:contact [ "mailto:rohan@ekabal.com" ] >> dc:created [ "2008-02-13" ] } global-timer-elements = ( element default-lease-time { xsd:unsignedInt >> compat:defaultValue ["3600"] >> dml:units ["s"] }?, element max-lease-time { xsd:unsignedInt >> dml:units ["s"] }? ) element-shared-network = element shared-network { attribute name { token }, element-subnet* }
element-subnet = element subnet { element-network, element-prefix-length, element-range?, element-dhcp-options?, element max-lease-time { xsd:unsignedInt >> dml:units ["s"] >> dml:status ["deprecated"] }?, element leases { element-lease* >> dml:infoType ["status"] }?, element-interface-filter? >> dml:key ["concat(network, '/', prefix-length)"] >> dml:manual-validation-rule [ "Verify that none of the subnets overlap with other subnets." ] } element-range = element range { element low { ipv4-address-content }?, element high { ipv4-address-content }? >> dml:existence [] >> dml:manual-validation-rule [ "Verify the range is within the subnet." ] }
element-dhcp-options = element dhcp-options { element-router-list-option? & element-domain-list-option? & element-custom-option* } element-lease = element lease { attribute ip-address { ipv4-address-content }, element starts { xsd:dateTime }, element ends { xsd:dateTime }, element mac-address { mac-address-content } >> dml:key ["@ip-address"] } element-custom-option = element custom { attribute option { xsd:unsignedByte }, ( element ip-address { ipv4-address-content } | element string { string } ) >> dml:key ["@option"] } element-interface-filter = element interface-filter { element-interface+ } element-interface = element interface { token >> dml:keyref ["//int:interface"] }