├── .github └── workflows │ └── ci.yaml ├── .gitignore ├── .gitmodules ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── assets ├── data │ ├── interfaces.json │ └── isis.json └── yang │ ├── iana-bfd-types@2018-08-01.yang │ ├── iana-if-type@2017-01-19.yang │ ├── iana-routing-types@2018-10-29.yang │ ├── ietf-bfd-types@2018-08-01.yang │ ├── ietf-interfaces@2018-02-20.yang │ ├── ietf-ip@2018-02-22.yang │ ├── ietf-ipv4-unicast-routing@2018-03-13.yang │ ├── ietf-isis@2019-10-15.yang │ ├── ietf-key-chain@2017-04-18.yang │ ├── ietf-mpls-ldp@2020-02-25.yang │ ├── ietf-netconf-acm@2018-02-14.yang │ ├── ietf-restconf@2017-01-26.yang │ ├── ietf-routing-types@2017-12-04.yang │ └── ietf-routing@2018-01-25.yang ├── benches └── data.rs ├── codecov.yml ├── examples ├── data_diff.rs ├── data_edit.rs ├── data_iteration.rs ├── data_json2xml.rs ├── schema_iteration.rs └── schema_yang2yin.rs ├── libyang3-sys ├── Cargo.toml ├── LICENSE ├── build.rs ├── pre-generated-bindings │ └── libyang3-f313632a8ff45d7bba4be29ea9dc83ab5b533807.rs ├── src │ └── lib.rs └── wrapper.h ├── rustfmt.toml ├── src ├── context.rs ├── data.rs ├── error.rs ├── iter.rs ├── lib.rs ├── schema.rs └── utils.rs └── tests ├── data.rs └── schema.rs /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | pull_request: 8 | branches: 9 | - "master" 10 | - "yang2" 11 | 12 | env: 13 | CARGO_TERM_COLOR: always 14 | # Make sure CI fails on all warnings, including Clippy lints 15 | RUSTFLAGS: "-Dwarnings" 16 | 17 | jobs: 18 | fmt: 19 | name: Code Formatting Check 20 | runs-on: ubuntu-latest 21 | steps: 22 | - uses: actions/checkout@v4 23 | - uses: dtolnay/rust-toolchain@stable 24 | with: 25 | components: rustfmt 26 | - uses: Swatinem/rust-cache@v2 27 | - name: "rustfmt --check" 28 | run: | 29 | if ! rustfmt --check --edition 2021 $(git ls-files '*.rs'); then 30 | printf "Please run \`rustfmt --edition 2021 \$(git ls-files '*.rs')\` to fix rustfmt errors.\nSee CONTRIBUTING.md for more details.\n" >&2 31 | exit 1 32 | fi 33 | 34 | clippy_check: 35 | name: Linter Check 36 | runs-on: ubuntu-latest 37 | steps: 38 | - uses: actions/checkout@v4 39 | - uses: dtolnay/rust-toolchain@stable 40 | with: 41 | components: clippy 42 | - name: Run Clippy 43 | run: cargo clippy 44 | 45 | tests_and_coverage_report: 46 | name: Tests and Coverage Report 47 | runs-on: ubuntu-latest 48 | steps: 49 | - uses: actions/checkout@v4 50 | - name: Install Rust 51 | run: rustup update stable 52 | - name: Install cargo-llvm-cov 53 | uses: taiki-e/install-action@cargo-llvm-cov 54 | - name: Generate code coverage 55 | run: cargo llvm-cov --features bundled --codecov --output-path codecov.json 56 | - name: Upload to Codecov 57 | uses: codecov/codecov-action@v4 58 | if: github.event_name != 'pull_request' 59 | with: 60 | files: ./lcov.info 61 | fail_ci_if_error: false 62 | token: ${{ secrets.CODECOV_TOKEN }} 63 | 64 | tests_arm: 65 | name: Tests (Arm64) 66 | runs-on: ubuntu-24.04-arm 67 | steps: 68 | - uses: actions/checkout@v4 69 | - uses: dtolnay/rust-toolchain@stable 70 | - name: Run tests 71 | run: cargo test --features bundled 72 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "libyang3-sys/libyang"] 2 | path = libyang3-sys/libyang 3 | url = https://github.com/CESNET/libyang.git 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "yang3" 3 | version = "0.16.0" 4 | authors = ["Renato Westphal "] 5 | description = "libyang3 bindings for Rust" 6 | keywords = ["yang", "libyang"] 7 | edition = "2018" 8 | license = "MIT" 9 | repository = "https://github.com/holo-routing/yang-rs" 10 | documentation = "https://docs.rs/yang3" 11 | readme = "README.md" 12 | categories = ["parser-implementations"] 13 | exclude = ["assets/**"] 14 | 15 | [dependencies] 16 | libyang3-sys = { path = "libyang3-sys", version = "0.6.0" } 17 | bitflags = "2.5" 18 | num-traits = "0.2" 19 | num-derive = "0.4" 20 | 21 | [target.'cfg(windows)'.dependencies] 22 | libc = "0.2" 23 | 24 | [dev-dependencies] 25 | criterion = "0.5.1" 26 | 27 | [lints.rust] 28 | rust_2018_idioms = "warn" 29 | 30 | [lints.clippy] 31 | missing_safety_doc = "allow" 32 | too_long_first_doc_paragraph = "allow" 33 | 34 | [[bench]] 35 | name = "data" 36 | harness = false 37 | 38 | [features] 39 | default = [] 40 | bindgen = ["libyang3-sys/bindgen"] 41 | bundled = ["libyang3-sys/bundled"] 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020 Renato Westphal 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # yang-rs 2 | 3 | [![Crates.io][crates-badge]][crates-url] 4 | [![Documentation][docs-badge]][docs-url] 5 | [![MIT licensed][mit-badge]][mit-url] 6 | [![Build Status][actions-badge]][actions-url] 7 | [![codecov][codecov-badge]][codecov-url] 8 | 9 | [crates-badge]: https://img.shields.io/crates/v/yang3.svg 10 | [crates-url]: https://crates.io/crates/yang3 11 | [docs-badge]: https://docs.rs/yang3/badge.svg 12 | [docs-url]: https://docs.rs/yang3 13 | [mit-badge]: https://img.shields.io/badge/license-MIT-blue.svg 14 | [mit-url]: https://github.com/holo-routing/yang-rs/blob/master/LICENSE 15 | [actions-badge]: https://github.com/holo-routing/yang-rs/workflows/CI/badge.svg 16 | [actions-url]: https://github.com/holo-routing/yang-rs/actions?query=workflow%3ACI+branch%3Amaster 17 | [codecov-badge]: https://codecov.io/gh/holo-routing/yang-rs/branch/master/graph/badge.svg?token=1KE3JMHG0H 18 | [codecov-url]: https://codecov.io/gh/holo-routing/yang-rs 19 | 20 | Rust bindings for the [libyang] library. 21 | 22 | For raw FFI bindings for libyang, see [libyang3-sys]. 23 | 24 | [libyang]: https://github.com/CESNET/libyang/ 25 | [libyang3-sys]: ./libyang3-sys 26 | 27 | #### Cargo.toml 28 | 29 | ```toml 30 | [dependencies] 31 | yang3 = "0.16" 32 | ``` 33 | ## Design Goals 34 | * Provide high-level bindings for libyang using idiomatic Rust 35 | * Leverage Rust's ownership system to detect API misuse problems at compile time 36 | * Automatic resource management 37 | * Zero-cost abstractions 38 | 39 | ## Feature flags 40 | By default, yang-rs uses pre-generated FFI bindings and uses dynamic linking to load libyang. The following feature flags, however, can be used to change that behavior: 41 | * **bundled**: instructs cargo to download and build libyang from the sources. The resulting objects are grouped into a static archive linked to this crate. This feature can be used when having a libyang dynamic link dependency isn't desirable. 42 | * Additional build requirements: *cc 1.0*, *cmake 0.1*, a C compiler and CMake. 43 | * **use_bindgen**: generate new C FFI bindings dynamically instead of using the pre-generated ones. Useful when updating this crate to use newer libyang versions. 44 | * Additional build requirements: *bindgen 0.68.0* 45 | 46 | ## Example 47 | 48 | A basic example that parses and validates JSON instance data, and then converts 49 | it to the XML format: 50 | ```rust,no_run 51 | use std::fs::File; 52 | use yang3::context::{Context, ContextFlags}; 53 | use yang3::data::{ 54 | Data, DataFormat, DataParserFlags, DataPrinterFlags, DataTree, 55 | DataValidationFlags, 56 | }; 57 | 58 | static SEARCH_DIR: &str = "./assets/yang/"; 59 | 60 | fn main() -> std::io::Result<()> { 61 | // Initialize context. 62 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 63 | .expect("Failed to create context"); 64 | ctx.set_searchdir(SEARCH_DIR) 65 | .expect("Failed to set YANG search directory"); 66 | 67 | // Load YANG modules. 68 | for module_name in &["ietf-interfaces", "iana-if-type"] { 69 | ctx.load_module(module_name, None, &[]) 70 | .expect("Failed to load module"); 71 | } 72 | 73 | // Parse and validate data tree in the JSON format. 74 | let dtree = DataTree::parse_file( 75 | &ctx, 76 | File::open("./assets/data/interfaces.json")?, 77 | DataFormat::JSON, 78 | DataParserFlags::empty(), 79 | DataValidationFlags::NO_STATE, 80 | ) 81 | .expect("Failed to parse data tree"); 82 | 83 | // Print data tree in the XML format. 84 | dtree 85 | .print_file( 86 | std::io::stdout(), 87 | DataFormat::XML, 88 | DataPrinterFlags::WD_ALL | DataPrinterFlags::WITH_SIBLINGS, 89 | ) 90 | .expect("Failed to print data tree"); 91 | 92 | Ok(()) 93 | } 94 | ``` 95 | 96 | Note the `NO_STATE` flag passed to `parse_file` since the example json file does not contain state data. 97 | More examples can be found [here][examples]. 98 | 99 | [examples]: https://github.com/holo-routing/yang-rs/tree/master/examples 100 | 101 | ## License 102 | 103 | This project is licensed under the [MIT license]. 104 | 105 | [MIT license]: https://github.com/holo-routing/yang-rs/blob/master/LICENSE 106 | 107 | ### Contributing 108 | 109 | Bug reports and pull requests are welcome on GitHub at https://github.com/holo-routing/yang-rs. 110 | -------------------------------------------------------------------------------- /assets/data/interfaces.json: -------------------------------------------------------------------------------- 1 | { 2 | "ietf-interfaces:interfaces":{ 3 | "interface": [ 4 | { 5 | "name": "eth/0/0", 6 | "description": "ENG", 7 | "type": "iana-if-type:ethernetCsmacd", 8 | "enabled": true 9 | } 10 | ], 11 | "interface": [ 12 | { 13 | "name": "eth/0/1", 14 | "description": "MKT", 15 | "type": "iana-if-type:ethernetCsmacd", 16 | "enabled": true 17 | } 18 | ] 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /assets/data/isis.json: -------------------------------------------------------------------------------- 1 | { 2 | "ietf-interfaces:interfaces":{ 3 | "interface": [ 4 | { 5 | "name": "eth/0/0", 6 | "description": "ENG", 7 | "type": "iana-if-type:ethernetCsmacd", 8 | "enabled": true 9 | } 10 | ], 11 | "interface": [ 12 | { 13 | "name": "eth/0/1", 14 | "description": "MKT", 15 | "type": "iana-if-type:ethernetCsmacd", 16 | "enabled": true 17 | } 18 | ] 19 | }, 20 | "ietf-routing:routing": { 21 | "control-plane-protocols": { 22 | "control-plane-protocol": [ 23 | { 24 | "type": "ietf-isis:isis", 25 | "name": "default", 26 | "ietf-isis:isis": { 27 | "area-address": [ 28 | "00" 29 | ], 30 | "interfaces": { 31 | "interface": [ 32 | { 33 | "name": "eth/0/0" 34 | } 35 | ] 36 | } 37 | } 38 | } 39 | ] 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /assets/yang/iana-bfd-types@2018-08-01.yang: -------------------------------------------------------------------------------- 1 | module iana-bfd-types { 2 | 3 | yang-version 1.1; 4 | 5 | namespace "urn:ietf:params:xml:ns:yang:iana-bfd-types"; 6 | 7 | prefix "iana-bfd-types"; 8 | 9 | organization "IANA"; 10 | 11 | contact 12 | " Internet Assigned Numbers Authority 13 | 14 | Postal: ICANN 15 | 12025 Waterfront Drive, Suite 300 16 | Los Angeles, CA 90094-2536 17 | United States of America 18 | 19 | Tel: +1 310 823 9358 20 | "; 21 | 22 | description 23 | "This module defines YANG data types for IANA-registered 24 | BFD parameters. 25 | 26 | This YANG module is maintained by IANA and reflects the 27 | 'BFD Diagnostic Codes' and 'BFD Authentication Types' registries. 28 | 29 | Copyright (c) 2018 IETF Trust and the persons 30 | identified as authors of the code. All rights reserved. 31 | 32 | Redistribution and use in source and binary forms, with or 33 | without modification, is permitted pursuant to, and subject 34 | to the license terms contained in, the Simplified BSD License 35 | set forth in Section 4.c of the IETF Trust's Legal Provisions 36 | Relating to IETF Documents 37 | (http://trustee.ietf.org/license-info). 38 | 39 | This version of this YANG module is part of RFC XXXX; see 40 | the RFC itself for full legal notices."; 41 | 42 | // RFC Ed.: replace XXXX with actual RFC number and remove 43 | // this note 44 | 45 | reference "RFC XXXX"; 46 | 47 | revision 2018-08-01 { 48 | description "Initial revision."; 49 | reference "RFC XXXX: IANA BFD YANG Data Types."; 50 | } 51 | 52 | /* 53 | * Type Definitions 54 | */ 55 | typedef diagnostic { 56 | type enumeration { 57 | enum none { 58 | value 0; 59 | description "None"; 60 | } 61 | enum control-expiry { 62 | value 1; 63 | description "Control timer expiry"; 64 | } 65 | enum echo-failed { 66 | value 2; 67 | description "Echo failure"; 68 | } 69 | enum neighbor-down { 70 | value 3; 71 | description "Neighbor down"; 72 | } 73 | enum forwarding-reset { 74 | value 4; 75 | description "Forwarding reset"; 76 | } 77 | enum path-down { 78 | value 5; 79 | description "Path down"; 80 | } 81 | enum concatenated-path-down { 82 | value 6; 83 | description "Concatenated path down"; 84 | } 85 | enum admin-down { 86 | value 7; 87 | description "Admin down"; 88 | } 89 | enum reverse-concatenated-path-down { 90 | value 8; 91 | description "Reverse concatenated path down"; 92 | } 93 | enum mis-connectivity-defect { 94 | value 9; 95 | description "Mis-connectivity defect as specified in RFC6428"; 96 | } 97 | } 98 | description 99 | "BFD diagnostic as defined in RFC 5880, values are maintained in 100 | the 'BFD Diagnostic Codes' IANA registry. Range is 0 to 31."; 101 | } 102 | 103 | typedef auth-type { 104 | type enumeration { 105 | enum reserved { 106 | value 0; 107 | description "Reserved"; 108 | } 109 | enum simple-password { 110 | value 1; 111 | description "Simple password"; 112 | } 113 | enum keyed-md5 { 114 | value 2; 115 | description "Keyed MD5"; 116 | } 117 | enum meticulous-keyed-md5 { 118 | value 3; 119 | description "Meticulous keyed MD5"; 120 | } 121 | enum keyed-sha1 { 122 | value 4; 123 | description "Keyed SHA1"; 124 | } 125 | enum meticulous-keyed-sha1 { 126 | value 5; 127 | description "Meticulous keyed SHA1"; 128 | } 129 | } 130 | description 131 | "BFD authentication type as defined in RFC 5880, values are 132 | maintained in the 'BFD Authentication Types' IANA registry. 133 | Range is 0 to 255."; 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /assets/yang/iana-routing-types@2018-10-29.yang: -------------------------------------------------------------------------------- 1 | module iana-routing-types { 2 | namespace "urn:ietf:params:xml:ns:yang:iana-routing-types"; 3 | prefix iana-rt-types; 4 | 5 | organization 6 | "IANA"; 7 | contact 8 | "Internet Assigned Numbers Authority 9 | 10 | Postal: ICANN 11 | 12025 Waterfront Drive, Suite 300 12 | Los Angeles, CA 90094-2536 13 | United States of America 14 | Tel: +1 310 301 5800 15 | "; 16 | 17 | description 18 | "This module contains a collection of YANG data types 19 | considered defined by IANA and used for routing 20 | protocols. 21 | 22 | Copyright (c) 2017 IETF Trust and the persons 23 | identified as authors of the code. All rights reserved. 24 | 25 | Redistribution and use in source and binary forms, with or 26 | without modification, is permitted pursuant to, and subject 27 | to the license terms contained in, the Simplified BSD License 28 | set forth in Section 4.c of the IETF Trust's Legal Provisions 29 | Relating to IETF Documents 30 | (https://trustee.ietf.org/license-info). 31 | 32 | This version of this YANG module is part of RFC 8294; see 33 | the RFC itself for full legal notices."; 34 | 35 | revision 2018-10-29 { 36 | description "Added SAFI value 74."; 37 | } 38 | 39 | revision 2017-12-04 { 40 | description "Initial revision."; 41 | reference 42 | "RFC 8294: Common YANG Data Types for the Routing Area. 43 | Section 4."; 44 | } 45 | 46 | 47 | 48 | 49 | /*** Collection of IANA types related to routing ***/ 50 | /*** IANA Address Family enumeration ***/ 51 | 52 | typedef address-family { 53 | type enumeration { 54 | enum ipv4 { 55 | value 1; 56 | description 57 | "IPv4 Address Family."; 58 | } 59 | 60 | enum ipv6 { 61 | value 2; 62 | description 63 | "IPv6 Address Family."; 64 | } 65 | 66 | enum nsap { 67 | value 3; 68 | description 69 | "OSI Network Service Access Point (NSAP) Address Family."; 70 | } 71 | 72 | enum hdlc { 73 | value 4; 74 | description 75 | "High-Level Data Link Control (HDLC) Address Family."; 76 | } 77 | 78 | enum bbn1822 { 79 | value 5; 80 | description 81 | "Bolt, Beranek, and Newman Report 1822 (BBN 1822) 82 | Address Family."; 83 | } 84 | 85 | enum ieee802 { 86 | value 6; 87 | description 88 | "IEEE 802 Committee Address Family 89 | (aka Media Access Control (MAC) address)."; 90 | } 91 | 92 | enum e163 { 93 | value 7; 94 | description 95 | "ITU-T E.163 Address Family."; 96 | } 97 | enum e164 { 98 | value 8; 99 | description 100 | "ITU-T E.164 (Switched Multimegabit Data Service (SMDS), 101 | Frame Relay, ATM) Address Family."; 102 | } 103 | 104 | enum f69 { 105 | value 9; 106 | description 107 | "ITU-T F.69 (Telex) Address Family."; 108 | } 109 | 110 | enum x121 { 111 | value 10; 112 | description 113 | "ITU-T X.121 (X.25, Frame Relay) Address Family."; 114 | } 115 | 116 | enum ipx { 117 | value 11; 118 | description 119 | "Novell Internetwork Packet Exchange (IPX) 120 | Address Family."; 121 | } 122 | 123 | enum appletalk { 124 | value 12; 125 | description 126 | "Apple AppleTalk Address Family."; 127 | } 128 | 129 | enum decnet-iv { 130 | value 13; 131 | description 132 | "Digital Equipment DECnet Phase IV Address Family."; 133 | } 134 | 135 | enum vines { 136 | value 14; 137 | description 138 | "Banyan Vines Address Family."; 139 | } 140 | 141 | 142 | 143 | 144 | 145 | enum e164-nsap { 146 | value 15; 147 | description 148 | "ITU-T E.164 with NSAP sub-address Address Family."; 149 | } 150 | 151 | enum dns { 152 | value 16; 153 | description 154 | "Domain Name System (DNS) Address Family."; 155 | } 156 | 157 | enum distinguished-name { 158 | value 17; 159 | description 160 | "Distinguished Name Address Family."; 161 | } 162 | 163 | enum as-num { 164 | value 18; 165 | description 166 | "Autonomous System (AS) Number Address Family."; 167 | } 168 | 169 | enum xtp-v4 { 170 | value 19; 171 | description 172 | "Xpress Transport Protocol (XTP) over IPv4 173 | Address Family."; 174 | } 175 | 176 | enum xtp-v6 { 177 | value 20; 178 | description 179 | "XTP over IPv6 Address Family."; 180 | } 181 | 182 | enum xtp-native { 183 | value 21; 184 | description 185 | "XTP native mode Address Family."; 186 | } 187 | 188 | enum fc-port { 189 | value 22; 190 | description 191 | "Fibre Channel (FC) World-Wide Port Name Address Family."; 192 | } 193 | enum fc-node { 194 | value 23; 195 | description 196 | "FC World-Wide Node Name Address Family."; 197 | } 198 | 199 | enum gwid { 200 | value 24; 201 | description 202 | "ATM Gateway Identifier (GWID) Number Address Family."; 203 | } 204 | 205 | enum l2vpn { 206 | value 25; 207 | description 208 | "Layer 2 VPN (L2VPN) Address Family."; 209 | } 210 | 211 | enum mpls-tp-section-eid { 212 | value 26; 213 | description 214 | "MPLS Transport Profile (MPLS-TP) Section Endpoint 215 | Identifier Address Family."; 216 | } 217 | 218 | enum mpls-tp-lsp-eid { 219 | value 27; 220 | description 221 | "MPLS-TP Label Switched Path (LSP) Endpoint Identifier 222 | Address Family."; 223 | } 224 | 225 | enum mpls-tp-pwe-eid { 226 | value 28; 227 | description 228 | "MPLS-TP Pseudowire Endpoint Identifier Address Family."; 229 | } 230 | 231 | enum mt-v4 { 232 | value 29; 233 | description 234 | "Multi-Topology IPv4 Address Family."; 235 | } 236 | 237 | 238 | 239 | 240 | 241 | enum mt-v6 { 242 | value 30; 243 | description 244 | "Multi-Topology IPv6 Address Family."; 245 | } 246 | 247 | enum eigrp-common-sf { 248 | value 16384; 249 | description 250 | "Enhanced Interior Gateway Routing Protocol (EIGRP) 251 | Common Service Family Address Family."; 252 | } 253 | 254 | enum eigrp-v4-sf { 255 | value 16385; 256 | description 257 | "EIGRP IPv4 Service Family Address Family."; 258 | } 259 | 260 | enum eigrp-v6-sf { 261 | value 16386; 262 | description 263 | "EIGRP IPv6 Service Family Address Family."; 264 | } 265 | 266 | enum lcaf { 267 | value 16387; 268 | description 269 | "Locator/ID Separation Protocol (LISP) 270 | Canonical Address Format (LCAF) Address Family."; 271 | } 272 | 273 | enum bgp-ls { 274 | value 16388; 275 | description 276 | "Border Gateway Protocol - Link State (BGP-LS) 277 | Address Family."; 278 | } 279 | 280 | enum mac-48 { 281 | value 16389; 282 | description 283 | "IEEE 48-bit MAC Address Family."; 284 | } 285 | 286 | 287 | 288 | 289 | enum mac-64 { 290 | value 16390; 291 | description 292 | "IEEE 64-bit MAC Address Family."; 293 | } 294 | 295 | enum trill-oui { 296 | value 16391; 297 | description 298 | "Transparent Interconnection of Lots of Links (TRILL) 299 | IEEE Organizationally Unique Identifier (OUI) 300 | Address Family."; 301 | } 302 | 303 | enum trill-mac-24 { 304 | value 16392; 305 | description 306 | "TRILL final 3 octets of 48-bit MAC Address Family."; 307 | } 308 | 309 | enum trill-mac-40 { 310 | value 16393; 311 | description 312 | "TRILL final 5 octets of 64-bit MAC Address Family."; 313 | } 314 | 315 | enum ipv6-64 { 316 | value 16394; 317 | description 318 | "First 8 octets (64 bits) of IPv6 address 319 | Address Family."; 320 | } 321 | 322 | enum trill-rbridge-port-id { 323 | value 16395; 324 | description 325 | "TRILL Routing Bridge (RBridge) Port ID Address Family."; 326 | } 327 | 328 | enum trill-nickname { 329 | value 16396; 330 | description 331 | "TRILL Nickname Address Family."; 332 | } 333 | } 334 | 335 | 336 | 337 | description 338 | "Enumeration containing all the IANA-defined 339 | Address Families."; 340 | 341 | } 342 | 343 | /*** Subsequent Address Family Identifiers (SAFIs) ***/ 344 | /*** for multiprotocol BGP enumeration ***/ 345 | 346 | typedef bgp-safi { 347 | type enumeration { 348 | enum unicast-safi { 349 | value 1; 350 | description 351 | "Unicast SAFI."; 352 | } 353 | 354 | enum multicast-safi { 355 | value 2; 356 | description 357 | "Multicast SAFI."; 358 | } 359 | 360 | enum labeled-unicast-safi { 361 | value 4; 362 | description 363 | "Labeled Unicast SAFI."; 364 | } 365 | 366 | enum multicast-vpn-safi { 367 | value 5; 368 | description 369 | "Multicast VPN SAFI."; 370 | } 371 | 372 | enum pseudowire-safi { 373 | value 6; 374 | description 375 | "Multi-segment Pseudowire VPN SAFI."; 376 | } 377 | 378 | enum tunnel-encap-safi { 379 | value 7; 380 | description 381 | "Tunnel Encap SAFI."; 382 | } 383 | 384 | 385 | enum mcast-vpls-safi { 386 | value 8; 387 | description 388 | "Multicast Virtual Private LAN Service (VPLS) SAFI."; 389 | } 390 | 391 | enum tunnel-safi { 392 | value 64; 393 | description 394 | "Tunnel SAFI."; 395 | } 396 | 397 | enum vpls-safi { 398 | value 65; 399 | description 400 | "VPLS SAFI."; 401 | } 402 | 403 | enum mdt-safi { 404 | value 66; 405 | description 406 | "Multicast Distribution Tree (MDT) SAFI."; 407 | } 408 | 409 | enum v4-over-v6-safi { 410 | value 67; 411 | description 412 | "IPv4 over IPv6 SAFI."; 413 | } 414 | 415 | enum v6-over-v4-safi { 416 | value 68; 417 | description 418 | "IPv6 over IPv4 SAFI."; 419 | } 420 | 421 | enum l1-vpn-auto-discovery-safi { 422 | value 69; 423 | description 424 | "Layer 1 VPN Auto-Discovery SAFI."; 425 | } 426 | 427 | enum evpn-safi { 428 | value 70; 429 | description 430 | "Ethernet VPN (EVPN) SAFI."; 431 | } 432 | 433 | enum bgp-ls-safi { 434 | value 71; 435 | description 436 | "BGP-LS SAFI."; 437 | } 438 | 439 | enum bgp-ls-vpn-safi { 440 | value 72; 441 | description 442 | "BGP-LS VPN SAFI."; 443 | } 444 | 445 | enum sr-te-safi { 446 | value 73; 447 | description 448 | "Segment Routing - Traffic Engineering (SR-TE) SAFI."; 449 | } 450 | 451 | enum sd-wan-capabilities-safi { 452 | value 74; 453 | description 454 | "SD-WAN Capabilities SAFI."; 455 | } 456 | 457 | enum labeled-vpn-safi { 458 | value 128; 459 | description 460 | "MPLS Labeled VPN SAFI."; 461 | } 462 | 463 | enum multicast-mpls-vpn-safi { 464 | value 129; 465 | description 466 | "Multicast for BGP/MPLS IP VPN SAFI."; 467 | } 468 | 469 | enum route-target-safi { 470 | value 132; 471 | description 472 | "Route Target SAFI."; 473 | } 474 | 475 | enum ipv4-flow-spec-safi { 476 | value 133; 477 | description 478 | "IPv4 Flow Specification SAFI."; 479 | } 480 | 481 | enum vpnv4-flow-spec-safi { 482 | value 134; 483 | description 484 | "IPv4 VPN Flow Specification SAFI."; 485 | } 486 | 487 | enum vpn-auto-discovery-safi { 488 | value 140; 489 | description 490 | "VPN Auto-Discovery SAFI."; 491 | } 492 | } 493 | description 494 | "Enumeration for BGP SAFI."; 495 | reference 496 | "RFC 4760: Multiprotocol Extensions for BGP-4."; 497 | } 498 | } 499 | -------------------------------------------------------------------------------- /assets/yang/ietf-bfd-types@2018-08-01.yang: -------------------------------------------------------------------------------- 1 | module ietf-bfd-types { 2 | 3 | yang-version 1.1; 4 | 5 | namespace "urn:ietf:params:xml:ns:yang:ietf-bfd-types"; 6 | 7 | prefix "bfd-types"; 8 | 9 | // RFC Ed.: replace occurences of XXXX with actual RFC number and 10 | // remove this note 11 | 12 | import iana-bfd-types { 13 | prefix "iana-bfd-types"; 14 | reference "RFC XXXX: YANG Data Model for BFD"; 15 | } 16 | 17 | import ietf-inet-types { 18 | prefix "inet"; 19 | reference "RFC 6991: Common YANG Data Types"; 20 | } 21 | 22 | import ietf-yang-types { 23 | prefix "yang"; 24 | reference "RFC 6991: Common YANG Data Types"; 25 | } 26 | 27 | import ietf-routing { 28 | prefix "rt"; 29 | reference 30 | "RFC 8349: A YANG Data Model for Routing Management 31 | (NMDA version)"; 32 | } 33 | 34 | import ietf-key-chain { 35 | prefix "kc"; 36 | reference "RFC 8177: YANG Data Model for Key Chains"; 37 | } 38 | 39 | organization "IETF BFD Working Group"; 40 | 41 | contact 42 | "WG Web: 43 | WG List: 44 | 45 | Editors: Reshad Rahman (rrahman@cisco.com), 46 | Lianshu Zheng (vero.zheng@huawei.com), 47 | Mahesh Jethanandani (mjethanandani@gmail.com)"; 48 | 49 | description 50 | "This module contains a collection of BFD specific YANG data type 51 | definitions, as per RFC 5880, and also groupings which are common 52 | to other BFD YANG modules. 53 | 54 | Copyright (c) 2018 IETF Trust and the persons 55 | identified as authors of the code. All rights reserved. 56 | 57 | Redistribution and use in source and binary forms, with or 58 | without modification, is permitted pursuant to, and subject 59 | to the license terms contained in, the Simplified BSD License 60 | set forth in Section 4.c of the IETF Trust's Legal Provisions 61 | Relating to IETF Documents 62 | (http://trustee.ietf.org/license-info). 63 | 64 | This version of this YANG module is part of RFC XXXX; see 65 | the RFC itself for full legal notices."; 66 | 67 | reference "RFC XXXX"; 68 | 69 | revision 2018-08-01 { 70 | description "Initial revision."; 71 | reference "RFC XXXX: YANG Data Model for BFD"; 72 | } 73 | /* 74 | * Feature definitions 75 | */ 76 | feature single-minimum-interval { 77 | description 78 | "This feature indicates that the server supports configuration 79 | of one minimum interval value which is used for both transmit and 80 | receive minimum intervals."; 81 | } 82 | 83 | feature authentication { 84 | description 85 | "This feature indicates that the server supports BFD 86 | authentication."; 87 | reference 88 | "RFC 5880: Bidirectional Forwarding Detection (BFD), 89 | section 6.7."; 90 | } 91 | 92 | feature demand-mode { 93 | description 94 | "This feature indicates that the server supports BFD demand 95 | mode."; 96 | reference 97 | "RFC 5880: Bidirectional Forwarding Detection (BFD), 98 | section 6.6."; 99 | } 100 | 101 | feature echo-mode { 102 | description 103 | "This feature indicates that the server supports BFD echo 104 | mode."; 105 | reference 106 | "RFC 5880: Bidirectional Forwarding Detection (BFD), 107 | section 6.4."; 108 | } 109 | 110 | /* 111 | * Identity definitions 112 | */ 113 | identity bfdv1 { 114 | base "rt:control-plane-protocol"; 115 | description "BFD protocol version 1."; 116 | reference 117 | "RFC 5880: Bidirectional Forwarding Detection (BFD)."; 118 | } 119 | 120 | identity path-type { 121 | description 122 | "Base identity for BFD path type. The path type indicates 123 | the type of path on which BFD is running."; 124 | } 125 | identity path-ip-sh { 126 | base path-type; 127 | description "BFD on IP single hop."; 128 | reference 129 | "RFC 5881: Bidirectional Forwarding Detection (BFD) 130 | for IPv4 and IPv6 (Single Hop)."; 131 | } 132 | identity path-ip-mh { 133 | base path-type; 134 | description "BFD on IP multihop paths."; 135 | reference 136 | "RFC 5883: Bidirectional Forwarding Detection (BFD) for 137 | Multihop Paths."; 138 | } 139 | identity path-mpls-te { 140 | base path-type; 141 | description 142 | "BFD on MPLS Traffic Engineering."; 143 | reference 144 | "RFC 5884: Bidirectional Forwarding Detection (BFD) 145 | for MPLS Label Switched Paths (LSPs)."; 146 | } 147 | identity path-mpls-lsp { 148 | base path-type; 149 | description 150 | "BFD on MPLS Label Switched Path."; 151 | reference 152 | "RFC 5884: Bidirectional Forwarding Detection (BFD) 153 | for MPLS Label Switched Paths (LSPs)."; 154 | } 155 | identity path-lag { 156 | base path-type; 157 | description 158 | "Micro-BFD on LAG member links."; 159 | reference 160 | "RFC 7130: Bidirectional Forwarding Detection (BFD) on 161 | Link Aggregation Group (LAG) Interfaces."; 162 | } 163 | 164 | identity encap-type { 165 | description 166 | "Base identity for BFD encapsulation type."; 167 | } 168 | identity encap-ip { 169 | base encap-type; 170 | description "BFD with IP encapsulation."; 171 | } 172 | 173 | /* 174 | * Type Definitions 175 | */ 176 | typedef discriminator { 177 | type uint32; 178 | description "BFD discriminator as described in RFC 5880."; 179 | } 180 | 181 | typedef state { 182 | type enumeration { 183 | enum adminDown { 184 | value 0; 185 | description "admindown"; 186 | } 187 | enum down { 188 | value 1; 189 | description "down"; 190 | } 191 | enum init { 192 | value 2; 193 | description "init"; 194 | } 195 | enum up { 196 | value 3; 197 | description "up"; 198 | } 199 | } 200 | description "BFD state as defined in RFC 5880."; 201 | } 202 | 203 | typedef multiplier { 204 | type uint8 { 205 | range 1..255; 206 | } 207 | description "BFD multiplier as described in RFC 5880."; 208 | } 209 | 210 | typedef hops { 211 | type uint8 { 212 | range 1..255; 213 | } 214 | description 215 | "This corresponds to Time To Live for IPv4 and corresponds to hop 216 | limit for IPv6."; 217 | } 218 | 219 | /* 220 | * Groupings 221 | */ 222 | grouping auth-parms { 223 | description 224 | "Grouping for BFD authentication parameters 225 | (see section 6.7 of RFC 5880)."; 226 | container authentication { 227 | if-feature authentication; 228 | presence 229 | "Enables BFD authentication (see section 6.7 of RFC 5880)."; 230 | description "Parameters for BFD authentication."; 231 | 232 | leaf key-chain { 233 | type kc:key-chain-ref; 234 | description "Name of the key-chain as per RFC 8177."; 235 | } 236 | 237 | leaf meticulous { 238 | type boolean; 239 | description 240 | "Enables meticulous mode as described in section 6.7 " + 241 | "of RFC 5880."; 242 | } 243 | } 244 | } 245 | 246 | grouping base-cfg-parms { 247 | description "BFD grouping for base config parameters."; 248 | leaf local-multiplier { 249 | type multiplier; 250 | default 3; 251 | description "Multiplier transmitted by local system."; 252 | } 253 | 254 | choice interval-config-type { 255 | description 256 | "Two interval values or one value used for both transmit and 257 | receive."; 258 | case tx-rx-intervals { 259 | leaf desired-min-tx-interval { 260 | type uint32; 261 | units microseconds; 262 | default 1000000; 263 | description 264 | "Desired minimum transmit interval of control packets."; 265 | } 266 | 267 | leaf required-min-rx-interval { 268 | type uint32; 269 | units microseconds; 270 | default 1000000; 271 | description 272 | "Required minimum receive interval of control packets."; 273 | } 274 | } 275 | case single-interval { 276 | if-feature single-minimum-interval; 277 | 278 | leaf min-interval { 279 | type uint32; 280 | units microseconds; 281 | default 1000000; 282 | description 283 | "Desired minimum transmit interval and required " + 284 | "minimum receive interval of control packets."; 285 | } 286 | } 287 | } 288 | } 289 | 290 | grouping client-cfg-parms { 291 | description 292 | "BFD grouping for configuration parameters 293 | used by clients of BFD, e.g. IGP or MPLS."; 294 | 295 | leaf enable { 296 | type boolean; 297 | default false; 298 | description 299 | "Indicates whether the BFD is enabled."; 300 | } 301 | uses base-cfg-parms; 302 | } 303 | 304 | grouping common-cfg-parms { 305 | description 306 | "BFD grouping for common configuration parameters."; 307 | 308 | uses base-cfg-parms; 309 | 310 | leaf demand-enabled { 311 | if-feature demand-mode; 312 | type boolean; 313 | default false; 314 | description 315 | "To enable demand mode."; 316 | } 317 | 318 | leaf admin-down { 319 | type boolean; 320 | default false; 321 | description 322 | "Is the BFD session administratively down."; 323 | } 324 | uses auth-parms; 325 | } 326 | 327 | grouping all-session { 328 | description "BFD session operational information"; 329 | leaf path-type { 330 | type identityref { 331 | base path-type; 332 | } 333 | config "false"; 334 | description 335 | "BFD path type, this indicates the path type that BFD is 336 | running on."; 337 | } 338 | leaf ip-encapsulation { 339 | type boolean; 340 | config "false"; 341 | description "Whether BFD encapsulation uses IP."; 342 | } 343 | leaf local-discriminator { 344 | type discriminator; 345 | config "false"; 346 | description "Local discriminator."; 347 | } 348 | leaf remote-discriminator { 349 | type discriminator; 350 | config "false"; 351 | description "Remote discriminator."; 352 | } 353 | leaf remote-multiplier { 354 | type multiplier; 355 | config "false"; 356 | description "Remote multiplier."; 357 | } 358 | leaf demand-capability { 359 | if-feature demand-mode; 360 | type boolean; 361 | config "false"; 362 | description "Local demand mode capability."; 363 | } 364 | leaf source-port { 365 | when "../ip-encapsulation = 'true'" { 366 | description 367 | "Source port valid only when IP encapsulation is used."; 368 | } 369 | type inet:port-number; 370 | config "false"; 371 | description "Source UDP port"; 372 | } 373 | leaf dest-port { 374 | when "../ip-encapsulation = 'true'" { 375 | description 376 | "Destination port valid only when IP encapsulation is used."; 377 | } 378 | type inet:port-number; 379 | config "false"; 380 | description "Destination UDP port."; 381 | } 382 | 383 | container session-running { 384 | config "false"; 385 | description "BFD session running information."; 386 | leaf session-index { 387 | type uint32; 388 | description 389 | "An index used to uniquely identify BFD sessions."; 390 | } 391 | leaf local-state { 392 | type state; 393 | description "Local state."; 394 | } 395 | leaf remote-state { 396 | type state; 397 | description "Remote state."; 398 | } 399 | leaf local-diagnostic { 400 | type iana-bfd-types:diagnostic; 401 | description "Local diagnostic."; 402 | } 403 | leaf remote-diagnostic { 404 | type iana-bfd-types:diagnostic; 405 | description "Remote diagnostic."; 406 | } 407 | leaf remote-authenticated { 408 | type boolean; 409 | description 410 | "Indicates whether incoming BFD control packets are 411 | authenticated."; 412 | } 413 | leaf remote-authentication-type { 414 | when "../remote-authenticated = 'true'" { 415 | description 416 | "Only valid when incoming BFD control packets are 417 | authenticated."; 418 | } 419 | if-feature authentication; 420 | type iana-bfd-types:auth-type; 421 | description 422 | "Authentication type of incoming BFD control packets."; 423 | } 424 | leaf detection-mode { 425 | type enumeration { 426 | enum async-with-echo { 427 | value "1"; 428 | description "Async with echo."; 429 | } 430 | enum async-without-echo { 431 | value "2"; 432 | description "Async without echo."; 433 | } 434 | enum demand-with-echo { 435 | value "3"; 436 | description "Demand with echo."; 437 | } 438 | enum demand-without-echo { 439 | value "4"; 440 | description "Demand without echo."; 441 | } 442 | } 443 | description "Detection mode."; 444 | } 445 | leaf negotiated-tx-interval { 446 | type uint32; 447 | units microseconds; 448 | description "Negotiated transmit interval."; 449 | } 450 | leaf negotiated-rx-interval { 451 | type uint32; 452 | units microseconds; 453 | description "Negotiated receive interval."; 454 | } 455 | leaf detection-time { 456 | type uint32; 457 | units microseconds; 458 | description "Detection time."; 459 | } 460 | leaf echo-tx-interval-in-use { 461 | when "../../path-type = 'bfd-types:path-ip-sh'" { 462 | description 463 | "Echo is supported for IP single-hop only."; 464 | } 465 | if-feature echo-mode; 466 | type uint32; 467 | units microseconds; 468 | description "Echo transmit interval in use."; 469 | } 470 | } 471 | 472 | container session-statistics { 473 | config "false"; 474 | description "BFD per-session statistics."; 475 | 476 | leaf create-time { 477 | type yang:date-and-time; 478 | description 479 | "Time and date when this session was created."; 480 | } 481 | leaf last-down-time { 482 | type yang:date-and-time; 483 | description 484 | "Time and date of last time this session went down."; 485 | } 486 | leaf last-up-time { 487 | type yang:date-and-time; 488 | description 489 | "Time and date of last time this session went up."; 490 | } 491 | leaf down-count { 492 | type yang:counter32; 493 | description 494 | "The number of times this session has transitioned in the 495 | down state."; 496 | } 497 | leaf admin-down-count { 498 | type yang:counter32; 499 | description 500 | "The number of times this session has transitioned in the 501 | admin-down state."; 502 | } 503 | leaf receive-packet-count { 504 | type yang:counter64; 505 | description 506 | "Count of received packets in this session. This includes 507 | valid and invalid received packets."; 508 | } 509 | leaf send-packet-count { 510 | type yang:counter64; 511 | description "Count of sent packets in this session."; 512 | } 513 | leaf receive-invalid-packet-count { 514 | type yang:counter64; 515 | description 516 | "Count of invalid received packets in this session."; 517 | } 518 | leaf send-failed-packet-count { 519 | type yang:counter64; 520 | description 521 | "Count of packets which failed to be sent in this session."; 522 | } 523 | } 524 | } 525 | 526 | grouping session-statistics-summary { 527 | description "Grouping for session statistics summary."; 528 | container summary { 529 | config false; 530 | description "BFD session statistics summary."; 531 | leaf number-of-sessions { 532 | type yang:gauge32; 533 | description "Number of BFD sessions."; 534 | } 535 | leaf number-of-sessions-up { 536 | type yang:gauge32; 537 | description 538 | "Number of BFD sessions currently in up state (as defined 539 | in RFC 5880)."; 540 | } 541 | leaf number-of-sessions-down { 542 | type yang:gauge32; 543 | description 544 | "Number of BFD sessions currently in down or init state 545 | but not admin-down (as defined in RFC 5880)."; 546 | } 547 | leaf number-of-sessions-admin-down { 548 | type yang:gauge32; 549 | description 550 | "Number of BFD sessions currently in admin-down state (as 551 | defined in RFC 5880)."; 552 | } 553 | } 554 | } 555 | 556 | grouping notification-parms { 557 | description 558 | "This group describes common parameters that will be sent " + 559 | "as part of BFD notification."; 560 | 561 | leaf local-discr { 562 | type discriminator; 563 | description "BFD local discriminator."; 564 | } 565 | 566 | leaf remote-discr { 567 | type discriminator; 568 | description "BFD remote discriminator."; 569 | } 570 | 571 | leaf new-state { 572 | type state; 573 | description "Current BFD state."; 574 | } 575 | 576 | leaf state-change-reason { 577 | type iana-bfd-types:diagnostic; 578 | description "BFD state change reason."; 579 | } 580 | 581 | leaf time-of-last-state-change { 582 | type yang:date-and-time; 583 | description 584 | "Calendar time of previous state change."; 585 | } 586 | 587 | leaf dest-addr { 588 | type inet:ip-address; 589 | description "BFD peer address."; 590 | } 591 | 592 | leaf source-addr { 593 | type inet:ip-address; 594 | description "BFD local address."; 595 | } 596 | 597 | leaf session-index { 598 | type uint32; 599 | description "An index used to uniquely identify BFD sessions."; 600 | } 601 | leaf path-type { 602 | type identityref { 603 | base path-type; 604 | } 605 | description "BFD path type."; 606 | } 607 | } 608 | } 609 | -------------------------------------------------------------------------------- /assets/yang/ietf-ipv4-unicast-routing@2018-03-13.yang: -------------------------------------------------------------------------------- 1 | module ietf-ipv4-unicast-routing { 2 | yang-version "1.1"; 3 | namespace 4 | "urn:ietf:params:xml:ns:yang:ietf-ipv4-unicast-routing"; 5 | prefix "v4ur"; 6 | 7 | import ietf-routing { 8 | prefix "rt"; 9 | description 10 | "An 'ietf-routing' module version that is compatible with 11 | the Network Management Datastore Architecture (NMDA) 12 | is required."; 13 | } 14 | 15 | import ietf-inet-types { 16 | prefix "inet"; 17 | } 18 | organization 19 | "IETF NETMOD (Network Modeling) Working Group"; 20 | contact 21 | "WG Web: 22 | WG List: 23 | 24 | Editor: Ladislav Lhotka 25 | 26 | Acee Lindem 27 | 28 | Yingzhen Qu 29 | "; 30 | 31 | description 32 | "This YANG module augments the 'ietf-routing' module with basic 33 | parameters for IPv4 unicast routing. The model fully conforms 34 | to the Network Management Datastore Architecture (NMDA). 35 | 36 | Copyright (c) 2018 IETF Trust and the persons 37 | identified as authors of the code. All rights reserved. 38 | 39 | Redistribution and use in source and binary forms, with or 40 | without modification, is permitted pursuant to, and subject 41 | to the license terms contained in, the Simplified BSD License 42 | set forth in Section 4.c of the IETF Trust's Legal Provisions 43 | Relating to IETF Documents 44 | (https://trustee.ietf.org/license-info). 45 | 46 | This version of this YANG module is part of RFC 8349; see 47 | the RFC itself for full legal notices."; 48 | 49 | revision 2018-03-13 { 50 | description 51 | "Network Management Datastore Architecture (NMDA) revision."; 52 | reference 53 | "RFC 8349: A YANG Data Model for Routing Management 54 | (NMDA Version)"; 55 | } 56 | 57 | revision 2016-11-04 { 58 | description 59 | "Initial revision."; 60 | reference 61 | "RFC 8022: A YANG Data Model for Routing Management"; 62 | } 63 | 64 | /* Identities */ 65 | 66 | identity ipv4-unicast { 67 | base rt:ipv4; 68 | description 69 | "This identity represents the IPv4 unicast address family."; 70 | } 71 | 72 | augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route" { 73 | when "derived-from-or-self(../../rt:address-family, " 74 | + "'v4ur:ipv4-unicast')" { 75 | description 76 | "This augment is valid only for IPv4 unicast."; 77 | } 78 | description 79 | "This leaf augments an IPv4 unicast route."; 80 | leaf destination-prefix { 81 | type inet:ipv4-prefix; 82 | description 83 | "IPv4 destination prefix."; 84 | } 85 | } 86 | 87 | augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/" 88 | + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { 89 | when "derived-from-or-self(../../../rt:address-family, " 90 | + "'v4ur:ipv4-unicast')" { 91 | description 92 | "This augment is valid only for IPv4 unicast."; 93 | } 94 | description 95 | "Augments the 'simple-next-hop' case in IPv4 unicast routes."; 96 | leaf next-hop-address { 97 | type inet:ipv4-address; 98 | description 99 | "IPv4 address of the next hop."; 100 | } 101 | } 102 | 103 | augment "/rt:routing/rt:ribs/rt:rib/rt:routes/rt:route/" 104 | + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" 105 | + "rt:next-hop-list/rt:next-hop" { 106 | when "derived-from-or-self(../../../../../rt:address-family, " 107 | + "'v4ur:ipv4-unicast')" { 108 | description 109 | "This augment is valid only for IPv4 unicast."; 110 | } 111 | description 112 | "This leaf augments the 'next-hop-list' case of IPv4 unicast 113 | routes."; 114 | leaf address { 115 | type inet:ipv4-address; 116 | description 117 | "IPv4 address of the next hop."; 118 | } 119 | } 120 | 121 | augment 122 | "/rt:routing/rt:ribs/rt:rib/rt:active-route/rt:input" { 123 | when "derived-from-or-self(../rt:address-family, " 124 | + "'v4ur:ipv4-unicast')" { 125 | description 126 | "This augment is valid only for IPv4 unicast RIBs."; 127 | } 128 | description 129 | "This augment adds the input parameter of the 'active-route' 130 | action."; 131 | leaf destination-address { 132 | type inet:ipv4-address; 133 | description 134 | "IPv4 destination address."; 135 | } 136 | } 137 | 138 | augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" 139 | + "rt:output/rt:route" { 140 | when "derived-from-or-self(../../rt:address-family, " 141 | + "'v4ur:ipv4-unicast')" { 142 | description 143 | "This augment is valid only for IPv4 unicast."; 144 | } 145 | description 146 | "This augment adds the destination prefix to the reply of the 147 | 'active-route' action."; 148 | leaf destination-prefix { 149 | type inet:ipv4-prefix; 150 | description 151 | "IPv4 destination prefix."; 152 | } 153 | } 154 | 155 | augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" 156 | + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" 157 | + "rt:simple-next-hop" { 158 | when "derived-from-or-self(../../../rt:address-family, " 159 | + "'v4ur:ipv4-unicast')" { 160 | description 161 | "This augment is valid only for IPv4 unicast."; 162 | } 163 | description 164 | "Augments the 'simple-next-hop' case in the reply to the 165 | 'active-route' action."; 166 | leaf next-hop-address { 167 | type inet:ipv4-address; 168 | description 169 | "IPv4 address of the next hop."; 170 | } 171 | } 172 | 173 | augment "/rt:routing/rt:ribs/rt:rib/rt:active-route/" 174 | + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" 175 | + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { 176 | when "derived-from-or-self(../../../../../rt:address-family, " 177 | + "'v4ur:ipv4-unicast')" { 178 | description 179 | "This augment is valid only for IPv4 unicast."; 180 | } 181 | description 182 | "Augments the 'next-hop-list' case in the reply to the 183 | 'active-route' action."; 184 | leaf next-hop-address { 185 | type inet:ipv4-address; 186 | description 187 | "IPv4 address of the next hop."; 188 | } 189 | } 190 | 191 | augment "/rt:routing/rt:control-plane-protocols/" 192 | + "rt:control-plane-protocol/rt:static-routes" { 193 | description 194 | "This augment defines the 'static' pseudo-protocol 195 | with data specific to IPv4 unicast."; 196 | container ipv4 { 197 | description 198 | "Support for a 'static' pseudo-protocol instance 199 | consists of a list of routes."; 200 | list route { 201 | key "destination-prefix"; 202 | description 203 | "A list of static routes."; 204 | leaf destination-prefix { 205 | type inet:ipv4-prefix; 206 | mandatory true; 207 | description 208 | "IPv4 destination prefix."; 209 | } 210 | leaf description { 211 | type string; 212 | description 213 | "Textual description of the route."; 214 | } 215 | container next-hop { 216 | description 217 | "Support for next-hop."; 218 | uses rt:next-hop-content { 219 | augment "next-hop-options/simple-next-hop" { 220 | description 221 | "Augments the 'simple-next-hop' case in IPv4 static 222 | routes."; 223 | leaf next-hop-address { 224 | type inet:ipv4-address; 225 | description 226 | "IPv4 address of the next hop."; 227 | } 228 | } 229 | augment "next-hop-options/next-hop-list/next-hop-list/" 230 | + "next-hop" { 231 | description 232 | "Augments the 'next-hop-list' case in IPv4 static 233 | routes."; 234 | leaf next-hop-address { 235 | type inet:ipv4-address; 236 | description 237 | "IPv4 address of the next hop."; 238 | } 239 | } 240 | } 241 | } 242 | } 243 | } 244 | } 245 | 246 | /* 247 | * The subsequent data nodes are obviated and obsoleted 248 | * by the Network Management Datastore Architecture 249 | * as described in RFC 8342. 250 | */ 251 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route" { 252 | when "derived-from-or-self(../../rt:address-family, " 253 | + "'v4ur:ipv4-unicast')" { 254 | description 255 | "This augment is valid only for IPv4 unicast."; 256 | } 257 | status obsolete; 258 | description 259 | "This leaf augments an IPv4 unicast route."; 260 | leaf destination-prefix { 261 | type inet:ipv4-prefix; 262 | status obsolete; 263 | description 264 | "IPv4 destination prefix."; 265 | } 266 | } 267 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" 268 | + "rt:next-hop/rt:next-hop-options/rt:simple-next-hop" { 269 | when "derived-from-or-self( 270 | ../../../rt:address-family, 'v4ur:ipv4-unicast')" { 271 | description 272 | "This augment is valid only for IPv4 unicast."; 273 | } 274 | status obsolete; 275 | description 276 | "Augments the 'simple-next-hop' case in IPv4 unicast routes."; 277 | leaf next-hop-address { 278 | type inet:ipv4-address; 279 | status obsolete; 280 | description 281 | "IPv4 address of the next hop."; 282 | } 283 | } 284 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:routes/rt:route/" 285 | + "rt:next-hop/rt:next-hop-options/rt:next-hop-list/" 286 | + "rt:next-hop-list/rt:next-hop" { 287 | when "derived-from-or-self(../../../../../rt:address-family, 288 | 'v4ur:ipv4-unicast')" { 289 | description 290 | "This augment is valid only for IPv4 unicast."; 291 | } 292 | status obsolete; 293 | description 294 | "This leaf augments the 'next-hop-list' case of IPv4 unicast 295 | routes."; 296 | leaf address { 297 | type inet:ipv4-address; 298 | status obsolete; 299 | description 300 | "IPv4 address of the next hop."; 301 | } 302 | } 303 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" 304 | + "rt:input" { 305 | when "derived-from-or-self(../rt:address-family, 306 | 'v4ur:ipv4-unicast')" { 307 | description 308 | "This augment is valid only for IPv4 unicast RIBs."; 309 | } 310 | status obsolete; 311 | description 312 | "This augment adds the input parameter of the 'active-route' 313 | action."; 314 | leaf destination-address { 315 | type inet:ipv4-address; 316 | status obsolete; 317 | description 318 | "IPv4 destination address."; 319 | } 320 | } 321 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" 322 | + "rt:output/rt:route" { 323 | when "derived-from-or-self(../../rt:address-family, 324 | 'v4ur:ipv4-unicast')" { 325 | description 326 | "This augment is valid only for IPv4 unicast."; 327 | } 328 | status obsolete; 329 | description 330 | "This augment adds the destination prefix to the reply of the 331 | 'active-route' action."; 332 | leaf destination-prefix { 333 | type inet:ipv4-prefix; 334 | status obsolete; 335 | description 336 | "IPv4 destination prefix."; 337 | } 338 | } 339 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" 340 | + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" 341 | + "rt:simple-next-hop" { 342 | when "derived-from-or-self(../../../rt:address-family, 343 | 'v4ur:ipv4-unicast')" { 344 | description 345 | "This augment is valid only for IPv4 unicast."; 346 | } 347 | status obsolete; 348 | description 349 | "Augments the 'simple-next-hop' case in the reply to the 350 | 'active-route' action."; 351 | leaf next-hop-address { 352 | type inet:ipv4-address; 353 | status obsolete; 354 | description 355 | "IPv4 address of the next hop."; 356 | } 357 | } 358 | augment "/rt:routing-state/rt:ribs/rt:rib/rt:active-route/" 359 | + "rt:output/rt:route/rt:next-hop/rt:next-hop-options/" 360 | + "rt:next-hop-list/rt:next-hop-list/rt:next-hop" { 361 | when "derived-from-or-self(../../../../../rt:address-family, 362 | 'v4ur:ipv4-unicast')" { 363 | description 364 | "This augment is valid only for IPv4 unicast."; 365 | } 366 | status obsolete; 367 | description 368 | "Augments the 'next-hop-list' case in the reply to the 369 | 'active-route' action."; 370 | leaf next-hop-address { 371 | type inet:ipv4-address; 372 | status obsolete; 373 | description 374 | "IPv4 address of the next hop."; 375 | } 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /assets/yang/ietf-key-chain@2017-04-18.yang: -------------------------------------------------------------------------------- 1 | module ietf-key-chain { 2 | yang-version 1.1; 3 | namespace "urn:ietf:params:xml:ns:yang:ietf-key-chain"; 4 | prefix key-chain; 5 | 6 | import ietf-yang-types { 7 | prefix yang; 8 | } 9 | import ietf-netconf-acm { 10 | prefix nacm; 11 | } 12 | 13 | organization 14 | "IETF RTG (Routing) Working Group"; 15 | contact 16 | "Acee Lindem - acee@cisco.com"; 17 | description 18 | "This YANG module defines the generic configuration 19 | data for key-chain. It is intended that the module 20 | will be extended by vendors to define vendor-specific 21 | key-chain configuration parameters. 22 | 23 | Copyright (c) 2015 IETF Trust and the persons identified as 24 | authors of the code. All rights reserved. 25 | 26 | Redistribution and use in source and binary forms, with or 27 | without modification, is permitted pursuant to, and subject 28 | to the license terms contained in, the Simplified BSD License 29 | set forth in Section 4.c of the IETF Trust's Legal Provisions 30 | Relating to IETF Documents 31 | (http://trustee.ietf.org/license-info). 32 | This version of this YANG module is part of RFC XXXX; see 33 | the RFC itself for full legal notices."; 34 | 35 | revision 2017-04-18 { 36 | description 37 | "Initial RFC Revision"; 38 | reference "RFC XXXX: A YANG Data Model for key-chain"; 39 | } 40 | 41 | feature hex-key-string { 42 | description 43 | "Support hexadecimal key string."; 44 | } 45 | 46 | feature accept-tolerance { 47 | description 48 | "To specify the tolerance or acceptance limit."; 49 | } 50 | 51 | feature independent-send-accept-lifetime { 52 | description 53 | "Support for independent send and accept key lifetimes."; 54 | } 55 | 56 | feature crypto-hmac-sha-1-12 { 57 | description 58 | "Support for TCP HMAC-SHA-1 12 byte digest hack."; 59 | } 60 | 61 | feature clear-text { 62 | description 63 | "Support for clear-text algorithm. Usage is 64 | NOT RECOMMENDED."; 65 | } 66 | 67 | feature aes-cmac-prf-128 { 68 | description 69 | "Support for AES Cipher based Message Authentication 70 | Code Pseudo Random Function."; 71 | } 72 | 73 | feature aes-key-wrap { 74 | description 75 | "Support for Advanced Encryption Standard (AES) Key Wrap."; 76 | } 77 | 78 | feature replay-protection-only { 79 | description 80 | "Provide replay-protection without any authentication 81 | as required by protocols such as Bidirectional 82 | Forwarding Detection (BFD)."; 83 | } 84 | identity crypto-algorithm { 85 | description 86 | "Base identity of cryptographic algorithm options."; 87 | } 88 | 89 | identity hmac-sha-1-12 { 90 | base crypto-algorithm; 91 | if-feature "crypto-hmac-sha-1-12"; 92 | description 93 | "The HMAC-SHA1-12 algorithm."; 94 | } 95 | 96 | identity aes-cmac-prf-128 { 97 | base crypto-algorithm; 98 | if-feature "aes-cmac-prf-128"; 99 | description 100 | "The AES-CMAC-PRF-128 algorithm - required by 101 | RFC 5926 for TCP-AO key derivation functions."; 102 | } 103 | 104 | identity md5 { 105 | base crypto-algorithm; 106 | description 107 | "The MD5 algorithm."; 108 | } 109 | 110 | identity sha-1 { 111 | base crypto-algorithm; 112 | description 113 | "The SHA-1 algorithm."; 114 | } 115 | 116 | identity hmac-sha-1 { 117 | base crypto-algorithm; 118 | description 119 | "HMAC-SHA-1 authentication algorithm."; 120 | } 121 | 122 | identity hmac-sha-256 { 123 | base crypto-algorithm; 124 | description 125 | "HMAC-SHA-256 authentication algorithm."; 126 | } 127 | 128 | identity hmac-sha-384 { 129 | base crypto-algorithm; 130 | description 131 | "HMAC-SHA-384 authentication algorithm."; 132 | } 133 | 134 | identity hmac-sha-512 { 135 | base crypto-algorithm; 136 | description 137 | "HMAC-SHA-512 authentication algorithm."; 138 | } 139 | 140 | identity clear-text { 141 | base crypto-algorithm; 142 | if-feature "clear-text"; 143 | description 144 | "Clear text."; 145 | } 146 | 147 | identity replay-protection-only { 148 | base crypto-algorithm; 149 | if-feature "replay-protection-only"; 150 | description 151 | "Provide replay-protection without any authentication as 152 | required by protocols such as Bidirectional Forwarding 153 | Detection (BFD)."; 154 | } 155 | 156 | typedef key-chain-ref { 157 | type leafref { 158 | path 159 | "/key-chain:key-chains/key-chain:key-chain/key-chain:name"; 160 | } 161 | description 162 | "This type is used by data models that need to reference 163 | configured key-chains."; 164 | } 165 | 166 | grouping lifetime { 167 | description 168 | "Key lifetime specification."; 169 | choice lifetime { 170 | default "always"; 171 | description 172 | "Options for specifying key accept or send lifetimes"; 173 | case always { 174 | leaf always { 175 | type empty; 176 | description 177 | "Indicates key lifetime is always valid."; 178 | } 179 | } 180 | case start-end-time { 181 | leaf start-date-time { 182 | type yang:date-and-time; 183 | description 184 | "Start time."; 185 | } 186 | choice end-time { 187 | default "infinite"; 188 | description 189 | "End-time setting."; 190 | case infinite { 191 | leaf no-end-time { 192 | type empty; 193 | description 194 | "Indicates key lifetime end-time in infinite."; 195 | } 196 | } 197 | case duration { 198 | leaf duration { 199 | type uint32 { 200 | range "1..2147483646"; 201 | } 202 | units "seconds"; 203 | description 204 | "Key lifetime duration, in seconds"; 205 | } 206 | } 207 | case end-date-time { 208 | leaf end-date-time { 209 | type yang:date-and-time; 210 | description 211 | "End time."; 212 | } 213 | } 214 | } 215 | } 216 | } 217 | } 218 | grouping key-common { 219 | description 220 | "Key-chain key data nodes common to 221 | configuration and state."; 222 | } 223 | 224 | container key-chains { 225 | description 226 | "All configured key-chains on the device."; 227 | list key-chain { 228 | key "name"; 229 | description 230 | "List of key-chains."; 231 | leaf name { 232 | type string; 233 | description 234 | "Name of the key-chain."; 235 | } 236 | leaf description { 237 | type string; 238 | description 239 | "A description of the key-chain"; 240 | } 241 | container accept-tolerance { 242 | if-feature "accept-tolerance"; 243 | description 244 | "Tolerance for key lifetime acceptance (seconds)."; 245 | leaf duration { 246 | type uint32; 247 | units "seconds"; 248 | default "0"; 249 | description 250 | "Tolerance range, in seconds."; 251 | } 252 | } 253 | leaf last-modified-timestamp { 254 | type yang:date-and-time; 255 | config false; 256 | description 257 | "Timestamp of the most recent update to the key-chain"; 258 | } 259 | list key { 260 | key "key-id"; 261 | description 262 | "Single key in key chain."; 263 | leaf key-id { 264 | type uint64; 265 | description 266 | "Numeric value uniquely identifying the key"; 267 | } 268 | container lifetime { 269 | description 270 | "Specify a key's lifetime."; 271 | choice lifetime { 272 | description 273 | "Options for specification of send and accept 274 | lifetimes."; 275 | case send-and-accept-lifetime { 276 | description 277 | "Send and accept key have the same lifetime."; 278 | container send-accept-lifetime { 279 | description 280 | "Single lifetime specification for both 281 | send and accept lifetimes."; 282 | uses lifetime; 283 | } 284 | } 285 | case independent-send-accept-lifetime { 286 | if-feature "independent-send-accept-lifetime"; 287 | description 288 | "Independent send and accept key lifetimes."; 289 | container send-lifetime { 290 | description 291 | "Separate lifetime specification for send 292 | lifetime."; 293 | uses lifetime; 294 | } 295 | container accept-lifetime { 296 | description 297 | "Separate lifetime specification for accept 298 | lifetime."; 299 | uses lifetime; 300 | } 301 | } 302 | } 303 | } 304 | leaf crypto-algorithm { 305 | type identityref { 306 | base crypto-algorithm; 307 | } 308 | mandatory true; 309 | description 310 | "Cryptographic algorithm associated with key."; 311 | } 312 | container key-string { 313 | description 314 | "The key string."; 315 | nacm:default-deny-all; 316 | choice key-string-style { 317 | description 318 | "Key string styles"; 319 | case keystring { 320 | leaf keystring { 321 | type string; 322 | description 323 | "Key string in ASCII format."; 324 | } 325 | } 326 | case hexadecimal { 327 | if-feature "hex-key-string"; 328 | leaf hexadecimal-string { 329 | type yang:hex-string; 330 | description 331 | "Key in hexadecimal string format. When compared 332 | to ASCII, specification in hexadecimal affords 333 | greater key entropy with the same number of 334 | octets. Additionally, it discourages usage of 335 | well-known words or numbers."; 336 | } 337 | } 338 | } 339 | } 340 | leaf send-lifetime-active { 341 | type boolean; 342 | config false; 343 | description 344 | "Indicates if the send lifetime of the 345 | key-chain key is currently active."; 346 | } 347 | leaf accept-lifetime-active { 348 | type boolean; 349 | config false; 350 | description 351 | "Indicates if the accept lifetime of the 352 | key-chain key is currently active."; 353 | } 354 | } 355 | } 356 | container aes-key-wrap { 357 | if-feature "aes-key-wrap"; 358 | description 359 | "AES Key Wrap password encryption."; 360 | leaf enable { 361 | type boolean; 362 | default "false"; 363 | description 364 | "Enable AES Key Wrap encryption."; 365 | } 366 | } 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /assets/yang/ietf-netconf-acm@2018-02-14.yang: -------------------------------------------------------------------------------- 1 | module ietf-netconf-acm { 2 | 3 | namespace "urn:ietf:params:xml:ns:yang:ietf-netconf-acm"; 4 | 5 | prefix nacm; 6 | 7 | import ietf-yang-types { 8 | prefix yang; 9 | } 10 | 11 | organization 12 | "IETF NETCONF (Network Configuration) Working Group"; 13 | 14 | contact 15 | "WG Web: 16 | WG List: 17 | 18 | Author: Andy Bierman 19 | 20 | 21 | Author: Martin Bjorklund 22 | "; 23 | 24 | description 25 | "Network Configuration Access Control Model. 26 | 27 | Copyright (c) 2012 - 2018 IETF Trust and the persons 28 | identified as authors of the code. All rights reserved. 29 | 30 | Redistribution and use in source and binary forms, with or 31 | without modification, is permitted pursuant to, and subject 32 | to the license terms contained in, the Simplified BSD 33 | License set forth in Section 4.c of the IETF Trust's 34 | Legal Provisions Relating to IETF Documents 35 | (https://trustee.ietf.org/license-info). 36 | 37 | This version of this YANG module is part of RFC 8341; see 38 | the RFC itself for full legal notices."; 39 | 40 | revision "2018-02-14" { 41 | description 42 | "Added support for YANG 1.1 actions and notifications tied to 43 | data nodes. Clarified how NACM extensions can be used by 44 | other data models."; 45 | reference 46 | "RFC 8341: Network Configuration Access Control Model"; 47 | } 48 | 49 | revision "2012-02-22" { 50 | description 51 | "Initial version."; 52 | reference 53 | "RFC 6536: Network Configuration Protocol (NETCONF) 54 | Access Control Model"; 55 | } 56 | 57 | /* 58 | * Extension statements 59 | */ 60 | 61 | extension default-deny-write { 62 | description 63 | "Used to indicate that the data model node 64 | represents a sensitive security system parameter. 65 | 66 | If present, the NETCONF server will only allow the designated 67 | 'recovery session' to have write access to the node. An 68 | explicit access control rule is required for all other users. 69 | 70 | If the NACM module is used, then it must be enabled (i.e., 71 | /nacm/enable-nacm object equals 'true'), or this extension 72 | is ignored. 73 | 74 | The 'default-deny-write' extension MAY appear within a data 75 | definition statement. It is ignored otherwise."; 76 | } 77 | 78 | extension default-deny-all { 79 | description 80 | "Used to indicate that the data model node 81 | controls a very sensitive security system parameter. 82 | 83 | If present, the NETCONF server will only allow the designated 84 | 'recovery session' to have read, write, or execute access to 85 | the node. An explicit access control rule is required for all 86 | other users. 87 | 88 | If the NACM module is used, then it must be enabled (i.e., 89 | /nacm/enable-nacm object equals 'true'), or this extension 90 | is ignored. 91 | 92 | The 'default-deny-all' extension MAY appear within a data 93 | definition statement, 'rpc' statement, or 'notification' 94 | statement. It is ignored otherwise."; 95 | } 96 | 97 | /* 98 | * Derived types 99 | */ 100 | 101 | typedef user-name-type { 102 | type string { 103 | length "1..max"; 104 | } 105 | description 106 | "General-purpose username string."; 107 | } 108 | 109 | typedef matchall-string-type { 110 | type string { 111 | pattern '\*'; 112 | } 113 | description 114 | "The string containing a single asterisk '*' is used 115 | to conceptually represent all possible values 116 | for the particular leaf using this data type."; 117 | } 118 | 119 | typedef access-operations-type { 120 | type bits { 121 | bit create { 122 | description 123 | "Any protocol operation that creates a 124 | new data node."; 125 | } 126 | bit read { 127 | description 128 | "Any protocol operation or notification that 129 | returns the value of a data node."; 130 | } 131 | bit update { 132 | description 133 | "Any protocol operation that alters an existing 134 | data node."; 135 | } 136 | bit delete { 137 | description 138 | "Any protocol operation that removes a data node."; 139 | } 140 | bit exec { 141 | description 142 | "Execution access to the specified protocol operation."; 143 | } 144 | } 145 | description 146 | "Access operation."; 147 | } 148 | 149 | typedef group-name-type { 150 | type string { 151 | length "1..max"; 152 | pattern '[^\*].*'; 153 | } 154 | description 155 | "Name of administrative group to which 156 | users can be assigned."; 157 | } 158 | 159 | typedef action-type { 160 | type enumeration { 161 | enum permit { 162 | description 163 | "Requested action is permitted."; 164 | } 165 | enum deny { 166 | description 167 | "Requested action is denied."; 168 | } 169 | } 170 | description 171 | "Action taken by the server when a particular 172 | rule matches."; 173 | } 174 | 175 | typedef node-instance-identifier { 176 | type yang:xpath1.0; 177 | description 178 | "Path expression used to represent a special 179 | data node, action, or notification instance-identifier 180 | string. 181 | 182 | A node-instance-identifier value is an 183 | unrestricted YANG instance-identifier expression. 184 | All the same rules as an instance-identifier apply, 185 | except that predicates for keys are optional. If a key 186 | predicate is missing, then the node-instance-identifier 187 | represents all possible server instances for that key. 188 | 189 | This XML Path Language (XPath) expression is evaluated in the 190 | following context: 191 | 192 | o The set of namespace declarations are those in scope on 193 | the leaf element where this type is used. 194 | 195 | o The set of variable bindings contains one variable, 196 | 'USER', which contains the name of the user of the 197 | current session. 198 | 199 | o The function library is the core function library, but 200 | note that due to the syntax restrictions of an 201 | instance-identifier, no functions are allowed. 202 | 203 | o The context node is the root node in the data tree. 204 | 205 | The accessible tree includes actions and notifications tied 206 | to data nodes."; 207 | } 208 | 209 | /* 210 | * Data definition statements 211 | */ 212 | 213 | container nacm { 214 | nacm:default-deny-all; 215 | 216 | description 217 | "Parameters for NETCONF access control model."; 218 | 219 | leaf enable-nacm { 220 | type boolean; 221 | default "true"; 222 | description 223 | "Enables or disables all NETCONF access control 224 | enforcement. If 'true', then enforcement 225 | is enabled. If 'false', then enforcement 226 | is disabled."; 227 | } 228 | 229 | leaf read-default { 230 | type action-type; 231 | default "permit"; 232 | description 233 | "Controls whether read access is granted if 234 | no appropriate rule is found for a 235 | particular read request."; 236 | } 237 | 238 | leaf write-default { 239 | type action-type; 240 | default "deny"; 241 | description 242 | "Controls whether create, update, or delete access 243 | is granted if no appropriate rule is found for a 244 | particular write request."; 245 | } 246 | 247 | leaf exec-default { 248 | type action-type; 249 | default "permit"; 250 | description 251 | "Controls whether exec access is granted if no appropriate 252 | rule is found for a particular protocol operation request."; 253 | } 254 | 255 | leaf enable-external-groups { 256 | type boolean; 257 | default "true"; 258 | description 259 | "Controls whether the server uses the groups reported by the 260 | NETCONF transport layer when it assigns the user to a set of 261 | NACM groups. If this leaf has the value 'false', any group 262 | names reported by the transport layer are ignored by the 263 | server."; 264 | } 265 | 266 | leaf denied-operations { 267 | type yang:zero-based-counter32; 268 | config false; 269 | mandatory true; 270 | description 271 | "Number of times since the server last restarted that a 272 | protocol operation request was denied."; 273 | } 274 | 275 | leaf denied-data-writes { 276 | type yang:zero-based-counter32; 277 | config false; 278 | mandatory true; 279 | description 280 | "Number of times since the server last restarted that a 281 | protocol operation request to alter 282 | a configuration datastore was denied."; 283 | } 284 | 285 | leaf denied-notifications { 286 | type yang:zero-based-counter32; 287 | config false; 288 | mandatory true; 289 | description 290 | "Number of times since the server last restarted that 291 | a notification was dropped for a subscription because 292 | access to the event type was denied."; 293 | } 294 | 295 | container groups { 296 | description 297 | "NETCONF access control groups."; 298 | 299 | list group { 300 | key name; 301 | 302 | description 303 | "One NACM group entry. This list will only contain 304 | configured entries, not any entries learned from 305 | any transport protocols."; 306 | 307 | leaf name { 308 | type group-name-type; 309 | description 310 | "Group name associated with this entry."; 311 | } 312 | 313 | leaf-list user-name { 314 | type user-name-type; 315 | description 316 | "Each entry identifies the username of 317 | a member of the group associated with 318 | this entry."; 319 | } 320 | } 321 | } 322 | 323 | list rule-list { 324 | key name; 325 | ordered-by user; 326 | description 327 | "An ordered collection of access control rules."; 328 | 329 | leaf name { 330 | type string { 331 | length "1..max"; 332 | } 333 | description 334 | "Arbitrary name assigned to the rule-list."; 335 | } 336 | leaf-list group { 337 | type union { 338 | type matchall-string-type; 339 | type group-name-type; 340 | } 341 | description 342 | "List of administrative groups that will be 343 | assigned the associated access rights 344 | defined by the 'rule' list. 345 | 346 | The string '*' indicates that all groups apply to the 347 | entry."; 348 | } 349 | 350 | list rule { 351 | key name; 352 | ordered-by user; 353 | description 354 | "One access control rule. 355 | 356 | Rules are processed in user-defined order until a match is 357 | found. A rule matches if 'module-name', 'rule-type', and 358 | 'access-operations' match the request. If a rule 359 | matches, the 'action' leaf determines whether or not 360 | access is granted."; 361 | 362 | leaf name { 363 | type string { 364 | length "1..max"; 365 | } 366 | description 367 | "Arbitrary name assigned to the rule."; 368 | } 369 | 370 | leaf module-name { 371 | type union { 372 | type matchall-string-type; 373 | type string; 374 | } 375 | default "*"; 376 | description 377 | "Name of the module associated with this rule. 378 | 379 | This leaf matches if it has the value '*' or if the 380 | object being accessed is defined in the module with the 381 | specified module name."; 382 | } 383 | choice rule-type { 384 | description 385 | "This choice matches if all leafs present in the rule 386 | match the request. If no leafs are present, the 387 | choice matches all requests."; 388 | case protocol-operation { 389 | leaf rpc-name { 390 | type union { 391 | type matchall-string-type; 392 | type string; 393 | } 394 | description 395 | "This leaf matches if it has the value '*' or if 396 | its value equals the requested protocol operation 397 | name."; 398 | } 399 | } 400 | case notification { 401 | leaf notification-name { 402 | type union { 403 | type matchall-string-type; 404 | type string; 405 | } 406 | description 407 | "This leaf matches if it has the value '*' or if its 408 | value equals the requested notification name."; 409 | } 410 | } 411 | 412 | case data-node { 413 | leaf path { 414 | type node-instance-identifier; 415 | mandatory true; 416 | description 417 | "Data node instance-identifier associated with the 418 | data node, action, or notification controlled by 419 | this rule. 420 | 421 | Configuration data or state data 422 | instance-identifiers start with a top-level 423 | data node. A complete instance-identifier is 424 | required for this type of path value. 425 | 426 | The special value '/' refers to all possible 427 | datastore contents."; 428 | } 429 | } 430 | } 431 | 432 | leaf access-operations { 433 | type union { 434 | type matchall-string-type; 435 | type access-operations-type; 436 | } 437 | default "*"; 438 | description 439 | "Access operations associated with this rule. 440 | 441 | This leaf matches if it has the value '*' or if the 442 | bit corresponding to the requested operation is set."; 443 | } 444 | 445 | leaf action { 446 | type action-type; 447 | mandatory true; 448 | description 449 | "The access control action associated with the 450 | rule. If a rule has been determined to match a 451 | particular request, then this object is used 452 | to determine whether to permit or deny the 453 | request."; 454 | } 455 | 456 | leaf comment { 457 | type string; 458 | description 459 | "A textual description of the access rule."; 460 | } 461 | } 462 | } 463 | } 464 | } 465 | -------------------------------------------------------------------------------- /assets/yang/ietf-restconf@2017-01-26.yang: -------------------------------------------------------------------------------- 1 | module ietf-restconf { 2 | yang-version 1.1; 3 | namespace "urn:ietf:params:xml:ns:yang:ietf-restconf"; 4 | prefix "rc"; 5 | 6 | organization 7 | "IETF NETCONF (Network Configuration) Working Group"; 8 | 9 | contact 10 | "WG Web: 11 | WG List: 12 | 13 | Author: Andy Bierman 14 | 15 | 16 | Author: Martin Bjorklund 17 | 18 | 19 | Author: Kent Watsen 20 | "; 21 | 22 | description 23 | "This module contains conceptual YANG specifications 24 | for basic RESTCONF media type definitions used in 25 | RESTCONF protocol messages. 26 | 27 | Note that the YANG definitions within this module do not 28 | represent configuration data of any kind. 29 | The 'restconf-media-type' YANG extension statement 30 | provides a normative syntax for XML and JSON 31 | message-encoding purposes. 32 | 33 | Copyright (c) 2017 IETF Trust and the persons identified as 34 | authors of the code. All rights reserved. 35 | 36 | Redistribution and use in source and binary forms, with or 37 | without modification, is permitted pursuant to, and subject 38 | to the license terms contained in, the Simplified BSD License 39 | set forth in Section 4.c of the IETF Trust's Legal Provisions 40 | Relating to IETF Documents 41 | (http://trustee.ietf.org/license-info). 42 | 43 | This version of this YANG module is part of RFC 8040; see 44 | the RFC itself for full legal notices."; 45 | 46 | revision 2017-01-26 { 47 | description 48 | "Initial revision."; 49 | reference 50 | "RFC 8040: RESTCONF Protocol."; 51 | } 52 | 53 | extension yang-data { 54 | argument name { 55 | yin-element true; 56 | } 57 | description 58 | "This extension is used to specify a YANG data template that 59 | represents conceptual data defined in YANG. It is 60 | intended to describe hierarchical data independent of 61 | protocol context or specific message-encoding format. 62 | Data definition statements within a yang-data extension 63 | specify the generic syntax for the specific YANG data 64 | template, whose name is the argument of the 'yang-data' 65 | extension statement. 66 | 67 | Note that this extension does not define a media type. 68 | A specification using this extension MUST specify the 69 | message-encoding rules, including the content media type. 70 | 71 | The mandatory 'name' parameter value identifies the YANG 72 | data template that is being defined. It contains the 73 | template name. 74 | 75 | This extension is ignored unless it appears as a top-level 76 | statement. It MUST contain data definition statements 77 | that result in exactly one container data node definition. 78 | An instance of a YANG data template can thus be translated 79 | into an XML instance document, whose top-level element 80 | corresponds to the top-level container. 81 | The module name and namespace values for the YANG module using 82 | the extension statement are assigned to instance document data 83 | conforming to the data definition statements within 84 | this extension. 85 | 86 | The substatements of this extension MUST follow the 87 | 'data-def-stmt' rule in the YANG ABNF. 88 | 89 | The XPath document root is the extension statement itself, 90 | such that the child nodes of the document root are 91 | represented by the data-def-stmt substatements within 92 | this extension. This conceptual document is the context 93 | for the following YANG statements: 94 | 95 | - must-stmt 96 | - when-stmt 97 | - path-stmt 98 | - min-elements-stmt 99 | - max-elements-stmt 100 | - mandatory-stmt 101 | - unique-stmt 102 | - ordered-by 103 | - instance-identifier data type 104 | 105 | The following data-def-stmt substatements are constrained 106 | when used within a 'yang-data' extension statement. 107 | 108 | - The list-stmt is not required to have a key-stmt defined. 109 | - The if-feature-stmt is ignored if present. 110 | - The config-stmt is ignored if present. 111 | - The available identity values for any 'identityref' 112 | leaf or leaf-list nodes are limited to the module 113 | containing this extension statement and the modules 114 | imported into that module. 115 | "; 116 | } 117 | 118 | rc:yang-data yang-errors { 119 | uses errors; 120 | } 121 | 122 | rc:yang-data yang-api { 123 | uses restconf; 124 | } 125 | 126 | grouping errors { 127 | description 128 | "A grouping that contains a YANG container 129 | representing the syntax and semantics of a 130 | YANG Patch error report within a response message."; 131 | 132 | container errors { 133 | description 134 | "Represents an error report returned by the server if 135 | a request results in an error."; 136 | 137 | list error { 138 | description 139 | "An entry containing information about one 140 | specific error that occurred while processing 141 | a RESTCONF request."; 142 | reference 143 | "RFC 6241, Section 4.3."; 144 | 145 | leaf error-type { 146 | type enumeration { 147 | enum transport { 148 | description 149 | "The transport layer."; 150 | } 151 | enum rpc { 152 | description 153 | "The rpc or notification layer."; 154 | } 155 | enum protocol { 156 | description 157 | "The protocol operation layer."; 158 | } 159 | enum application { 160 | description 161 | "The server application layer."; 162 | } 163 | } 164 | mandatory true; 165 | description 166 | "The protocol layer where the error occurred."; 167 | } 168 | 169 | leaf error-tag { 170 | type string; 171 | mandatory true; 172 | description 173 | "The enumerated error-tag."; 174 | } 175 | 176 | leaf error-app-tag { 177 | type string; 178 | description 179 | "The application-specific error-tag."; 180 | } 181 | 182 | leaf error-path { 183 | type instance-identifier; 184 | description 185 | "The YANG instance identifier associated 186 | with the error node."; 187 | } 188 | 189 | leaf error-message { 190 | type string; 191 | description 192 | "A message describing the error."; 193 | } 194 | 195 | anydata error-info { 196 | description 197 | "This anydata value MUST represent a container with 198 | zero or more data nodes representing additional 199 | error information."; 200 | } 201 | } 202 | } 203 | } 204 | 205 | grouping restconf { 206 | description 207 | "Conceptual grouping representing the RESTCONF 208 | root resource."; 209 | 210 | container restconf { 211 | description 212 | "Conceptual container representing the RESTCONF 213 | root resource."; 214 | 215 | container data { 216 | description 217 | "Container representing the datastore resource. 218 | Represents the conceptual root of all state data 219 | and configuration data supported by the server. 220 | The child nodes of this container can be any data 221 | resources that are defined as top-level data nodes 222 | from the YANG modules advertised by the server in 223 | the 'ietf-yang-library' module."; 224 | } 225 | 226 | container operations { 227 | description 228 | "Container for all operation resources. 229 | 230 | Each resource is represented as an empty leaf with the 231 | name of the RPC operation from the YANG 'rpc' statement. 232 | 233 | For example, the 'system-restart' RPC operation defined 234 | in the 'ietf-system' module would be represented as 235 | an empty leaf in the 'ietf-system' namespace. This is 236 | a conceptual leaf and will not actually be found in 237 | the module: 238 | 239 | module ietf-system { 240 | leaf system-reset { 241 | type empty; 242 | } 243 | } 244 | 245 | To invoke the 'system-restart' RPC operation: 246 | 247 | POST /restconf/operations/ietf-system:system-restart 248 | 249 | To discover the RPC operations supported by the server: 250 | 251 | GET /restconf/operations 252 | 253 | In XML, the YANG module namespace identifies the module: 254 | 255 | 257 | 258 | In JSON, the YANG module name identifies the module: 259 | 260 | { 'ietf-system:system-restart' : [null] } 261 | "; 262 | } 263 | leaf yang-library-version { 264 | type string { 265 | pattern '\d{4}-\d{2}-\d{2}'; 266 | } 267 | config false; 268 | mandatory true; 269 | description 270 | "Identifies the revision date of the 'ietf-yang-library' 271 | module that is implemented by this RESTCONF server. 272 | Indicates the year, month, and day in YYYY-MM-DD 273 | numeric format."; 274 | } 275 | } 276 | } 277 | 278 | } 279 | -------------------------------------------------------------------------------- /assets/yang/ietf-routing@2018-01-25.yang: -------------------------------------------------------------------------------- 1 | module ietf-routing { 2 | yang-version "1.1"; 3 | namespace "urn:ietf:params:xml:ns:yang:ietf-routing"; 4 | prefix "rt"; 5 | 6 | import ietf-yang-types { 7 | prefix "yang"; 8 | } 9 | 10 | import ietf-interfaces { 11 | prefix "if"; 12 | description 13 | "A Network Management Datastore Architecture (NMDA) 14 | compatible version of the ietf-interfaces module 15 | is required."; 16 | } 17 | 18 | organization 19 | "IETF NETMOD - Networking Modeling Working Group"; 20 | contact 21 | "WG Web: 22 | WG List: 23 | 24 | Editor: Ladislav Lhotka 25 | 26 | Acee Lindem 27 | 28 | Yingzhen Qu 29 | "; 30 | 31 | description 32 | "This YANG module defines essential components for the management 33 | of a routing subsystem. The model fully conforms to the Network 34 | Management Datastore Architecture (NMDA). 35 | 36 | Copyright (c) 2017 IETF Trust and the persons 37 | identified as authors of the code. All rights reserved. 38 | 39 | Redistribution and use in source and binary forms, with or 40 | without modification, is permitted pursuant to, and subject 41 | to the license terms contained in, the Simplified BSD License 42 | set forth in Section 4.c of the IETF Trust's Legal Provisions 43 | Relating to IETF Documents 44 | (http://trustee.ietf.org/license-info). 45 | 46 | This version of this YANG module is part of RFC XXXX; see 47 | the RFC itself for full legal notices."; 48 | reference "RFC XXXX"; 49 | 50 | revision 2018-01-25 { 51 | description 52 | "Network Management Datastore Architecture (NMDA) Revision"; 53 | reference 54 | "RFC XXXX: A YANG Data Model for Routing Management 55 | (NMDA Version)"; 56 | } 57 | 58 | revision 2016-11-04 { 59 | description 60 | "Initial revision."; 61 | reference 62 | "RFC 8022: A YANG Data Model for Routing Management"; 63 | } 64 | 65 | /* Features */ 66 | feature multiple-ribs { 67 | description 68 | "This feature indicates that the server supports user-defined 69 | RIBs. 70 | 71 | Servers that do not advertise this feature SHOULD provide 72 | exactly one system-controlled RIB per supported address family 73 | and make it also the default RIB. This RIB then appears as an 74 | entry of the list /routing/ribs/rib."; 75 | } 76 | 77 | feature router-id { 78 | description 79 | "This feature indicates that the server supports of an explicit 80 | 32-bit router ID that is used by some routing protocols. 81 | 82 | Servers that do not advertise this feature set a router ID 83 | algorithmically, usually to one of the configured IPv4 84 | addresses. However, this algorithm is implementation 85 | specific."; 86 | } 87 | 88 | /* Identities */ 89 | 90 | identity address-family { 91 | description 92 | "Base identity from which identities describing address 93 | families are derived."; 94 | } 95 | 96 | identity ipv4 { 97 | base address-family; 98 | description 99 | "This identity represents IPv4 address family."; 100 | } 101 | 102 | identity ipv6 { 103 | base address-family; 104 | description 105 | "This identity represents IPv6 address family."; 106 | } 107 | 108 | identity control-plane-protocol { 109 | description 110 | "Base identity from which control-plane protocol identities are 111 | derived."; 112 | } 113 | 114 | identity routing-protocol { 115 | base control-plane-protocol; 116 | description 117 | "Identity from which Layer 3 routing protocol identities are 118 | derived."; 119 | } 120 | 121 | identity direct { 122 | base routing-protocol; 123 | description 124 | "Routing pseudo-protocol that provides routes to directly 125 | connected networks."; 126 | } 127 | 128 | identity static { 129 | base routing-protocol; 130 | description 131 | "Static routing pseudo-protocol."; 132 | } 133 | 134 | /* Type Definitions */ 135 | 136 | typedef route-preference { 137 | type uint32; 138 | description 139 | "This type is used for route preferences."; 140 | } 141 | 142 | /* Groupings */ 143 | 144 | grouping address-family { 145 | description 146 | "This grouping provides a leaf identifying an address 147 | family."; 148 | leaf address-family { 149 | type identityref { 150 | base address-family; 151 | } 152 | mandatory "true"; 153 | description 154 | "Address family."; 155 | } 156 | } 157 | 158 | grouping router-id { 159 | description 160 | "This grouping provides router ID."; 161 | leaf router-id { 162 | type yang:dotted-quad; 163 | description 164 | "A 32-bit number in the form of a dotted quad that is used by 165 | some routing protocols identifying a router."; 166 | reference 167 | "RFC 2328: OSPF Version 2."; 168 | } 169 | } 170 | 171 | grouping special-next-hop { 172 | description 173 | "This grouping provides a leaf with an enumeration of special 174 | next hops."; 175 | leaf special-next-hop { 176 | type enumeration { 177 | enum blackhole { 178 | description 179 | "Silently discard the packet."; 180 | } 181 | enum unreachable { 182 | description 183 | "Discard the packet and notify the sender with an error 184 | message indicating that the destination host is 185 | unreachable."; 186 | } 187 | enum prohibit { 188 | description 189 | "Discard the packet and notify the sender with an error 190 | message indicating that the communication is 191 | administratively prohibited."; 192 | } 193 | enum receive { 194 | description 195 | "The packet will be received by the local system."; 196 | } 197 | } 198 | description 199 | "Options for special next hops."; 200 | } 201 | } 202 | 203 | grouping next-hop-content { 204 | description 205 | "Generic parameters of next hops in static routes."; 206 | choice next-hop-options { 207 | mandatory "true"; 208 | description 209 | "Options for next hops in static routes. 210 | 211 | It is expected that further cases will be added through 212 | augments from other modules."; 213 | case simple-next-hop { 214 | description 215 | "This case represents a simple next hop consisting of the 216 | next-hop address and/or outgoing interface. 217 | 218 | Modules for address families MUST augment this case with a 219 | leaf containing a next-hop address of that address 220 | family."; 221 | leaf outgoing-interface { 222 | type if:interface-ref; 223 | description 224 | "Name of the outgoing interface."; 225 | } 226 | } 227 | case special-next-hop { 228 | uses special-next-hop; 229 | } 230 | case next-hop-list { 231 | container next-hop-list { 232 | description 233 | "Container for multiple next-hops."; 234 | list next-hop { 235 | key "index"; 236 | description 237 | "An entry of a next-hop list. 238 | 239 | Modules for address families MUST augment this list 240 | with a leaf containing a next-hop address of that 241 | address family."; 242 | leaf index { 243 | type string; 244 | description 245 | "A user-specified identifier utilized to uniquely 246 | reference the next-hop entry in the next-hop list. 247 | The value of this index has no semantic meaning 248 | other than for referencing the entry."; 249 | } 250 | leaf outgoing-interface { 251 | type if:interface-ref; 252 | description 253 | "Name of the outgoing interface."; 254 | } 255 | } 256 | } 257 | } 258 | } 259 | } 260 | grouping next-hop-state-content { 261 | description 262 | "Generic state parameters of next hops."; 263 | choice next-hop-options { 264 | mandatory "true"; 265 | description 266 | "Options for next hops. 267 | 268 | It is expected that further cases will be added through 269 | augments from other modules, e.g., for recursive 270 | next hops."; 271 | case simple-next-hop { 272 | description 273 | "This case represents a simple next hop consisting of the 274 | next-hop address and/or outgoing interface. 275 | 276 | Modules for address families MUST augment this case with a 277 | leaf containing a next-hop address of that address 278 | family."; 279 | leaf outgoing-interface { 280 | type if:interface-ref; 281 | description 282 | "Name of the outgoing interface."; 283 | } 284 | } 285 | case special-next-hop { 286 | uses special-next-hop; 287 | } 288 | case next-hop-list { 289 | container next-hop-list { 290 | description 291 | "Container for multiple next hops."; 292 | list next-hop { 293 | description 294 | "An entry of a next-hop list. 295 | 296 | Modules for address families MUST augment this list 297 | with a leaf containing a next-hop address of that 298 | address family."; 299 | leaf outgoing-interface { 300 | type if:interface-ref; 301 | description 302 | "Name of the outgoing interface."; 303 | } 304 | } 305 | } 306 | } 307 | } 308 | } 309 | 310 | grouping route-metadata { 311 | description 312 | "Common route metadata."; 313 | leaf source-protocol { 314 | type identityref { 315 | base routing-protocol; 316 | } 317 | mandatory "true"; 318 | description 319 | "Type of the routing protocol from which the route 320 | originated."; 321 | } 322 | leaf active { 323 | type empty; 324 | description 325 | "Presence of this leaf indicates that the route is preferred 326 | among all routes in the same RIB that have the same 327 | destination prefix."; 328 | } 329 | leaf last-updated { 330 | type yang:date-and-time; 331 | description 332 | "Time stamp of the last modification of the route. If the 333 | route was never modified, it is the time when the route was 334 | inserted into the RIB."; 335 | } 336 | } 337 | 338 | /* Data nodes */ 339 | 340 | container routing { 341 | description 342 | "Configuration parameters for the routing subsystem."; 343 | uses router-id { 344 | if-feature "router-id"; 345 | description 346 | "Support for the global router ID. Routing protocols 347 | that use router ID can use this parameter or override it 348 | with another value."; 349 | } 350 | container interfaces { 351 | config "false"; 352 | description 353 | "Network-layer interfaces used for routing."; 354 | leaf-list interface { 355 | type if:interface-ref; 356 | description 357 | "Each entry is a reference to the name of a configured 358 | network-layer interface."; 359 | } 360 | } 361 | container control-plane-protocols { 362 | description 363 | "Support for control-plane protocol instances."; 364 | list control-plane-protocol { 365 | key "type name"; 366 | description 367 | "Each entry contains a control-plane protocol instance."; 368 | leaf type { 369 | type identityref { 370 | base control-plane-protocol; 371 | } 372 | description 373 | "Type of the control-plane protocol - an identity derived 374 | from the 'control-plane-protocol' base identity."; 375 | } 376 | leaf name { 377 | type string; 378 | description 379 | "An arbitrary name of the control-plane protocol 380 | instance."; 381 | } 382 | leaf description { 383 | type string; 384 | description 385 | "Textual description of the control-plane protocol 386 | instance."; 387 | } 388 | container static-routes { 389 | when "derived-from-or-self(../type, 'rt:static')" { 390 | description 391 | "This container is only valid for the 'static' routing 392 | protocol."; 393 | } 394 | description 395 | "Support for the 'static' pseudo-protocol. 396 | 397 | Address-family-specific modules augment this node with 398 | their lists of routes."; 399 | } 400 | } 401 | } 402 | container ribs { 403 | description 404 | "Support for RIBs."; 405 | list rib { 406 | key "name"; 407 | description 408 | "Each entry contains configuration for a RIB identified by 409 | the 'name' key. 410 | 411 | Entries having the same key as a system-controlled entry 412 | of the list /routing/ribs/rib are used for 413 | configuring parameters of that entry. Other entries 414 | define additional user-controlled RIBs."; 415 | leaf name { 416 | type string; 417 | description 418 | "The name of the RIB. 419 | 420 | For system-controlled entries, the value of this leaf 421 | must be the same as the name of the corresponding entry 422 | in operational state. 423 | 424 | For user-controlled entries, an arbitrary name can be 425 | used."; 426 | } 427 | uses address-family { 428 | description 429 | "The address family of the system-controlled RIB."; 430 | } 431 | 432 | leaf default-rib { 433 | if-feature "multiple-ribs"; 434 | type boolean; 435 | default "true"; 436 | config "false"; 437 | description 438 | "This flag has the value of 'true' if and only if the RIB 439 | is the default RIB for the given address family. 440 | 441 | By default, control-plane protocols place their routes 442 | in the default RIBs."; 443 | } 444 | container routes { 445 | config "false"; 446 | description 447 | "Current content of the RIB."; 448 | list route { 449 | description 450 | "A RIB route entry. This data node MUST be augmented 451 | with information specific for routes of each address 452 | family."; 453 | leaf route-preference { 454 | type route-preference; 455 | description 456 | "This route attribute, also known as administrative 457 | distance, allows for selecting the preferred route 458 | among routes with the same destination prefix. A 459 | smaller value means a more preferred route."; 460 | } 461 | container next-hop { 462 | description 463 | "Route's next-hop attribute."; 464 | uses next-hop-state-content; 465 | } 466 | uses route-metadata; 467 | } 468 | } 469 | action active-route { 470 | description 471 | "Return the active RIB route that is used for the 472 | destination address. 473 | 474 | Address-family-specific modules MUST augment input 475 | parameters with a leaf named 'destination-address'."; 476 | output { 477 | container route { 478 | description 479 | "The active RIB route for the specified destination. 480 | 481 | If no route exists in the RIB for the destination 482 | address, no output is returned. 483 | 484 | Address-family-specific modules MUST augment this 485 | container with appropriate route contents."; 486 | container next-hop { 487 | description 488 | "Route's next-hop attribute."; 489 | uses next-hop-state-content; 490 | } 491 | uses route-metadata; 492 | } 493 | } 494 | } 495 | leaf description { 496 | type string; 497 | description 498 | "Textual description of the RIB."; 499 | } 500 | } 501 | } 502 | } 503 | 504 | /* 505 | * The subsequent data nodes are obviated and obsoleted by the 506 | * "Network Management Architecture" as described in 507 | * draft-ietf-netmod-revised-datastores. 508 | */ 509 | container routing-state { 510 | config false; 511 | status obsolete; 512 | description 513 | "State data of the routing subsystem."; 514 | uses router-id { 515 | status obsolete; 516 | description 517 | "Global router ID. 518 | 519 | It may be either configured or assigned algorithmically by 520 | the implementation."; 521 | } 522 | container interfaces { 523 | status obsolete; 524 | description 525 | "Network-layer interfaces used for routing."; 526 | leaf-list interface { 527 | type if:interface-state-ref; 528 | status obsolete; 529 | description 530 | "Each entry is a reference to the name of a configured 531 | network-layer interface."; 532 | } 533 | } 534 | container control-plane-protocols { 535 | status obsolete; 536 | description 537 | "Container for the list of routing protocol instances."; 538 | list control-plane-protocol { 539 | key "type name"; 540 | status obsolete; 541 | description 542 | "State data of a control-plane protocol instance. 543 | 544 | An implementation MUST provide exactly one 545 | system-controlled instance of the 'direct' 546 | pseudo-protocol. Instances of other control-plane 547 | protocols MAY be created by configuration."; 548 | leaf type { 549 | type identityref { 550 | base control-plane-protocol; 551 | } 552 | status obsolete; 553 | description 554 | "Type of the control-plane protocol."; 555 | } 556 | leaf name { 557 | type string; 558 | status obsolete; 559 | description 560 | "The name of the control-plane protocol instance. 561 | 562 | For system-controlled instances this name is 563 | persistent, i.e., it SHOULD NOT change across 564 | reboots."; 565 | } 566 | } 567 | } 568 | container ribs { 569 | status obsolete; 570 | description 571 | "Container for RIBs."; 572 | list rib { 573 | key "name"; 574 | min-elements 1; 575 | status obsolete; 576 | description 577 | "Each entry represents a RIB identified by the 'name' 578 | key. All routes in a RIB MUST belong to the same address 579 | family. 580 | 581 | An implementation SHOULD provide one system-controlled 582 | default RIB for each supported address family."; 583 | leaf name { 584 | type string; 585 | status obsolete; 586 | description 587 | "The name of the RIB."; 588 | } 589 | uses address-family { 590 | status obsolete; 591 | description 592 | "The address family of the RIB."; 593 | } 594 | leaf default-rib { 595 | if-feature "multiple-ribs"; 596 | type boolean; 597 | default "true"; 598 | status obsolete; 599 | description 600 | "This flag has the value of 'true' if and only if the 601 | RIB is the default RIB for the given address family. 602 | 603 | By default, control-plane protocols place their routes 604 | in the default RIBs."; 605 | } 606 | container routes { 607 | status obsolete; 608 | description 609 | "Current content of the RIB."; 610 | list route { 611 | status obsolete; 612 | description 613 | "A RIB route entry. This data node MUST be augmented 614 | with information specific for routes of each address 615 | family."; 616 | leaf route-preference { 617 | type route-preference; 618 | status obsolete; 619 | description 620 | "This route attribute, also known as administrative 621 | distance, allows for selecting the preferred route 622 | among routes with the same destination prefix. A 623 | smaller value means a more preferred route."; 624 | } 625 | container next-hop { 626 | status obsolete; 627 | description 628 | "Route's next-hop attribute."; 629 | uses next-hop-state-content { 630 | status obsolete; 631 | description 632 | "Route's next-hop attribute operational state."; 633 | } 634 | } 635 | uses route-metadata { 636 | status obsolete; 637 | description 638 | "Route metadata."; 639 | } 640 | } 641 | } 642 | action active-route { 643 | status obsolete; 644 | description 645 | "Return the active RIB route that is used for the 646 | destination address. 647 | 648 | Address-family-specific modules MUST augment input 649 | parameters with a leaf named 'destination-address'."; 650 | output { 651 | container route { 652 | status obsolete; 653 | description 654 | "The active RIB route for the specified 655 | destination. 656 | 657 | If no route exists in the RIB for the destination 658 | address, no output is returned. 659 | 660 | Address-family-specific modules MUST augment this 661 | container with appropriate route contents."; 662 | container next-hop { 663 | status obsolete; 664 | description 665 | "Route's next-hop attribute."; 666 | uses next-hop-state-content { 667 | status obsolete; 668 | description 669 | "Active route state data."; 670 | } 671 | } 672 | uses route-metadata { 673 | status obsolete; 674 | description 675 | "Active route metadata."; 676 | } 677 | } 678 | } 679 | } 680 | } 681 | } 682 | } 683 | } 684 | -------------------------------------------------------------------------------- /benches/data.rs: -------------------------------------------------------------------------------- 1 | use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion}; 2 | use yang3::context::{Context, ContextFlags}; 3 | use yang3::data::{Data, DataDiffFlags, DataTree, DataValidationFlags}; 4 | 5 | static SEARCH_DIR: &str = "./assets/yang/"; 6 | 7 | fn data_generate(ctx: &Context, interfaces: u32) -> DataTree { 8 | let mut dtree = DataTree::new(ctx); 9 | 10 | for i in 1..=interfaces { 11 | let changes = [ 12 | (format!("/ietf-interfaces:interfaces/interface[name='eth{}']", i), None), 13 | (format!("/ietf-interfaces:interfaces/interface[name='eth{}']/type", i), Some("iana-if-type:ethernetCsmacd")), 14 | (format!("/ietf-interfaces:interfaces/interface[name='eth{}']/enabled", i), Some("true")), 15 | ]; 16 | 17 | for (xpath, value) in &changes { 18 | dtree 19 | .new_path(xpath, *value, false) 20 | .expect("Failed to edit data tree"); 21 | } 22 | } 23 | 24 | dtree 25 | } 26 | 27 | fn criterion_benchmark(c: &mut Criterion) { 28 | let tree_sizes = [ 29 | 1 * 1024, 30 | 2 * 1024, 31 | 4 * 1024, 32 | 8 * 1024, 33 | 16 * 1024, 34 | 32 * 1024, 35 | 64 * 1024, 36 | ]; 37 | 38 | // Initialize context. 39 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 40 | .expect("Failed to create context"); 41 | ctx.set_searchdir(SEARCH_DIR) 42 | .expect("Failed to set YANG search directory"); 43 | 44 | // Load YANG modules. 45 | for module_name in &["ietf-interfaces", "iana-if-type"] { 46 | ctx.load_module(module_name, None, &[]) 47 | .expect("Failed to load module"); 48 | } 49 | 50 | // Prepare DataTree.diff() benchmark. 51 | let mut group = c.benchmark_group("DataTree.diff() / tree size"); 52 | for size in &tree_sizes { 53 | // Create artificial data trees. 54 | let dtree = data_generate(&ctx, *size); 55 | let dtree_base = data_generate(&ctx, *size + 1024); 56 | 57 | // Run benchmark. 58 | group.bench_with_input( 59 | BenchmarkId::from_parameter(size), 60 | size, 61 | |b, _| { 62 | b.iter(|| { 63 | dtree 64 | .diff(&dtree_base, DataDiffFlags::empty()) 65 | .expect("Failed to compare data trees"); 66 | }); 67 | }, 68 | ); 69 | } 70 | group.finish(); 71 | 72 | // Prepare DataTree.find() benchmark. 73 | let mut group = c.benchmark_group("DataTree.find() / tree size"); 74 | for size in &tree_sizes { 75 | // Create artificial data tree. 76 | let dtree = data_generate(&ctx, *size); 77 | 78 | // Run benchmark. 79 | group.bench_with_input( 80 | BenchmarkId::from_parameter(size), 81 | size, 82 | |b, _| { 83 | b.iter(|| { 84 | for dnode in dtree.traverse() { 85 | let path = dnode.path(); 86 | dtree.find_path(&path).expect("Failed to find data"); 87 | } 88 | }); 89 | }, 90 | ); 91 | } 92 | group.finish(); 93 | 94 | // Prepare DataTree.validate() benchmark. 95 | let mut group = c.benchmark_group("DataTree.validate() / tree size"); 96 | for size in &tree_sizes { 97 | // Create artificial data tree. 98 | let mut dtree = data_generate(&ctx, *size); 99 | 100 | // Run benchmark. 101 | group.bench_with_input( 102 | BenchmarkId::from_parameter(size), 103 | size, 104 | |b, _| { 105 | b.iter(|| { 106 | dtree 107 | .validate( 108 | DataValidationFlags::NO_STATE 109 | | DataValidationFlags::PRESENT, 110 | ) 111 | .expect("Failed to validate data tree") 112 | }) 113 | }, 114 | ); 115 | } 116 | group.finish(); 117 | } 118 | 119 | criterion_group!(benches, criterion_benchmark); 120 | criterion_main!(benches); 121 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | threshold: 10% 6 | patch: off 7 | changes: no 8 | -------------------------------------------------------------------------------- /examples/data_diff.rs: -------------------------------------------------------------------------------- 1 | use yang3::context::{Context, ContextFlags}; 2 | use yang3::data::{ 3 | Data, DataDiffFlags, DataFormat, DataParserFlags, DataPrinterFlags, 4 | DataTree, DataValidationFlags, 5 | }; 6 | 7 | static SEARCH_DIR: &str = "./assets/yang/"; 8 | static JSON_TREE1: &str = r###" 9 | { 10 | "ietf-interfaces:interfaces":{ 11 | "interface": [ 12 | { 13 | "name": "eth/0/0", 14 | "description": "ENG", 15 | "type": "iana-if-type:ethernetCsmacd", 16 | "enabled": true 17 | } 18 | ], 19 | "interface": [ 20 | { 21 | "name": "eth/0/1", 22 | "description": "MKT", 23 | "type": "iana-if-type:ethernetCsmacd", 24 | "enabled": true 25 | } 26 | ] 27 | } 28 | }"###; 29 | static JSON_TREE2: &str = r###" 30 | { 31 | "ietf-interfaces:interfaces":{ 32 | "interface": [ 33 | { 34 | "name": "eth/0/0", 35 | "description": "ENG", 36 | "type": "iana-if-type:ethernetCsmacd", 37 | "enabled": false 38 | } 39 | ], 40 | "interface": [ 41 | { 42 | "name": "eth/0/2", 43 | "description": "MGMT", 44 | "type": "iana-if-type:ethernetCsmacd", 45 | "enabled": true 46 | } 47 | ] 48 | } 49 | }"###; 50 | 51 | fn main() -> std::io::Result<()> { 52 | // Initialize context. 53 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 54 | .expect("Failed to create context"); 55 | ctx.set_searchdir(SEARCH_DIR) 56 | .expect("Failed to set YANG search directory"); 57 | 58 | // Load YANG modules. 59 | for module_name in &["ietf-interfaces", "iana-if-type"] { 60 | ctx.load_module(module_name, None, &[]) 61 | .expect("Failed to load module"); 62 | } 63 | 64 | // Parse data trees from JSON strings. 65 | let dtree1 = DataTree::parse_string( 66 | &ctx, 67 | JSON_TREE1, 68 | DataFormat::JSON, 69 | DataParserFlags::NO_VALIDATION, 70 | DataValidationFlags::empty(), 71 | ) 72 | .expect("Failed to parse data tree"); 73 | 74 | let dtree2 = DataTree::parse_string( 75 | &ctx, 76 | JSON_TREE2, 77 | DataFormat::JSON, 78 | DataParserFlags::NO_VALIDATION, 79 | DataValidationFlags::empty(), 80 | ) 81 | .expect("Failed to parse data tree"); 82 | 83 | // Compare data trees. 84 | println!("Comparing data trees (JSON output):"); 85 | let diff = dtree1 86 | .diff(&dtree2, DataDiffFlags::empty()) 87 | .expect("Failed to compare data trees"); 88 | diff.print_file( 89 | std::io::stdout(), 90 | DataFormat::JSON, 91 | DataPrinterFlags::WITH_SIBLINGS, 92 | ) 93 | .expect("Failed to print data diff"); 94 | 95 | println!("Comparing data trees (manual iteration):"); 96 | for (op, dnode) in diff.iter() { 97 | println!(" {:?}: {} ({:?})", op, dnode.path(), dnode.value()); 98 | } 99 | 100 | Ok(()) 101 | } 102 | -------------------------------------------------------------------------------- /examples/data_edit.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use yang3::context::{Context, ContextFlags}; 3 | use yang3::data::{ 4 | Data, DataFormat, DataParserFlags, DataPrinterFlags, DataTree, 5 | DataValidationFlags, 6 | }; 7 | 8 | static SEARCH_DIR: &str = "./assets/yang/"; 9 | 10 | enum Operation { 11 | MODIFY(&'static str, Option<&'static str>), 12 | DELETE(&'static str), 13 | } 14 | 15 | fn main() -> std::io::Result<()> { 16 | // Initialize context. 17 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 18 | .expect("Failed to create context"); 19 | ctx.set_searchdir(SEARCH_DIR) 20 | .expect("Failed to set YANG search directory"); 21 | 22 | // Load YANG modules. 23 | for module_name in &["ietf-interfaces", "iana-if-type"] { 24 | ctx.load_module(module_name, None, &[]) 25 | .expect("Failed to load module"); 26 | } 27 | 28 | // Parse data tree from JSON file. 29 | let mut dtree = DataTree::parse_file( 30 | &ctx, 31 | File::open("./assets/data/interfaces.json")?, 32 | DataFormat::JSON, 33 | DataParserFlags::NO_VALIDATION, 34 | DataValidationFlags::empty(), 35 | ) 36 | .expect("Failed to parse data tree"); 37 | 38 | // Modify data tree. 39 | let changes = [ 40 | Operation::DELETE( 41 | "/ietf-interfaces:interfaces/interface[name='eth/0/0']", 42 | ), 43 | Operation::MODIFY( 44 | "/ietf-interfaces:interfaces/interface[name='eth/0/1']/description", 45 | Some("HR"), 46 | ), 47 | Operation::MODIFY( 48 | "/ietf-interfaces:interfaces/interface[name='eth/0/2']/description", 49 | Some("MGMT"), 50 | ), 51 | Operation::MODIFY( 52 | "/ietf-interfaces:interfaces/interface[name='eth/0/2']/type", 53 | Some("iana-if-type:ethernetCsmacd"), 54 | ), 55 | Operation::MODIFY( 56 | "/ietf-interfaces:interfaces/interface[name='eth/0/2']/enabled", 57 | Some("true"), 58 | ), 59 | ]; 60 | for change in &changes { 61 | match change { 62 | Operation::MODIFY(xpath, value) => { 63 | dtree 64 | .new_path(xpath, *value, false) 65 | .expect("Failed to edit data tree"); 66 | } 67 | Operation::DELETE(xpath) => { 68 | dtree.remove(xpath).expect("Failed to edit data tree") 69 | } 70 | }; 71 | } 72 | 73 | // Print the modified data tree. 74 | dtree 75 | .print_file( 76 | std::io::stdout(), 77 | DataFormat::JSON, 78 | DataPrinterFlags::WD_ALL | DataPrinterFlags::WITH_SIBLINGS, 79 | ) 80 | .expect("Failed to print data tree"); 81 | 82 | Ok(()) 83 | } 84 | -------------------------------------------------------------------------------- /examples/data_iteration.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use yang3::context::{Context, ContextFlags}; 3 | use yang3::data::{ 4 | Data, DataFormat, DataParserFlags, DataTree, DataValidationFlags, 5 | }; 6 | 7 | static SEARCH_DIR: &str = "./assets/yang/"; 8 | 9 | fn main() -> std::io::Result<()> { 10 | // Initialize context. 11 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 12 | .expect("Failed to create context"); 13 | ctx.set_searchdir(SEARCH_DIR) 14 | .expect("Failed to set YANG search directory"); 15 | 16 | // Load YANG modules. 17 | for module_name in &["ietf-interfaces", "iana-if-type", "ietf-isis"] { 18 | ctx.load_module(module_name, None, &[]) 19 | .expect("Failed to load module"); 20 | } 21 | 22 | // Parse data tree in the JSON format. 23 | let dtree = DataTree::parse_file( 24 | &ctx, 25 | File::open("./assets/data/isis.json")?, 26 | DataFormat::JSON, 27 | DataParserFlags::NO_VALIDATION, 28 | DataValidationFlags::empty(), 29 | ) 30 | .expect("Failed to parse data tree"); 31 | 32 | // Iterate over all nodes of the data tree. 33 | println!("Iterating over all data nodes..."); 34 | for dnode in dtree.traverse() { 35 | println!(" {}: {:?}", dnode.path(), dnode.value()); 36 | } 37 | 38 | // Iterate over all interfaces present in the data tree. 39 | println!("Iterating over interfaces only..."); 40 | for dnode in dtree 41 | .find_xpath("/ietf-interfaces:interfaces/interface") 42 | .expect("Failed to find interfaces") 43 | { 44 | println!(" {}: {:?}", dnode.path(), dnode.value()); 45 | } 46 | 47 | Ok(()) 48 | } 49 | -------------------------------------------------------------------------------- /examples/data_json2xml.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use yang3::context::{Context, ContextFlags}; 3 | use yang3::data::{ 4 | Data, DataFormat, DataParserFlags, DataPrinterFlags, DataTree, 5 | DataValidationFlags, 6 | }; 7 | 8 | static SEARCH_DIR: &str = "./assets/yang/"; 9 | 10 | fn main() -> std::io::Result<()> { 11 | // Initialize context. 12 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 13 | .expect("Failed to create context"); 14 | ctx.set_searchdir(SEARCH_DIR) 15 | .expect("Failed to set YANG search directory"); 16 | 17 | // Load YANG modules. 18 | for module_name in &["ietf-interfaces", "iana-if-type"] { 19 | ctx.load_module(module_name, None, &[]) 20 | .expect("Failed to load module"); 21 | } 22 | 23 | // Parse data tree in the JSON format. 24 | let dtree = DataTree::parse_file( 25 | &ctx, 26 | File::open("./assets/data/interfaces.json")?, 27 | DataFormat::JSON, 28 | DataParserFlags::NO_VALIDATION, 29 | DataValidationFlags::empty(), 30 | ) 31 | .expect("Failed to parse data tree"); 32 | 33 | // Print data tree in the XML format. 34 | dtree 35 | .print_file( 36 | std::io::stdout(), 37 | DataFormat::XML, 38 | DataPrinterFlags::WD_ALL | DataPrinterFlags::WITH_SIBLINGS, 39 | ) 40 | .expect("Failed to print data tree"); 41 | 42 | Ok(()) 43 | } 44 | -------------------------------------------------------------------------------- /examples/schema_iteration.rs: -------------------------------------------------------------------------------- 1 | use yang3::context::{Context, ContextFlags}; 2 | use yang3::schema::SchemaPathFormat; 3 | 4 | static SEARCH_DIR: &str = "./assets/yang/"; 5 | static MODULE_NAME: &str = "ietf-isis"; 6 | 7 | fn main() -> std::io::Result<()> { 8 | // Initialize context. 9 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 10 | .expect("Failed to create context"); 11 | ctx.set_searchdir(SEARCH_DIR) 12 | .expect("Failed to set YANG search directory"); 13 | 14 | // Load test module. 15 | ctx.load_module(MODULE_NAME, None, &[]) 16 | .expect("Failed to load module"); 17 | 18 | // Iterate over all schema nodes that belong to the test module and print 19 | // their full paths. 20 | println!("Data (DFS iteration):"); 21 | for snode in ctx 22 | .traverse() 23 | .filter(|snode| snode.module().name() == MODULE_NAME) 24 | { 25 | println!(" {}", snode.path(SchemaPathFormat::DATA)); 26 | } 27 | 28 | println!("RPCs:"); 29 | let module = ctx 30 | .get_module_latest(MODULE_NAME) 31 | .expect("Failed to find loaded module"); 32 | for snode in module.rpcs() { 33 | println!(" {}", snode.path(SchemaPathFormat::DATA)); 34 | } 35 | 36 | println!("Notifications:"); 37 | for snode in module.notifications() { 38 | println!(" {}", snode.path(SchemaPathFormat::DATA)); 39 | } 40 | 41 | Ok(()) 42 | } 43 | -------------------------------------------------------------------------------- /examples/schema_yang2yin.rs: -------------------------------------------------------------------------------- 1 | use yang3::context::{Context, ContextFlags}; 2 | use yang3::schema::{SchemaOutputFormat, SchemaPrinterFlags}; 3 | 4 | static SEARCH_DIR: &str = "./assets/yang/"; 5 | static MODULE_NAME: &str = "ietf-routing"; 6 | 7 | fn main() -> std::io::Result<()> { 8 | // Initialize context. 9 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 10 | .expect("Failed to create context"); 11 | ctx.set_searchdir(SEARCH_DIR) 12 | .expect("Failed to set YANG search directory"); 13 | 14 | // Load test module. 15 | let module = ctx 16 | .load_module(MODULE_NAME, None, &[]) 17 | .expect("Failed to load module"); 18 | 19 | // Print test module. 20 | module 21 | .print_file( 22 | std::io::stdout(), 23 | SchemaOutputFormat::YIN, 24 | SchemaPrinterFlags::empty(), 25 | ) 26 | .expect("Failed to print module"); 27 | 28 | Ok(()) 29 | } 30 | -------------------------------------------------------------------------------- /libyang3-sys/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "libyang3-sys" 3 | version = "0.6.0" 4 | authors = ["Renato Westphal "] 5 | description = "Raw FFI bindings for libyang3" 6 | keywords = ["yang", "libyang"] 7 | edition = "2018" 8 | license = "MIT" 9 | documentation = "https://docs.rs/libyang3-sys" 10 | categories = ["external-ffi-bindings"] 11 | 12 | [dependencies] 13 | 14 | [build-dependencies] 15 | bindgen = { version = "0.68.0", optional = true } 16 | cc = { version = "1.0", features = ["parallel"], optional = true } 17 | cmake = { version = "0.1", optional = true } 18 | pkg-config = "0.3.27" 19 | 20 | [features] 21 | # Use pre-generated FFI bindings 22 | default = [] 23 | # Generate FFI bindings dynamically. 24 | # For this to work libyang3 needs to be installed in the system. 25 | bindgen = ["dep:bindgen"] 26 | # Bundle libyang3 C files into a static archive linked to this crate. 27 | # This removes the libyang3 dynamic link dependency. 28 | bundled = ["dep:cc", "dep:cmake"] 29 | -------------------------------------------------------------------------------- /libyang3-sys/LICENSE: -------------------------------------------------------------------------------- 1 | ../LICENSE -------------------------------------------------------------------------------- /libyang3-sys/build.rs: -------------------------------------------------------------------------------- 1 | use std::env; 2 | use std::path::PathBuf; 3 | 4 | fn main() { 5 | let dst = PathBuf::from(env::var("OUT_DIR").unwrap()); 6 | let out_file = dst.join("libyang3.rs"); 7 | 8 | #[cfg(feature = "bindgen")] 9 | { 10 | // Generate Rust FFI to libyang. 11 | println!("cargo:rerun-if-changed=wrapper.h"); 12 | let bindings = bindgen::Builder::default() 13 | .header("wrapper.h") 14 | .derive_default(true) 15 | .default_enum_style(bindgen::EnumVariation::ModuleConsts) 16 | .generate() 17 | .expect("Unable to generate libyang3 bindings"); 18 | bindings 19 | .write_to_file(out_file) 20 | .expect("Couldn't write libyang3 bindings!"); 21 | } 22 | #[cfg(not(feature = "bindgen"))] 23 | { 24 | let mut pregen_bindings = PathBuf::new(); 25 | pregen_bindings.push(env::var("CARGO_MANIFEST_DIR").unwrap()); 26 | pregen_bindings.push("pre-generated-bindings"); 27 | pregen_bindings 28 | .push("libyang3-f313632a8ff45d7bba4be29ea9dc83ab5b533807.rs"); 29 | 30 | std::fs::copy(&pregen_bindings, &out_file) 31 | .expect("Unable to copy pre-generated libyang3 bindings"); 32 | } 33 | 34 | #[cfg(feature = "bundled")] 35 | { 36 | use std::path::Path; 37 | use std::process::Command; 38 | 39 | // Initialize the libyang submodule if necessary. 40 | if !Path::new("libyang/.git").exists() { 41 | let _ = Command::new("git") 42 | .args(&["submodule", "update", "--init"]) 43 | .status(); 44 | } 45 | 46 | // Run cmake. 47 | let cmake_dst = cmake::build("libyang"); 48 | 49 | // Build libyang3. 50 | let mut build = cc::Build::new(); 51 | build 52 | .include(format!("{}/build/compat", cmake_dst.display())) 53 | .include(format!("{}/build/src", cmake_dst.display())) 54 | .include(format!("{}/build/libyang", cmake_dst.display())) 55 | .include("libyang/src") 56 | .include("libyang/src/plugins_exts") 57 | .file("libyang/compat/compat.c") 58 | .file("libyang/src/context.c") 59 | .file("libyang/src/dict.c") 60 | .file("libyang/src/diff.c") 61 | .file("libyang/src/hash_table.c") 62 | .file("libyang/src/in.c") 63 | .file("libyang/src/json.c") 64 | .file("libyang/src/log.c") 65 | .file("libyang/src/lyb.c") 66 | .file("libyang/src/ly_common.c") 67 | .file("libyang/src/out.c") 68 | .file("libyang/src/parser_common.c") 69 | .file("libyang/src/parser_json.c") 70 | .file("libyang/src/parser_lyb.c") 71 | .file("libyang/src/parser_xml.c") 72 | .file("libyang/src/parser_yang.c") 73 | .file("libyang/src/parser_yin.c") 74 | .file("libyang/src/path.c") 75 | .file("libyang/src/plugins.c") 76 | .file("libyang/src/plugins_exts.c") 77 | .file("libyang/src/plugins_exts/metadata.c") 78 | .file("libyang/src/plugins_exts/nacm.c") 79 | .file("libyang/src/plugins_exts/schema_mount.c") 80 | .file("libyang/src/plugins_exts/structure.c") 81 | .file("libyang/src/plugins_exts/yangdata.c") 82 | .file("libyang/src/plugins_types.c") 83 | .file("libyang/src/plugins_types/binary.c") 84 | .file("libyang/src/plugins_types/bits.c") 85 | .file("libyang/src/plugins_types/boolean.c") 86 | .file("libyang/src/plugins_types/date_and_time.c") 87 | .file("libyang/src/plugins_types/decimal64.c") 88 | .file("libyang/src/plugins_types/empty.c") 89 | .file("libyang/src/plugins_types/enumeration.c") 90 | .file("libyang/src/plugins_types/hex_string.c") 91 | .file("libyang/src/plugins_types/identityref.c") 92 | .file("libyang/src/plugins_types/instanceid.c") 93 | .file("libyang/src/plugins_types/instanceid_keys.c") 94 | .file("libyang/src/plugins_types/integer.c") 95 | .file("libyang/src/plugins_types/ipv4_address.c") 96 | .file("libyang/src/plugins_types/ipv4_address_no_zone.c") 97 | .file("libyang/src/plugins_types/ipv4_prefix.c") 98 | .file("libyang/src/plugins_types/ipv6_address.c") 99 | .file("libyang/src/plugins_types/ipv6_address_no_zone.c") 100 | .file("libyang/src/plugins_types/ipv6_prefix.c") 101 | .file("libyang/src/plugins_types/leafref.c") 102 | .file("libyang/src/plugins_types/lyds_tree.c") 103 | .file("libyang/src/plugins_types/node_instanceid.c") 104 | .file("libyang/src/plugins_types/time_period.c") 105 | .file("libyang/src/plugins_types/string.c") 106 | .file("libyang/src/plugins_types/union.c") 107 | .file("libyang/src/plugins_types/xpath1.0.c") 108 | .file("libyang/src/printer_data.c") 109 | .file("libyang/src/printer_json.c") 110 | .file("libyang/src/printer_lyb.c") 111 | .file("libyang/src/printer_schema.c") 112 | .file("libyang/src/printer_tree.c") 113 | .file("libyang/src/printer_xml.c") 114 | .file("libyang/src/printer_yang.c") 115 | .file("libyang/src/printer_yin.c") 116 | .file("libyang/src/schema_compile_amend.c") 117 | .file("libyang/src/schema_compile.c") 118 | .file("libyang/src/schema_compile_node.c") 119 | .file("libyang/src/schema_features.c") 120 | .file("libyang/src/set.c") 121 | .file("libyang/src/tree_data.c") 122 | .file("libyang/src/tree_data_common.c") 123 | .file("libyang/src/tree_data_free.c") 124 | .file("libyang/src/tree_data_hash.c") 125 | .file("libyang/src/tree_data_new.c") 126 | .file("libyang/src/tree_data_sorted.c") 127 | .file("libyang/src/tree_schema.c") 128 | .file("libyang/src/tree_schema_common.c") 129 | .file("libyang/src/tree_schema_free.c") 130 | .file("libyang/src/validation.c") 131 | .file("libyang/src/xml.c") 132 | .file("libyang/src/xpath.c") 133 | .warnings(false); 134 | 135 | build.compile("yang3"); 136 | println!("cargo:root={}", env::var("OUT_DIR").unwrap()); 137 | if let Err(e) = pkg_config::Config::new().probe("libpcre2-8") { 138 | println!("cargo:warning=failed to find pcre2 library with pkg-config: {}", e); 139 | println!("cargo:warning=attempting to link without pkg-config"); 140 | println!("cargo:rustc-link-lib=pcre2-8"); 141 | } 142 | } 143 | #[cfg(not(feature = "bundled"))] 144 | { 145 | if let Err(e) = pkg_config::Config::new().probe("libyang") { 146 | println!( 147 | "cargo:warning=failed to find yang library with pkg-config: {}", 148 | e 149 | ); 150 | println!("cargo:warning=attempting to link without pkg-config"); 151 | println!("cargo:rustc-link-lib=yang"); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /libyang3-sys/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![allow( 2 | non_upper_case_globals, 3 | non_camel_case_types, 4 | non_snake_case, 5 | // Silence "128-bit integers don't currently have a known stable ABI" warnings 6 | improper_ctypes, 7 | // Silence "constants have by default a `'static` lifetime" clippy warnings 8 | clippy::redundant_static_lifetimes, 9 | // https://github.com/rust-lang/rust-bindgen/issues/1651 10 | deref_nullptr, 11 | )] 12 | 13 | include!(concat!(env!("OUT_DIR"), "/libyang3.rs")); 14 | -------------------------------------------------------------------------------- /libyang3-sys/wrapper.h: -------------------------------------------------------------------------------- 1 | #include 2 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2018" 2 | max_width = 80 3 | wrap_comments = true 4 | format_code_in_doc_comments = true 5 | -------------------------------------------------------------------------------- /src/context.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) The yang-rs Core Contributors 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | //! YANG context. 8 | 9 | use bitflags::bitflags; 10 | use std::collections::HashMap; 11 | use std::ffi::CString; 12 | use std::mem::ManuallyDrop; 13 | use std::os::raw::{c_char, c_void}; 14 | use std::path::Path; 15 | use std::slice; 16 | use std::sync::Once; 17 | 18 | use crate::error::{Error, Result}; 19 | use crate::iter::{SchemaModules, Set}; 20 | use crate::schema::{SchemaModule, SchemaNode}; 21 | use crate::utils::*; 22 | use libyang3_sys as ffi; 23 | 24 | /// Context of the YANG schemas. 25 | /// 26 | /// [Official C documentation] 27 | /// 28 | /// [Official C documentation]: https://netopeer.liberouter.org/doc/libyang/master/html/howto_context.html 29 | #[derive(Debug, PartialEq)] 30 | pub struct Context { 31 | pub(crate) raw: *mut ffi::ly_ctx, 32 | } 33 | 34 | bitflags! { 35 | /// Options to change context behavior. 36 | pub struct ContextFlags: u16 { 37 | /// All the imported modules of the schema being parsed are implemented. 38 | const ALL_IMPLEMENTED = ffi::LY_CTX_ALL_IMPLEMENTED as u16; 39 | 40 | /// Implement all imported modules "referenced" from an implemented 41 | /// module. Normally, leafrefs, augment and deviation targets are 42 | /// implemented as specified by YANG 1.1. In addition to this, implement 43 | /// any modules of nodes referenced by when and must conditions and by 44 | /// any default values. Generally, only if all these modules are 45 | /// implemented, the explicitly implemented modules can be properly 46 | /// used and instantiated in data. 47 | const REF_IMPLEMENTED = ffi::LY_CTX_REF_IMPLEMENTED as u16; 48 | 49 | /// Do not internally implement ietf-yang-library module. This option 50 | /// cannot be changed on existing context. 51 | const NO_YANGLIBRARY = ffi::LY_CTX_NO_YANGLIBRARY as u16; 52 | 53 | /// Do not search for schemas in context's searchdirs neither in current 54 | /// working directory. 55 | const DISABLE_SEARCHDIRS = ffi::LY_CTX_DISABLE_SEARCHDIRS as u16; 56 | 57 | /// Do not automatically search for schemas in current working 58 | /// directory, which is by default searched automatically (despite not 59 | /// recursively). 60 | const DISABLE_SEARCHDIR_CWD = ffi::LY_CTX_DISABLE_SEARCHDIR_CWD as u16; 61 | 62 | /// When searching for schema, prefer searchdirs instead of user callback. 63 | const PREFER_SEARCHDIRS = ffi::LY_CTX_PREFER_SEARCHDIRS as u16; 64 | } 65 | } 66 | 67 | /// Embedded module key containing the module/submodule name and optional 68 | /// revision. 69 | #[derive(Debug, Eq, Hash, PartialEq)] 70 | pub struct EmbeddedModuleKey { 71 | mod_name: &'static str, 72 | mod_rev: Option<&'static str>, 73 | submod_name: Option<&'static str>, 74 | submod_rev: Option<&'static str>, 75 | } 76 | 77 | /// A hashmap containing embedded YANG modules. 78 | pub type EmbeddedModules = HashMap; 79 | 80 | /// Callback for retrieving missing included or imported models in a custom way. 81 | pub type ModuleImportCb = unsafe extern "C" fn( 82 | mod_name: *const c_char, 83 | mod_rev: *const c_char, 84 | submod_name: *const c_char, 85 | submod_rev: *const c_char, 86 | user_data: *mut c_void, 87 | format: *mut ffi::LYS_INFORMAT::Type, 88 | module_data: *mut *const c_char, 89 | free_module_data: *mut ffi::ly_module_imp_data_free_clb, 90 | ) -> ffi::LY_ERR::Type; 91 | 92 | // ===== impl Context ===== 93 | 94 | impl Context { 95 | /// Create libyang context. 96 | /// 97 | /// Context is used to hold all information about schemas. Usually, the 98 | /// application is supposed to work with a single context in which 99 | /// libyang is holding all schemas (and other internal information) 100 | /// according to which the data trees will be processed and validated. 101 | pub fn new(options: ContextFlags) -> Result { 102 | static INIT: Once = Once::new(); 103 | let mut context = std::ptr::null_mut(); 104 | let ctx_ptr = &mut context; 105 | 106 | // Initialization routine that is called only once when the first YANG 107 | // context is created. 108 | INIT.call_once(|| { 109 | // Disable automatic logging to stderr in order to give users more 110 | // control over the handling of errors. 111 | unsafe { ffi::ly_log_options(ffi::LY_LOSTORE_LAST) }; 112 | }); 113 | 114 | let ret = unsafe { 115 | ffi::ly_ctx_new(std::ptr::null(), options.bits(), ctx_ptr) 116 | }; 117 | if ret != ffi::LY_ERR::LY_SUCCESS { 118 | // Need to construct error structure by hand. 119 | return Err(Error { 120 | errcode: ret, 121 | msg: None, 122 | path: None, 123 | apptag: None, 124 | }); 125 | } 126 | 127 | Ok(Context { raw: context }) 128 | } 129 | 130 | /// Returns a mutable raw pointer to the underlying C library representation 131 | /// of the libyang context. 132 | pub fn into_raw(self) -> *mut ffi::ly_ctx { 133 | ManuallyDrop::new(self).raw 134 | } 135 | 136 | /// Add the search path into libyang context. 137 | pub fn set_searchdir>( 138 | &mut self, 139 | search_dir: P, 140 | ) -> Result<()> { 141 | let search_dir = 142 | CString::new(search_dir.as_ref().to_str().unwrap()).unwrap(); 143 | let ret = 144 | unsafe { ffi::ly_ctx_set_searchdir(self.raw, search_dir.as_ptr()) }; 145 | if ret != ffi::LY_ERR::LY_SUCCESS { 146 | return Err(Error::new(self)); 147 | } 148 | 149 | Ok(()) 150 | } 151 | 152 | /// Clean the search path from the libyang context. 153 | /// 154 | /// To remove the recently added search path(s), use 155 | /// Context::unset_searchdir_last(). 156 | pub fn unset_searchdir>( 157 | &mut self, 158 | search_dir: P, 159 | ) -> Result<()> { 160 | let search_dir = 161 | CString::new(search_dir.as_ref().to_str().unwrap()).unwrap(); 162 | let ret = unsafe { 163 | ffi::ly_ctx_unset_searchdir(self.raw, search_dir.as_ptr()) 164 | }; 165 | if ret != ffi::LY_ERR::LY_SUCCESS { 166 | return Err(Error::new(self)); 167 | } 168 | 169 | Ok(()) 170 | } 171 | 172 | /// Clean all search paths from the libyang context. 173 | pub fn unset_searchdirs(&mut self) -> Result<()> { 174 | let ret = 175 | unsafe { ffi::ly_ctx_unset_searchdir(self.raw, std::ptr::null()) }; 176 | if ret != ffi::LY_ERR::LY_SUCCESS { 177 | return Err(Error::new(self)); 178 | } 179 | 180 | Ok(()) 181 | } 182 | 183 | /// Remove the least recently added search path(s) from the libyang context. 184 | /// 185 | /// To remove a specific search path by its value, use 186 | /// Context::unset_searchdir(). 187 | pub fn unset_searchdir_last(&mut self, count: u32) -> Result<()> { 188 | let ret = unsafe { ffi::ly_ctx_unset_searchdir_last(self.raw, count) }; 189 | if ret != ffi::LY_ERR::LY_SUCCESS { 190 | return Err(Error::new(self)); 191 | } 192 | 193 | Ok(()) 194 | } 195 | 196 | /// Set hash map containing embedded YANG modules, which are loaded on 197 | /// demand. 198 | pub fn set_embedded_modules(&mut self, modules: &EmbeddedModules) { 199 | unsafe { 200 | ffi::ly_ctx_set_module_imp_clb( 201 | self.raw, 202 | Some(ly_module_import_cb), 203 | modules as *const _ as *mut c_void, 204 | ) 205 | }; 206 | } 207 | 208 | /// Remove all embedded modules from the libyang context. 209 | pub fn unset_embedded_modules(&mut self) { 210 | unsafe { 211 | ffi::ly_ctx_set_module_imp_clb(self.raw, None, std::ptr::null_mut()) 212 | }; 213 | } 214 | 215 | /// Set missing include or import module callback. It is meant to be used 216 | /// when the models are not locally available (such as when downloading 217 | /// modules from a NETCONF server), it should not be required in other 218 | /// cases. 219 | pub unsafe fn set_module_import_callback( 220 | &mut self, 221 | module_import_cb: ModuleImportCb, 222 | user_data: *mut c_void, 223 | ) { 224 | unsafe { 225 | ffi::ly_ctx_set_module_imp_clb( 226 | self.raw, 227 | Some(module_import_cb), 228 | user_data, 229 | ) 230 | }; 231 | } 232 | 233 | /// Get the currently set context's options. 234 | pub fn get_options(&self) -> ContextFlags { 235 | let options = unsafe { ffi::ly_ctx_get_options(self.raw) }; 236 | ContextFlags::from_bits_truncate(options) 237 | } 238 | 239 | /// Set some of the context's options. 240 | pub fn set_options(&mut self, options: ContextFlags) -> Result<()> { 241 | let ret = unsafe { ffi::ly_ctx_set_options(self.raw, options.bits()) }; 242 | if ret != ffi::LY_ERR::LY_SUCCESS { 243 | return Err(Error::new(self)); 244 | } 245 | 246 | Ok(()) 247 | } 248 | 249 | /// Unset some of the context's options. 250 | pub fn unset_options(&mut self, options: ContextFlags) -> Result<()> { 251 | let ret = 252 | unsafe { ffi::ly_ctx_unset_options(self.raw, options.bits()) }; 253 | if ret != ffi::LY_ERR::LY_SUCCESS { 254 | return Err(Error::new(self)); 255 | } 256 | 257 | Ok(()) 258 | } 259 | 260 | /// Get current ID of the modules set. 261 | pub fn get_module_set_id(&self) -> u16 { 262 | unsafe { ffi::ly_ctx_get_change_count(self.raw) } 263 | } 264 | 265 | /// Get YANG module of the given name and revision. 266 | /// 267 | /// If the revision is not specified, the schema with no revision is 268 | /// returned (if it is present in the context). 269 | pub fn get_module( 270 | &self, 271 | name: &str, 272 | revision: Option<&str>, 273 | ) -> Option> { 274 | let name = CString::new(name).unwrap(); 275 | let revision_cstr; 276 | 277 | let revision_ptr = match revision { 278 | Some(revision) => { 279 | revision_cstr = CString::new(revision).unwrap(); 280 | revision_cstr.as_ptr() 281 | } 282 | None => std::ptr::null(), 283 | }; 284 | let module = unsafe { 285 | ffi::ly_ctx_get_module(self.raw, name.as_ptr(), revision_ptr) 286 | }; 287 | if module.is_null() { 288 | return None; 289 | } 290 | 291 | Some(unsafe { SchemaModule::from_raw(self, module) }) 292 | } 293 | 294 | /// Get the latest revision of the YANG module specified by its name. 295 | /// 296 | /// YANG modules with no revision are supposed to be the oldest one. 297 | pub fn get_module_latest(&self, name: &str) -> Option> { 298 | let name = CString::new(name).unwrap(); 299 | let module = 300 | unsafe { ffi::ly_ctx_get_module_latest(self.raw, name.as_ptr()) }; 301 | if module.is_null() { 302 | return None; 303 | } 304 | 305 | Some(unsafe { SchemaModule::from_raw(self, module) }) 306 | } 307 | 308 | /// Get the (only) implemented YANG module specified by its name. 309 | pub fn get_module_implemented( 310 | &self, 311 | name: &str, 312 | ) -> Option> { 313 | let name = CString::new(name).unwrap(); 314 | let module = unsafe { 315 | ffi::ly_ctx_get_module_implemented(self.raw, name.as_ptr()) 316 | }; 317 | if module.is_null() { 318 | return None; 319 | } 320 | 321 | Some(unsafe { SchemaModule::from_raw(self, module) }) 322 | } 323 | 324 | /// YANG module of the given namespace and revision. 325 | /// 326 | /// If the revision is not specified, the schema with no revision is 327 | /// returned (if it is present in the context). 328 | pub fn get_module_ns( 329 | &self, 330 | ns: &str, 331 | revision: Option<&str>, 332 | ) -> Option> { 333 | let ns = CString::new(ns).unwrap(); 334 | let revision_cstr; 335 | 336 | let revision_ptr = match revision { 337 | Some(revision) => { 338 | revision_cstr = CString::new(revision).unwrap(); 339 | revision_cstr.as_ptr() 340 | } 341 | None => std::ptr::null(), 342 | }; 343 | 344 | let module = unsafe { 345 | ffi::ly_ctx_get_module_ns(self.raw, ns.as_ptr(), revision_ptr) 346 | }; 347 | if module.is_null() { 348 | return None; 349 | } 350 | 351 | Some(unsafe { SchemaModule::from_raw(self, module) }) 352 | } 353 | 354 | /// Get the latest revision of the YANG module specified by its namespace. 355 | /// 356 | /// YANG modules with no revision are supposed to be the oldest one. 357 | pub fn get_module_latest_ns(&self, ns: &str) -> Option> { 358 | let ns = CString::new(ns).unwrap(); 359 | let module = 360 | unsafe { ffi::ly_ctx_get_module_latest_ns(self.raw, ns.as_ptr()) }; 361 | if module.is_null() { 362 | return None; 363 | } 364 | 365 | Some(unsafe { SchemaModule::from_raw(self, module) }) 366 | } 367 | 368 | /// Get the (only) implemented YANG module specified by its namespace. 369 | pub fn get_module_implemented_ns( 370 | &self, 371 | ns: &str, 372 | ) -> Option> { 373 | let ns = CString::new(ns).unwrap(); 374 | let module = unsafe { 375 | ffi::ly_ctx_get_module_implemented_ns(self.raw, ns.as_ptr()) 376 | }; 377 | if module.is_null() { 378 | return None; 379 | } 380 | 381 | Some(unsafe { SchemaModule::from_raw(self, module) }) 382 | } 383 | 384 | /// Get list of loaded modules. 385 | /// 386 | /// Internal modules (loaded during the context creation) can be skipped by 387 | /// setting "skip_internal" to true. 388 | pub fn modules(&self, skip_internal: bool) -> SchemaModules<'_> { 389 | SchemaModules::new(self, skip_internal) 390 | } 391 | 392 | /// Returns an iterator over all data nodes from all modules in the YANG 393 | /// context (depth-first search algorithm). 394 | pub fn traverse(&self) -> impl Iterator> { 395 | self.modules(false).flat_map(|module| module.traverse()) 396 | } 397 | 398 | /// Learn the number of internal modules of the context. Internal modules is 399 | /// considered one that was loaded during the context creation. 400 | pub fn internal_module_count(&self) -> u32 { 401 | unsafe { ffi::ly_ctx_internal_modules_count(self.raw) } 402 | } 403 | 404 | /// Try to find the model in the searchpaths and load it. 405 | /// 406 | /// The context itself is searched for the requested module first. If 407 | /// revision is not specified (the module of the latest revision is 408 | /// requested) and there is implemented revision of the requested module 409 | /// in the context, this implemented revision is returned despite there 410 | /// might be a newer revision. This behavior is caused by the fact that 411 | /// it is not possible to have multiple implemented revisions of 412 | /// the same module in the context. 413 | /// 414 | /// If the revision is not specified, the latest revision is loaded. 415 | /// 416 | /// The `features` parameter specifies the module features that should be 417 | /// enabled. If let empty, no features are enabled. The feature string '*' 418 | /// enables all module features. 419 | pub fn load_module( 420 | &mut self, 421 | name: &str, 422 | revision: Option<&str>, 423 | features: &[&str], 424 | ) -> Result> { 425 | let name = CString::new(name).unwrap(); 426 | let revision_cstr; 427 | let mut features_ptr; 428 | 429 | // Prepare revision string. 430 | let revision_ptr = match revision { 431 | Some(revision) => { 432 | revision_cstr = CString::new(revision).unwrap(); 433 | revision_cstr.as_ptr() 434 | } 435 | None => std::ptr::null(), 436 | }; 437 | 438 | // Prepare features array. 439 | let features_cstr = features 440 | .iter() 441 | .map(|feature| CString::new(*feature).unwrap()) 442 | .collect::>(); 443 | features_ptr = features_cstr 444 | .iter() 445 | .map(|feature| feature.as_ptr()) 446 | .collect::>(); 447 | features_ptr.push(std::ptr::null()); 448 | 449 | let module = unsafe { 450 | ffi::ly_ctx_load_module( 451 | self.raw, 452 | name.as_ptr(), 453 | revision_ptr, 454 | features_ptr.as_mut_ptr(), 455 | ) 456 | }; 457 | if module.is_null() { 458 | return Err(Error::new(self)); 459 | } 460 | 461 | Ok(unsafe { SchemaModule::from_raw(self, module as *mut _) }) 462 | } 463 | 464 | /// Evaluate an xpath expression on schema nodes. 465 | pub fn find_xpath(&self, path: &str) -> Result>> { 466 | let path = CString::new(path).unwrap(); 467 | let mut set = std::ptr::null_mut(); 468 | let set_ptr = &mut set; 469 | let options = 0u32; 470 | 471 | let ret = unsafe { 472 | ffi::lys_find_xpath( 473 | self.raw, 474 | std::ptr::null(), 475 | path.as_ptr(), 476 | options, 477 | set_ptr, 478 | ) 479 | }; 480 | if ret != ffi::LY_ERR::LY_SUCCESS { 481 | return Err(Error::new(self)); 482 | } 483 | 484 | let rnodes_count = unsafe { (*set).count } as usize; 485 | let slice = if rnodes_count == 0 { 486 | &[] 487 | } else { 488 | let rnodes = unsafe { (*set).__bindgen_anon_1.snodes }; 489 | unsafe { slice::from_raw_parts(rnodes, rnodes_count) } 490 | }; 491 | 492 | Ok(Set::new(self, slice)) 493 | } 494 | 495 | /// Get a schema node based on the given data path (JSON format). 496 | pub fn find_path(&self, path: &str) -> Result> { 497 | let path = CString::new(path).unwrap(); 498 | 499 | let rnode = unsafe { 500 | ffi::lys_find_path(self.raw, std::ptr::null(), path.as_ptr(), 0) 501 | }; 502 | if rnode.is_null() { 503 | return Err(Error::new(self)); 504 | } 505 | 506 | Ok(unsafe { SchemaNode::from_raw(self, rnode as *mut _) }) 507 | } 508 | } 509 | 510 | unsafe impl Send for Context {} 511 | unsafe impl Sync for Context {} 512 | 513 | impl Drop for Context { 514 | fn drop(&mut self) { 515 | unsafe { ffi::ly_ctx_destroy(self.raw) }; 516 | } 517 | } 518 | 519 | // ===== impl EmbeddedModuleKey ===== 520 | 521 | impl EmbeddedModuleKey { 522 | pub fn new( 523 | mod_name: &'static str, 524 | mod_rev: Option<&'static str>, 525 | submod_name: Option<&'static str>, 526 | submod_rev: Option<&'static str>, 527 | ) -> EmbeddedModuleKey { 528 | EmbeddedModuleKey { 529 | mod_name, 530 | mod_rev, 531 | submod_name, 532 | submod_rev, 533 | } 534 | } 535 | } 536 | 537 | unsafe impl<'a> Binding<'a> for Context { 538 | type CType = ffi::ly_ctx; 539 | type Container = (); 540 | 541 | unsafe fn from_raw(_: &'a Self::Container, raw: *mut Self::CType) -> Self { 542 | Self { raw } 543 | } 544 | } 545 | 546 | // ===== helper functions ===== 547 | 548 | fn find_embedded_module<'a>( 549 | modules: &'a EmbeddedModules, 550 | mod_name: &'a str, 551 | mod_rev: Option<&'a str>, 552 | submod_name: Option<&'a str>, 553 | submod_rev: Option<&'a str>, 554 | ) -> Option<(&'a EmbeddedModuleKey, &'a &'a str)> { 555 | modules.iter().find(|(key, _)| { 556 | *key.mod_name == *mod_name 557 | && (mod_rev.is_none() || key.mod_rev == mod_rev) 558 | && match submod_name { 559 | Some(submod_name) => { 560 | key.submod_name == Some(submod_name) 561 | && (submod_rev.is_none() 562 | || key.submod_rev == submod_rev) 563 | } 564 | None => key.submod_name.is_none(), 565 | } 566 | }) 567 | } 568 | 569 | unsafe extern "C" fn ly_module_import_cb( 570 | mod_name: *const c_char, 571 | mod_rev: *const c_char, 572 | submod_name: *const c_char, 573 | submod_rev: *const c_char, 574 | user_data: *mut c_void, 575 | format: *mut ffi::LYS_INFORMAT::Type, 576 | module_data: *mut *const c_char, 577 | _free_module_data: *mut ffi::ly_module_imp_data_free_clb, 578 | ) -> ffi::LY_ERR::Type { 579 | let modules = &*(user_data as *const EmbeddedModules); 580 | let mod_name = char_ptr_to_str(mod_name); 581 | let mod_rev = char_ptr_to_opt_str(mod_rev); 582 | let submod_name = char_ptr_to_opt_str(submod_name); 583 | let submod_rev = char_ptr_to_opt_str(submod_rev); 584 | 585 | if let Some((_emod_key, emod_data)) = find_embedded_module( 586 | modules, 587 | mod_name, 588 | mod_rev, 589 | submod_name, 590 | submod_rev, 591 | ) { 592 | let data = CString::new(*emod_data).unwrap(); 593 | 594 | *format = ffi::LYS_INFORMAT::LYS_IN_YANG; 595 | *module_data = data.as_ptr(); 596 | std::mem::forget(data); 597 | return ffi::LY_ERR::LY_SUCCESS; 598 | } 599 | 600 | ffi::LY_ERR::LY_ENOTFOUND 601 | } 602 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) The yang-rs Core Contributors 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | use crate::context::Context; 8 | use crate::utils::*; 9 | use libyang3_sys as ffi; 10 | 11 | /// A convenience wrapper around `Result` for `yang3::Error`. 12 | pub type Result = std::result::Result; 13 | 14 | /// Enum listing possible errors from yang3. 15 | #[derive(Debug, Eq, PartialEq)] 16 | pub struct Error { 17 | pub errcode: ffi::LY_ERR::Type, 18 | pub msg: Option, 19 | pub path: Option, 20 | pub apptag: Option, 21 | } 22 | 23 | impl Error { 24 | pub fn new(ctx: &Context) -> Error { 25 | let error = unsafe { ffi::ly_err_last(ctx.raw) }; 26 | if error.is_null() { 27 | return Self { 28 | ..Default::default() 29 | }; 30 | } 31 | 32 | let errcode = unsafe { (*error).err }; 33 | let msg = unsafe { char_ptr_to_opt_string((*error).msg, false) }; 34 | let path = unsafe { char_ptr_to_opt_string((*error).data_path, false) }; 35 | let apptag = unsafe { char_ptr_to_opt_string((*error).apptag, false) }; 36 | 37 | Self { 38 | errcode, 39 | msg, 40 | path, 41 | apptag, 42 | } 43 | } 44 | } 45 | 46 | impl Default for Error { 47 | fn default() -> Self { 48 | Self { 49 | errcode: ffi::LY_ERR::LY_EOTHER, 50 | msg: None, 51 | path: None, 52 | apptag: None, 53 | } 54 | } 55 | } 56 | 57 | impl std::fmt::Display for Error { 58 | // Print only the base error message by default. 59 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 60 | if let Some(msg) = &self.msg { 61 | write!(f, "{}", msg) 62 | } else { 63 | write!(f, "Unknown error: {}", self.errcode) 64 | } 65 | } 66 | } 67 | 68 | impl std::error::Error for Error {} 69 | -------------------------------------------------------------------------------- /src/iter.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) The yang-rs Core Contributors 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | //! YANG iterators. 8 | 9 | use crate::context::Context; 10 | use crate::data::Metadata; 11 | use crate::schema::{SchemaModule, SchemaNode}; 12 | use crate::utils::Binding; 13 | use bitflags::bitflags; 14 | use libyang3_sys as ffi; 15 | 16 | /// Common methods used by multiple data and schema node iterators. 17 | #[doc(hidden)] 18 | pub trait NodeIterable<'a>: Sized + Clone + PartialEq + Binding<'a> { 19 | /// Returns the parent node. 20 | fn parent(&self) -> Option; 21 | 22 | /// Returns the next sibling node. 23 | fn next_sibling(&self) -> Option; 24 | 25 | /// Returns the fist child none. 26 | fn first_child(&self) -> Option; 27 | } 28 | 29 | /// An iterator over the siblings of a node. 30 | #[derive(Debug)] 31 | pub struct Siblings<'a, T> 32 | where 33 | T: NodeIterable<'a>, 34 | { 35 | next: Option, 36 | _marker: std::marker::PhantomData<&'a T>, 37 | } 38 | 39 | /// An iterator over the ancestors of a node. 40 | #[derive(Debug)] 41 | pub struct Ancestors<'a, T> 42 | where 43 | T: NodeIterable<'a>, 44 | { 45 | next: Option, 46 | _marker: std::marker::PhantomData<&'a T>, 47 | } 48 | 49 | /// An iterator over all elements in a tree (depth-first search algorithm). 50 | /// 51 | /// When traversing over schema trees, note that _actions_ and _notifications_ 52 | /// are ignored. 53 | #[derive(Debug)] 54 | pub struct Traverse<'a, T> 55 | where 56 | T: NodeIterable<'a>, 57 | { 58 | start: T, 59 | next: Option, 60 | _marker: std::marker::PhantomData<&'a T>, 61 | } 62 | 63 | /// An customizable iterator over the children of a node. 64 | #[derive(Debug)] 65 | pub struct Getnext<'a> { 66 | flags: IterSchemaFlags, 67 | last: Option>, 68 | parent: Option>, 69 | module: Option>, 70 | } 71 | 72 | bitflags! { 73 | /// Various options that control iteration behavior. 74 | #[derive(Debug)] 75 | pub struct IterSchemaFlags: u32 { 76 | /// Return #LYS_CHOICE nodes instead of looking into them. 77 | const WITH_CHOICE = ffi::LYS_GETNEXT_WITHCHOICE; 78 | /// Ignore (kind of conditional) nodes within choice node. 79 | const NO_CHOICE = ffi::LYS_GETNEXT_NOCHOICE; 80 | /// Allow returning #LYS_CASE nodes instead of looking into them. 81 | const WITH_CASE = ffi::LYS_GETNEXT_WITHCASE; 82 | /// Look into non-presence container, instead of returning container 83 | /// itself. 84 | const INTO_NP_CONT = ffi::LYS_GETNEXT_INTONPCONT; 85 | /// Provide RPC's/action's output schema nodes instead of input schema 86 | /// nodes provided by default. 87 | const OUTPUT = ffi::LYS_GETNEXT_OUTPUT; 88 | } 89 | } 90 | 91 | /// An iterator over a set of nodes. 92 | /// 93 | /// This is a safe wrapper around ffi::ly_set. 94 | #[derive(Debug)] 95 | pub struct Set<'a, T> 96 | where 97 | T: NodeIterable<'a>, 98 | { 99 | container: &'a T::Container, 100 | slice: &'a [*mut T::CType], 101 | } 102 | 103 | /// An iterator over an array of nodes or substatements. 104 | /// 105 | /// This is a safe wrapper around libyang3's 106 | /// [sized arrays](https://netopeer.liberouter.org/doc/libyang/master/html/howto_structures.html). 107 | #[derive(Debug)] 108 | pub struct Array<'a, S: Binding<'a>> { 109 | context: &'a Context, 110 | raw: *mut S::CType, 111 | ptr_size: usize, 112 | count: usize, 113 | } 114 | 115 | /// An iterator over a list of schema modules. 116 | #[derive(Debug)] 117 | pub struct SchemaModules<'a> { 118 | context: &'a Context, 119 | index: u32, 120 | } 121 | 122 | /// An iterator over a list of metadata. 123 | #[derive(Debug)] 124 | pub struct MetadataList<'a> { 125 | next: Option>, 126 | } 127 | 128 | // ===== impl Siblings ===== 129 | 130 | impl<'a, T> Siblings<'a, T> 131 | where 132 | T: NodeIterable<'a>, 133 | { 134 | pub fn new(next: Option) -> Siblings<'a, T> { 135 | Siblings { 136 | next, 137 | _marker: std::marker::PhantomData, 138 | } 139 | } 140 | } 141 | 142 | impl<'a, T> Iterator for Siblings<'a, T> 143 | where 144 | T: NodeIterable<'a>, 145 | { 146 | type Item = T; 147 | 148 | fn next(&mut self) -> Option { 149 | let ret = self.next.clone(); 150 | if let Some(next) = &self.next { 151 | self.next = next.next_sibling(); 152 | } 153 | ret 154 | } 155 | } 156 | 157 | // ===== impl Ancestors ===== 158 | 159 | impl<'a, T> Ancestors<'a, T> 160 | where 161 | T: NodeIterable<'a>, 162 | { 163 | pub fn new(next: Option) -> Ancestors<'a, T> { 164 | Ancestors { 165 | next, 166 | _marker: std::marker::PhantomData, 167 | } 168 | } 169 | } 170 | 171 | impl<'a, T> Iterator for Ancestors<'a, T> 172 | where 173 | T: NodeIterable<'a>, 174 | { 175 | type Item = T; 176 | 177 | fn next(&mut self) -> Option { 178 | let node = self.next.clone(); 179 | if let Some(next) = &self.next { 180 | self.next = next.parent(); 181 | } 182 | node 183 | } 184 | } 185 | 186 | // ===== impl Traverse ===== 187 | 188 | impl<'a, T> Traverse<'a, T> 189 | where 190 | T: NodeIterable<'a>, 191 | { 192 | pub fn new(start: T) -> Traverse<'a, T> { 193 | let next = start.clone(); 194 | 195 | Traverse { 196 | start, 197 | next: Some(next), 198 | _marker: std::marker::PhantomData, 199 | } 200 | } 201 | } 202 | 203 | impl<'a, T> Iterator for Traverse<'a, T> 204 | where 205 | T: NodeIterable<'a>, 206 | { 207 | type Item = T; 208 | 209 | fn next(&mut self) -> Option { 210 | let ret = self.next.clone(); 211 | 212 | if let Some(elem) = &mut self.next { 213 | // Select element for the next run - children first. 214 | let mut next_elem = elem.first_child(); 215 | if next_elem.is_none() { 216 | // Check end condition. 217 | if *elem == self.start { 218 | self.next = None; 219 | return ret; 220 | } 221 | 222 | // No children, try siblings. 223 | next_elem = elem.next_sibling(); 224 | } 225 | 226 | while next_elem.is_none() { 227 | // Parent is already processed, go to its sibling. 228 | *elem = elem.parent().unwrap(); 229 | 230 | // Check end condition. 231 | if *elem == self.start { 232 | self.next = None; 233 | return ret; 234 | } 235 | next_elem = elem.next_sibling(); 236 | } 237 | 238 | *elem = next_elem.unwrap(); 239 | } 240 | 241 | ret 242 | } 243 | } 244 | 245 | // ===== impl Getnext ===== 246 | 247 | impl<'a> Getnext<'a> { 248 | pub fn new( 249 | flags: IterSchemaFlags, 250 | parent: Option>, 251 | module: Option>, 252 | ) -> Getnext<'a> { 253 | Getnext { 254 | flags, 255 | last: None, 256 | parent, 257 | module, 258 | } 259 | } 260 | } 261 | 262 | impl<'a> Iterator for Getnext<'a> { 263 | type Item = SchemaNode<'a>; 264 | 265 | fn next(&mut self) -> Option> { 266 | let last = self.last.take(); 267 | let parent = self.parent.clone(); 268 | let module = self.module.clone(); 269 | 270 | let last_raw = 271 | last.map(|snode| snode.raw as _).unwrap_or(std::ptr::null()); 272 | let parent_raw = parent 273 | .as_ref() 274 | .map(|snode| snode.raw as _) 275 | .unwrap_or(std::ptr::null()); 276 | let module_raw = module 277 | .as_ref() 278 | .map(|smodule| unsafe { (*smodule.raw).compiled } as _) 279 | .unwrap_or(std::ptr::null()); 280 | let next = unsafe { 281 | ffi::lys_getnext( 282 | last_raw, 283 | parent_raw, 284 | module_raw, 285 | self.flags.bits(), 286 | ) 287 | }; 288 | 289 | let context = parent 290 | .map(|snode| snode.context) 291 | .or(module.map(|smodule| smodule.context)) 292 | .unwrap(); 293 | let next = unsafe { SchemaNode::from_raw_opt(context, next as *mut _) }; 294 | self.last = next.clone(); 295 | next 296 | } 297 | } 298 | 299 | // ===== impl Set ===== 300 | 301 | impl<'a, T> Set<'a, T> 302 | where 303 | T: NodeIterable<'a>, 304 | { 305 | pub fn new( 306 | container: &'a T::Container, 307 | slice: &'a [*mut T::CType], 308 | ) -> Set<'a, T> { 309 | Set { container, slice } 310 | } 311 | } 312 | 313 | impl<'a, T> Iterator for Set<'a, T> 314 | where 315 | T: NodeIterable<'a>, 316 | { 317 | type Item = T; 318 | 319 | fn next(&mut self) -> Option { 320 | if !self.slice.is_empty() { 321 | let dnode = 322 | Some(unsafe { T::from_raw(self.container, self.slice[0]) }); 323 | self.slice = &self.slice[1..]; 324 | dnode 325 | } else { 326 | None 327 | } 328 | } 329 | 330 | fn size_hint(&self) -> (usize, Option) { 331 | (0, Some(self.slice.len())) 332 | } 333 | } 334 | 335 | unsafe impl<'a, T> Send for Set<'a, T> where T: NodeIterable<'a> {} 336 | unsafe impl<'a, T> Sync for Set<'a, T> where T: NodeIterable<'a> {} 337 | 338 | // ===== impl Array ===== 339 | 340 | impl<'a, S> Array<'a, S> 341 | where 342 | S: Binding<'a>, 343 | { 344 | pub fn new( 345 | context: &'a Context, 346 | raw: *mut S::CType, 347 | ptr_size: usize, 348 | ) -> Array<'a, S> { 349 | // Get the number of records in the array (equivalent to 350 | // LY_ARRAY_COUNT). 351 | let count = if raw.is_null() { 352 | 0 353 | } else { 354 | unsafe { (raw as *const usize).offset(-1).read() } 355 | }; 356 | 357 | Array { 358 | context, 359 | raw, 360 | ptr_size, 361 | count, 362 | } 363 | } 364 | } 365 | 366 | impl<'a, S> Iterator for Array<'a, S> 367 | where 368 | S: Binding<'a, Container = Context>, 369 | { 370 | type Item = S; 371 | 372 | fn next(&mut self) -> Option { 373 | if self.count > 0 { 374 | let next = unsafe { S::from_raw_opt(self.context, self.raw) }; 375 | self.count -= 1; 376 | self.raw = (self.raw as usize + self.ptr_size) as *mut S::CType; 377 | next 378 | } else { 379 | None 380 | } 381 | } 382 | 383 | fn size_hint(&self) -> (usize, Option) { 384 | (0, Some(self.count)) 385 | } 386 | } 387 | 388 | unsafe impl<'a, S> Send for Array<'a, S> where S: NodeIterable<'a> {} 389 | unsafe impl<'a, S> Sync for Array<'a, S> where S: NodeIterable<'a> {} 390 | 391 | // ===== impl SchemaModules ===== 392 | 393 | impl<'a> SchemaModules<'a> { 394 | pub fn new(context: &'a Context, skip_internal: bool) -> SchemaModules<'a> { 395 | let index = if skip_internal { 396 | context.internal_module_count() 397 | } else { 398 | 0 399 | }; 400 | SchemaModules { context, index } 401 | } 402 | } 403 | 404 | impl<'a> Iterator for SchemaModules<'a> { 405 | type Item = SchemaModule<'a>; 406 | 407 | fn next(&mut self) -> Option> { 408 | let rmodule = unsafe { 409 | ffi::ly_ctx_get_module_iter(self.context.raw, &mut self.index) 410 | }; 411 | unsafe { SchemaModule::from_raw_opt(self.context, rmodule as *mut _) } 412 | } 413 | } 414 | 415 | // ===== impl MetadataList ===== 416 | 417 | impl<'a> MetadataList<'a> { 418 | pub fn new(next: Option>) -> MetadataList<'a> { 419 | MetadataList { next } 420 | } 421 | } 422 | 423 | impl<'a> Iterator for MetadataList<'a> { 424 | type Item = Metadata<'a>; 425 | 426 | fn next(&mut self) -> Option> { 427 | let meta = self.next.clone(); 428 | if let Some(next) = &self.next { 429 | self.next = next.next(); 430 | } 431 | meta 432 | } 433 | } 434 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) The yang-rs Core Contributors 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | //! Rust bindings for the [libyang3] library. 8 | //! 9 | //! For raw FFI bindings for libyang3, see [libyang3-sys]. 10 | //! 11 | //! [libyang3]: https://github.com/CESNET/libyang/tree/master 12 | //! [libyang3-sys]: https://github.com/holo-routing/yang-rs/tree/master/libyang3-sys 13 | //! 14 | //! ## Design Goals 15 | //! * Provide high-level bindings for libyang3 using idiomatic Rust 16 | //! * Leverage Rust's ownership system to detect API misuse problems at compile 17 | //! time 18 | //! * Automatic resource management 19 | //! * Zero-cost abstractions 20 | //! 21 | //! ## Feature flags 22 | //! By default, yang-rs uses pre-generated FFI bindings and uses dynamic 23 | //! linking to load libyang3. The following feature flags, however, can be used 24 | //! to change that behavior: 25 | //! * **bundled**: instructs cargo to download and build libyang3 from the 26 | //! sources. The resulting objects are grouped into a static archive linked to 27 | //! this crate. This feature can be used when having a libyang3 dynamic link 28 | //! dependency isn't desirable. 29 | //! * Additional build requirements: *cc 1.0*, *cmake 0.1*, a C compiler and 30 | //! CMake. 31 | //! * **use_bindgen**: generate new C FFI bindings dynamically instead of using 32 | //! the pre-generated ones. Useful when updating this crate to use newer 33 | //! libyang3 versions. 34 | //! * Additional build requirements: *bindgen 0.68.0* 35 | //! 36 | //! ## Examples 37 | //! 38 | //! See 39 | 40 | mod error; 41 | 42 | pub mod context; 43 | pub mod data; 44 | pub mod iter; 45 | pub mod schema; 46 | pub mod utils; 47 | 48 | pub use crate::error::Error; 49 | 50 | // Re-export the raw FFI bindings for convenience. 51 | pub use libyang3_sys as ffi; 52 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | // 2 | // Copyright (c) The yang-rs Core Contributors 3 | // 4 | // SPDX-License-Identifier: MIT 5 | // 6 | 7 | use libyang3_sys as ffi; 8 | use std::ffi::CStr; 9 | use std::os::raw::c_char; 10 | 11 | /// Convert C String to owned string. 12 | pub(crate) fn char_ptr_to_string(c_str: *const c_char, free: bool) -> String { 13 | let string = 14 | unsafe { CStr::from_ptr(c_str).to_string_lossy().into_owned() }; 15 | if free { 16 | unsafe { ffi::free(c_str as *mut std::ffi::c_void) }; 17 | } 18 | string 19 | } 20 | 21 | /// Convert C String to optional owned string. 22 | pub(crate) fn char_ptr_to_opt_string( 23 | c_str: *const c_char, 24 | free: bool, 25 | ) -> Option { 26 | if c_str.is_null() { 27 | None 28 | } else { 29 | Some(char_ptr_to_string(c_str, free)) 30 | } 31 | } 32 | 33 | /// Convert C String to string slice. 34 | pub(crate) fn char_ptr_to_str<'a>(c_str: *const c_char) -> &'a str { 35 | unsafe { CStr::from_ptr(c_str).to_str().unwrap() } 36 | } 37 | 38 | /// Convert C String to optional string slice. 39 | pub(crate) fn char_ptr_to_opt_str<'a>(c_str: *const c_char) -> Option<&'a str> { 40 | if c_str.is_null() { 41 | None 42 | } else { 43 | Some(char_ptr_to_str(c_str)) 44 | } 45 | } 46 | 47 | /// A trait implemented by all types that can be created from a raw C pointer 48 | /// and a generic container type. 49 | pub unsafe trait Binding<'a> 50 | where 51 | Self: Sized, 52 | >::Container: 'a, 53 | { 54 | type CType; 55 | type Container; 56 | 57 | unsafe fn from_raw( 58 | container: &'a Self::Container, 59 | raw: *mut Self::CType, 60 | ) -> Self; 61 | 62 | unsafe fn from_raw_opt( 63 | container: &'a Self::Container, 64 | raw: *mut Self::CType, 65 | ) -> Option { 66 | if raw.is_null() { 67 | None 68 | } else { 69 | Some(Self::from_raw(container, raw)) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /tests/schema.rs: -------------------------------------------------------------------------------- 1 | use yang3::context::{Context, ContextFlags}; 2 | use yang3::iter::IterSchemaFlags; 3 | use yang3::schema::{ 4 | DataValue, DataValueType, SchemaNodeKind, SchemaPathFormat, 5 | }; 6 | 7 | static SEARCH_DIR: &str = "./assets/yang/"; 8 | 9 | fn create_context() -> Context { 10 | // Initialize context. 11 | let mut ctx = Context::new(ContextFlags::NO_YANGLIBRARY) 12 | .expect("Failed to create context"); 13 | ctx.set_searchdir(SEARCH_DIR) 14 | .expect("Failed to set YANG search directory"); 15 | 16 | // Load YANG modules. 17 | ctx.load_module("ietf-interfaces", None, &["pre-provisioning"]) 18 | .expect("Failed to load module"); 19 | ctx.load_module("iana-if-type", None, &[]) 20 | .expect("Failed to load module"); 21 | ctx.load_module("ietf-key-chain", None, &["hex-key-string"]) 22 | .expect("Failed to load module"); 23 | ctx.load_module("ietf-routing", None, &[]) 24 | .expect("Failed to load module"); 25 | ctx.load_module("ietf-mpls-ldp", None, &[]) 26 | .expect("Failed to load module"); 27 | 28 | ctx 29 | } 30 | 31 | #[test] 32 | fn schema_feature_value() { 33 | let ctx = create_context(); 34 | let module = ctx.get_module_latest("ietf-interfaces").unwrap(); 35 | assert_eq!(module.feature_value("pre-provisioning"), Ok(true)); 36 | assert_eq!(module.feature_value("if-mib"), Ok(false)); 37 | assert!(module.feature_value("blabla").is_err()); 38 | } 39 | 40 | #[test] 41 | fn schema_find_xpath() { 42 | let ctx = create_context(); 43 | 44 | assert_eq!( 45 | ctx.find_xpath("/ietf-interfaces:interfaces/*") 46 | .expect("Failed to lookup schema data") 47 | .map(|dnode| dnode.path(SchemaPathFormat::DATA)) 48 | .collect::>(), 49 | vec!["/ietf-interfaces:interfaces/interface"] 50 | ); 51 | 52 | assert_eq!( 53 | ctx.find_xpath("/ietf-interfaces:interfaces/interface/*") 54 | .expect("Failed to lookup schema data") 55 | .map(|dnode| dnode.path(SchemaPathFormat::DATA)) 56 | .collect::>(), 57 | vec![ 58 | "/ietf-interfaces:interfaces/interface/name", 59 | "/ietf-interfaces:interfaces/interface/description", 60 | "/ietf-interfaces:interfaces/interface/type", 61 | "/ietf-interfaces:interfaces/interface/enabled", 62 | "/ietf-interfaces:interfaces/interface/oper-status", 63 | "/ietf-interfaces:interfaces/interface/last-change", 64 | "/ietf-interfaces:interfaces/interface/phys-address", 65 | "/ietf-interfaces:interfaces/interface/higher-layer-if", 66 | "/ietf-interfaces:interfaces/interface/lower-layer-if", 67 | "/ietf-interfaces:interfaces/interface/speed", 68 | "/ietf-interfaces:interfaces/interface/statistics", 69 | ] 70 | ); 71 | } 72 | 73 | #[test] 74 | fn schema_find_path() { 75 | let ctx = create_context(); 76 | 77 | assert!(ctx 78 | .find_path("/ietf-interfaces:interfaces/interface/*") 79 | .is_err()); 80 | assert!(ctx 81 | .find_path("/ietf-interfaces:interfaces/interface") 82 | .is_ok()); 83 | } 84 | 85 | #[test] 86 | fn schema_iterator_traverse() { 87 | let ctx = create_context(); 88 | 89 | assert_eq!( 90 | ctx 91 | .traverse() 92 | .filter(|snode| snode.module().name() == "ietf-interfaces") 93 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 94 | .collect::>(), 95 | vec![ 96 | "/ietf-interfaces:interfaces", 97 | "/ietf-interfaces:interfaces/interface", 98 | "/ietf-interfaces:interfaces/interface/name", 99 | "/ietf-interfaces:interfaces/interface/description", 100 | "/ietf-interfaces:interfaces/interface/type", 101 | "/ietf-interfaces:interfaces/interface/enabled", 102 | "/ietf-interfaces:interfaces/interface/oper-status", 103 | "/ietf-interfaces:interfaces/interface/last-change", 104 | "/ietf-interfaces:interfaces/interface/phys-address", 105 | "/ietf-interfaces:interfaces/interface/higher-layer-if", 106 | "/ietf-interfaces:interfaces/interface/lower-layer-if", 107 | "/ietf-interfaces:interfaces/interface/speed", 108 | "/ietf-interfaces:interfaces/interface/statistics", 109 | "/ietf-interfaces:interfaces/interface/statistics/discontinuity-time", 110 | "/ietf-interfaces:interfaces/interface/statistics/in-octets", 111 | "/ietf-interfaces:interfaces/interface/statistics/in-unicast-pkts", 112 | "/ietf-interfaces:interfaces/interface/statistics/in-broadcast-pkts", 113 | "/ietf-interfaces:interfaces/interface/statistics/in-multicast-pkts", 114 | "/ietf-interfaces:interfaces/interface/statistics/in-discards", 115 | "/ietf-interfaces:interfaces/interface/statistics/in-errors", 116 | "/ietf-interfaces:interfaces/interface/statistics/in-unknown-protos", 117 | "/ietf-interfaces:interfaces/interface/statistics/out-octets", 118 | "/ietf-interfaces:interfaces/interface/statistics/out-unicast-pkts", 119 | "/ietf-interfaces:interfaces/interface/statistics/out-broadcast-pkts", 120 | "/ietf-interfaces:interfaces/interface/statistics/out-multicast-pkts", 121 | "/ietf-interfaces:interfaces/interface/statistics/out-discards", 122 | "/ietf-interfaces:interfaces/interface/statistics/out-errors", 123 | "/ietf-interfaces:interfaces-state", 124 | "/ietf-interfaces:interfaces-state/interface", 125 | "/ietf-interfaces:interfaces-state/interface/name", 126 | "/ietf-interfaces:interfaces-state/interface/type", 127 | "/ietf-interfaces:interfaces-state/interface/oper-status", 128 | "/ietf-interfaces:interfaces-state/interface/last-change", 129 | "/ietf-interfaces:interfaces-state/interface/phys-address", 130 | "/ietf-interfaces:interfaces-state/interface/higher-layer-if", 131 | "/ietf-interfaces:interfaces-state/interface/lower-layer-if", 132 | "/ietf-interfaces:interfaces-state/interface/speed", 133 | "/ietf-interfaces:interfaces-state/interface/statistics", 134 | "/ietf-interfaces:interfaces-state/interface/statistics/discontinuity-time", 135 | "/ietf-interfaces:interfaces-state/interface/statistics/in-octets", 136 | "/ietf-interfaces:interfaces-state/interface/statistics/in-unicast-pkts", 137 | "/ietf-interfaces:interfaces-state/interface/statistics/in-broadcast-pkts", 138 | "/ietf-interfaces:interfaces-state/interface/statistics/in-multicast-pkts", 139 | "/ietf-interfaces:interfaces-state/interface/statistics/in-discards", 140 | "/ietf-interfaces:interfaces-state/interface/statistics/in-errors", 141 | "/ietf-interfaces:interfaces-state/interface/statistics/in-unknown-protos", 142 | "/ietf-interfaces:interfaces-state/interface/statistics/out-octets", 143 | "/ietf-interfaces:interfaces-state/interface/statistics/out-unicast-pkts", 144 | "/ietf-interfaces:interfaces-state/interface/statistics/out-broadcast-pkts", 145 | "/ietf-interfaces:interfaces-state/interface/statistics/out-multicast-pkts", 146 | "/ietf-interfaces:interfaces-state/interface/statistics/out-discards", 147 | "/ietf-interfaces:interfaces-state/interface/statistics/out-errors" 148 | ] 149 | ); 150 | } 151 | 152 | #[test] 153 | fn schema_iterator_ancestors() { 154 | let ctx = create_context(); 155 | 156 | assert_eq!( 157 | ctx 158 | .find_path("/ietf-interfaces:interfaces/interface/statistics/discontinuity-time") 159 | .expect("Failed to lookup schema data") 160 | .ancestors() 161 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 162 | .collect::>(), 163 | vec![ 164 | "/ietf-interfaces:interfaces/interface/statistics", 165 | "/ietf-interfaces:interfaces/interface", 166 | "/ietf-interfaces:interfaces", 167 | ] 168 | ); 169 | assert_eq!( 170 | ctx 171 | .find_path("/ietf-interfaces:interfaces/interface/statistics/discontinuity-time") 172 | .expect("Failed to lookup schema data") 173 | .inclusive_ancestors() 174 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 175 | .collect::>(), 176 | vec![ 177 | "/ietf-interfaces:interfaces/interface/statistics/discontinuity-time", 178 | "/ietf-interfaces:interfaces/interface/statistics", 179 | "/ietf-interfaces:interfaces/interface", 180 | "/ietf-interfaces:interfaces", 181 | ] 182 | ); 183 | } 184 | 185 | #[test] 186 | fn schema_iterator_siblings() { 187 | let ctx = create_context(); 188 | 189 | assert_eq!( 190 | ctx.find_path("/ietf-interfaces:interfaces/interface/name") 191 | .expect("Failed to lookup schema data") 192 | .siblings() 193 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 194 | .collect::>(), 195 | vec![ 196 | "/ietf-interfaces:interfaces/interface/description", 197 | "/ietf-interfaces:interfaces/interface/type", 198 | "/ietf-interfaces:interfaces/interface/enabled", 199 | "/ietf-interfaces:interfaces/interface/oper-status", 200 | "/ietf-interfaces:interfaces/interface/last-change", 201 | "/ietf-interfaces:interfaces/interface/phys-address", 202 | "/ietf-interfaces:interfaces/interface/higher-layer-if", 203 | "/ietf-interfaces:interfaces/interface/lower-layer-if", 204 | "/ietf-interfaces:interfaces/interface/speed", 205 | "/ietf-interfaces:interfaces/interface/statistics", 206 | ] 207 | ); 208 | assert_eq!( 209 | ctx.find_path("/ietf-interfaces:interfaces/interface/name") 210 | .expect("Failed to lookup schema data") 211 | .inclusive_siblings() 212 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 213 | .collect::>(), 214 | vec![ 215 | "/ietf-interfaces:interfaces/interface/name", 216 | "/ietf-interfaces:interfaces/interface/description", 217 | "/ietf-interfaces:interfaces/interface/type", 218 | "/ietf-interfaces:interfaces/interface/enabled", 219 | "/ietf-interfaces:interfaces/interface/oper-status", 220 | "/ietf-interfaces:interfaces/interface/last-change", 221 | "/ietf-interfaces:interfaces/interface/phys-address", 222 | "/ietf-interfaces:interfaces/interface/higher-layer-if", 223 | "/ietf-interfaces:interfaces/interface/lower-layer-if", 224 | "/ietf-interfaces:interfaces/interface/speed", 225 | "/ietf-interfaces:interfaces/interface/statistics", 226 | ] 227 | ); 228 | } 229 | 230 | #[test] 231 | fn schema_iterator_children() { 232 | let ctx = create_context(); 233 | 234 | assert_eq!( 235 | ctx 236 | .find_path("/ietf-interfaces:interfaces/interface/statistics") 237 | .expect("Failed to lookup schema data") 238 | .children() 239 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 240 | .collect::>(), 241 | vec![ 242 | "/ietf-interfaces:interfaces/interface/statistics/discontinuity-time", 243 | "/ietf-interfaces:interfaces/interface/statistics/in-octets", 244 | "/ietf-interfaces:interfaces/interface/statistics/in-unicast-pkts", 245 | "/ietf-interfaces:interfaces/interface/statistics/in-broadcast-pkts", 246 | "/ietf-interfaces:interfaces/interface/statistics/in-multicast-pkts", 247 | "/ietf-interfaces:interfaces/interface/statistics/in-discards", 248 | "/ietf-interfaces:interfaces/interface/statistics/in-errors", 249 | "/ietf-interfaces:interfaces/interface/statistics/in-unknown-protos", 250 | "/ietf-interfaces:interfaces/interface/statistics/out-octets", 251 | "/ietf-interfaces:interfaces/interface/statistics/out-unicast-pkts", 252 | "/ietf-interfaces:interfaces/interface/statistics/out-broadcast-pkts", 253 | "/ietf-interfaces:interfaces/interface/statistics/out-multicast-pkts", 254 | "/ietf-interfaces:interfaces/interface/statistics/out-discards", 255 | "/ietf-interfaces:interfaces/interface/statistics/out-errors", 256 | ] 257 | ); 258 | 259 | assert_eq!( 260 | ctx.find_path("/ietf-routing:routing/ribs/rib") 261 | .expect("Failed to lookup schema data") 262 | .children() 263 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 264 | .collect::>(), 265 | vec![ 266 | "/ietf-routing:routing/ribs/rib/name", 267 | "/ietf-routing:routing/ribs/rib/address-family", 268 | "/ietf-routing:routing/ribs/rib/routes", 269 | "/ietf-routing:routing/ribs/rib/description" 270 | ] 271 | ); 272 | 273 | assert_eq!( 274 | ctx.find_path("/ietf-routing:routing/ribs/rib") 275 | .expect("Failed to lookup schema data") 276 | .all_children() 277 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 278 | .collect::>(), 279 | vec![ 280 | "/ietf-routing:routing/ribs/rib/name", 281 | "/ietf-routing:routing/ribs/rib/address-family", 282 | "/ietf-routing:routing/ribs/rib/routes", 283 | "/ietf-routing:routing/ribs/rib/description", 284 | "/ietf-routing:routing/ribs/rib/active-route" 285 | ] 286 | ); 287 | } 288 | 289 | #[test] 290 | fn schema_iterator_children2() { 291 | let ctx = create_context(); 292 | 293 | assert_eq!( 294 | ctx.find_path("/ietf-key-chain:key-chains/key-chain/key/key-string") 295 | .expect("Failed to lookup schema data") 296 | .children2(IterSchemaFlags::empty()) 297 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 298 | .collect::>(), 299 | vec![ 300 | "/ietf-key-chain:key-chains/key-chain/key/key-string/keystring", 301 | "/ietf-key-chain:key-chains/key-chain/key/key-string/hexadecimal-string" 302 | ] 303 | ); 304 | 305 | assert_eq!( 306 | ctx.find_path("/ietf-key-chain:key-chains/key-chain/key/key-string") 307 | .expect("Failed to lookup schema data") 308 | .children2(IterSchemaFlags::NO_CHOICE) 309 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 310 | .collect::>(), 311 | Vec::::new() 312 | ); 313 | 314 | assert_eq!( 315 | ctx.find_path("/ietf-key-chain:key-chains/key-chain/key") 316 | .expect("Failed to lookup schema data") 317 | .children2(IterSchemaFlags::empty()) 318 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 319 | .collect::>(), 320 | vec![ 321 | "/ietf-key-chain:key-chains/key-chain/key/key-id", 322 | "/ietf-key-chain:key-chains/key-chain/key/lifetime", 323 | "/ietf-key-chain:key-chains/key-chain/key/crypto-algorithm", 324 | "/ietf-key-chain:key-chains/key-chain/key/key-string", 325 | "/ietf-key-chain:key-chains/key-chain/key/send-lifetime-active", 326 | "/ietf-key-chain:key-chains/key-chain/key/accept-lifetime-active" 327 | ] 328 | ); 329 | 330 | assert_eq!( 331 | ctx.find_path("/ietf-key-chain:key-chains/key-chain/key") 332 | .expect("Failed to lookup schema data") 333 | .children2(IterSchemaFlags::INTO_NP_CONT) 334 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 335 | .collect::>(), 336 | vec![ 337 | "/ietf-key-chain:key-chains/key-chain/key/key-id", 338 | "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/always", 339 | "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/start-date-time", 340 | "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/no-end-time", 341 | "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/duration", 342 | "/ietf-key-chain:key-chains/key-chain/key/lifetime/send-accept-lifetime/end-date-time", 343 | "/ietf-key-chain:key-chains/key-chain/key/crypto-algorithm", 344 | "/ietf-key-chain:key-chains/key-chain/key/key-string/keystring", 345 | "/ietf-key-chain:key-chains/key-chain/key/key-string/hexadecimal-string", 346 | "/ietf-key-chain:key-chains/key-chain/key/send-lifetime-active", 347 | "/ietf-key-chain:key-chains/key-chain/key/accept-lifetime-active" 348 | ] 349 | ); 350 | 351 | assert_eq!( 352 | ctx.find_path("/ietf-routing:routing/ribs/rib") 353 | .expect("Failed to lookup schema data") 354 | .children2(IterSchemaFlags::empty()) 355 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 356 | .collect::>(), 357 | vec![ 358 | "/ietf-routing:routing/ribs/rib/name", 359 | "/ietf-routing:routing/ribs/rib/address-family", 360 | "/ietf-routing:routing/ribs/rib/routes", 361 | "/ietf-routing:routing/ribs/rib/description", 362 | "/ietf-routing:routing/ribs/rib/active-route" 363 | ] 364 | ); 365 | } 366 | 367 | #[test] 368 | fn schema_iterator_top_level_nodes() { 369 | let ctx = create_context(); 370 | 371 | assert_eq!( 372 | ctx.get_module_latest("ietf-interfaces") 373 | .expect("Failed to lookup schema module") 374 | .top_level_nodes(IterSchemaFlags::empty()) 375 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 376 | .collect::>(), 377 | vec![ 378 | "/ietf-interfaces:interfaces", 379 | "/ietf-interfaces:interfaces-state" 380 | ] 381 | ); 382 | 383 | assert_eq!( 384 | ctx.get_module_latest("ietf-mpls-ldp") 385 | .expect("Failed to lookup schema module") 386 | .top_level_nodes(IterSchemaFlags::empty()) 387 | .map(|snode| snode.path(SchemaPathFormat::DATA)) 388 | .collect::>(), 389 | vec![ 390 | "/ietf-mpls-ldp:mpls-ldp-clear-peer", 391 | "/ietf-mpls-ldp:mpls-ldp-clear-hello-adjacency", 392 | "/ietf-mpls-ldp:mpls-ldp-clear-peer-statistics", 393 | "/ietf-mpls-ldp:mpls-ldp-peer-event", 394 | "/ietf-mpls-ldp:mpls-ldp-hello-adjacency-event", 395 | "/ietf-mpls-ldp:mpls-ldp-fec-event" 396 | ] 397 | ); 398 | } 399 | 400 | #[test] 401 | fn schema_node_attributes() { 402 | let ctx = create_context(); 403 | 404 | let snode = ctx 405 | .find_path("/ietf-interfaces:interfaces/interface/enabled") 406 | .expect("Failed to lookup schema node"); 407 | assert_eq!(snode.kind(), SchemaNodeKind::Leaf); 408 | assert_eq!(snode.name(), "enabled"); 409 | assert!(snode.description().is_some()); 410 | assert!(snode.reference().is_some()); 411 | assert_eq!(snode.is_config(), true); 412 | assert_eq!(snode.is_state(), false); 413 | assert_eq!(snode.is_mandatory(), false); 414 | assert_eq!(snode.default_value_canonical(), Some("true")); 415 | assert_eq!(snode.default_value(), Some(DataValue::Bool(true))); 416 | assert_eq!(snode.leaf_type().unwrap().base_type(), DataValueType::Bool); 417 | assert!(snode.units().is_none()); 418 | assert!(snode.musts().unwrap().count() == 0); 419 | assert!(snode.whens().count() == 0); 420 | assert_eq!(snode.is_status_current(), true); 421 | assert_eq!(snode.is_status_deprecated(), false); 422 | assert_eq!(snode.is_status_obsolete(), false); 423 | 424 | let snode = ctx 425 | .find_path("/ietf-interfaces:interfaces/interface") 426 | .expect("Failed to lookup schema node"); 427 | assert_eq!(snode.kind(), SchemaNodeKind::List); 428 | assert_eq!(snode.name(), "interface"); 429 | assert!(snode.description().is_some()); 430 | assert!(snode.reference().is_none()); 431 | assert_eq!(snode.is_config(), true); 432 | assert_eq!(snode.is_state(), false); 433 | assert_eq!(snode.is_mandatory(), false); 434 | assert_eq!(snode.is_keyless_list(), false); 435 | assert_eq!(snode.is_user_ordered(), false); 436 | assert_eq!(snode.min_elements(), None); 437 | assert_eq!(snode.max_elements(), None); 438 | assert!(snode.musts().unwrap().count() == 0); 439 | assert!(snode.whens().count() == 0); 440 | assert!(snode.actions().count() == 0); 441 | assert!(snode.notifications().count() == 0); 442 | assert_eq!(snode.is_status_current(), true); 443 | assert_eq!(snode.is_status_deprecated(), false); 444 | assert_eq!(snode.is_status_obsolete(), false); 445 | 446 | let snode = ctx 447 | .find_path("/ietf-interfaces:interfaces-state/interface") 448 | .expect("Failed to lookup schema node"); 449 | assert_eq!(snode.kind(), SchemaNodeKind::List); 450 | assert_eq!(snode.name(), "interface"); 451 | assert!(snode.description().is_some()); 452 | assert!(snode.reference().is_none()); 453 | assert_eq!(snode.is_config(), false); 454 | assert_eq!(snode.is_state(), true); 455 | assert_eq!(snode.is_mandatory(), false); 456 | assert_eq!(snode.is_keyless_list(), false); 457 | // TODO: this is wrong, report back to upstream. 458 | assert_eq!(snode.is_user_ordered(), true); 459 | assert_eq!(snode.min_elements(), None); 460 | assert_eq!(snode.max_elements(), None); 461 | assert!(snode.musts().unwrap().count() == 0); 462 | assert!(snode.whens().count() == 0); 463 | assert!(snode.actions().count() == 0); 464 | assert!(snode.notifications().count() == 0); 465 | assert_eq!(snode.is_status_current(), false); 466 | assert_eq!(snode.is_status_deprecated(), true); 467 | assert_eq!(snode.is_status_obsolete(), false); 468 | } 469 | 470 | #[test] 471 | fn ext_yang_data() { 472 | let mut ctx = create_context(); 473 | 474 | let module = ctx 475 | .load_module("ietf-restconf", None, &[]) 476 | .expect("Failed to load module"); 477 | 478 | assert_eq!( 479 | module 480 | .extensions() 481 | .filter_map(|ext| ext.argument()) 482 | .collect::>(), 483 | ["yang-errors", "yang-api"] 484 | ); 485 | 486 | // yang-errors 487 | let ext = module 488 | .extensions() 489 | .find(|ext| ext.argument().as_deref() == Some("yang-errors")) 490 | .expect("Failed to find the \"yang-api\" extension instance"); 491 | 492 | let dtree = ext.new_inner("errors").expect("Failed to create data"); 493 | 494 | assert_eq!( 495 | dtree 496 | .traverse() 497 | .map(|dnode| dnode.path()) 498 | .collect::>(), 499 | vec!["/ietf-restconf:errors"] 500 | ); 501 | 502 | // yang-api 503 | let ext = module 504 | .extensions() 505 | .find(|ext| ext.argument().as_deref() == Some("yang-api")) 506 | .expect("Failed to find the \"yang-api\" extension instance"); 507 | 508 | let dtree = ext 509 | .new_path("/ietf-restconf:restconf/data", None, false) 510 | .expect("Failed to create data") 511 | .unwrap(); 512 | 513 | assert_eq!( 514 | dtree 515 | .traverse() 516 | .map(|dnode| dnode.path()) 517 | .collect::>(), 518 | vec!["/ietf-restconf:restconf", "/ietf-restconf:restconf/data"] 519 | ); 520 | } 521 | --------------------------------------------------------------------------------