Library Usage

For better understanding, look at the Examples and Yangson library

Yangson

Yangson is a Python3 library for working with JSON-encoded [RFC7951] configuration and state data modelled using the YANG [RFC7895] data modelling language. Here Yangson is used to work with data model and validate loaded Json data against it.

Import necessary python modules.

>>> from json import load
>>> from yangson.datamodel import DataModel

Initialize the YANG data model. It can be done easily by reading YANG library data [RFC7895] from a file:

>>> model = DataModel.from_file('../yang-modules/yanglib.json', ["../yang-modules"])

Load data from Json-encoded file [RFC7951]

>>> with open('example-data.json') as infile:
...   ri = load(infile)
>>> json_data = model.from_raw(ri)

Validate data against the data model:

>>> json_data.validate()
>>> json_data = json_data.add_defaults()

Now the data are valid and you can work with it.


Resolvers-YANG

YANG modules

yang-modules/
├── cznic-deckard.yang
├── cznic-deckard.yinx
├── cznic-dns-parameters.yang
├── cznic-dns-parameters.yinx
├── cznic-dns-rdata.yang
├── cznic-dns-rdata.yinx
├── cznic-resolver-common.yang
├── cznic-resolver-common.yinx
├── cznic-resolver-knot.yang
├── cznic-resolver-knot.yinx
├── cznic-resolver-unbound.yang
├── cznic-resolver-unbound.yinx
├── iana-dns-class-rr-type.yang
├── ietf-inet-types@2013-07-15.yang
├── ietf-netconf-acm@2012-02-22.yang
├── ietf-yang-library@2016-06-21.yang
├── ietf-yang-types@2013-07-15.yang
├── Makefile
├── model.tree
├── yanglib-deckard.json
└── yanglib.json

Library

resolvers_yang/
├── __init__.py
├── kresd/
│   ├── __init__.py
│   └── kresd_conf_gen.py
├── unbound/
|   ├── __init__.py
|   ├── unbound_conf_gen.py
|   └── unbound_conf_loader.py
├── errors.py
├── parser.py
└── regex.py

Knot-Resolver modules

kresd_conf_gen.py

Import kresd.kresd_conf_gen module which contains generate function to generate Knot-Resolver configuration strings from valid python dictionary loaded from Json. Returns string.

>>> from resolvers_yang.kresd import kresd_conf_gen

Call kresd_conf_gen.generate() functions to create kresd_conf string.

>>> kresd_conf = kresd_conf_gen.generate(json_data)
>>> print(kresd_conf)    
net = {'127.0.0.1@53', '::1@53', '198.51.100.1@8853'}
net.outgoing_v6('2001:db8:0:2::1')
net.ipv6 = true
net.ipv4 = true
net.bufsize(4096)
user('jetconf','wheel')
modules.load('hints')
hints.set("localhost 127.0.0.1")
hints.set("localhost ::1")
hints.set("loopback 127.0.0.1")
hints.add_hosts('/etc/hosts')
hints.root({['a.root-servers.net'] = {'198.41.0.4', '2001:503:ba3e::2:30'}})
hints.root_file('/etc/resolver/root.hints')
mode('strict')
option('NO_MINIMIZE', false)
reorder_RR(true)
option('ALLOW_LOCAL', true)
verbose(true)
trust_anchors.add('. IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5')
trust_anchors.add('. IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D')
trust_anchors.add('. IN DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0=')
trust_anchors.negative = {'bad.example.com', 'worse.example.com'}
cache.open(104857600)
cache.max_ttl(172800)
cache.min_ttl(0)
modules.load('prefill')
prefill.config({['.'] = {url = 'https://www.internic.net/domain/root.zone', ca_file = '/etc/pki/tls/certs/ca-bundle.crt', interval = 86400}})
modules = {dns64 = '64:ff9b::'}

Unbound modules

unbound_conf_gen.py

Import unbound.unbound_conf_gen module which contains generate function to generate Unbound configuration strings from valid python dictionary loaded from Json. Returns string.

>>> from resolvers_yang.unbound import unbound_conf_gen

Call unbound_conf_gen.generate() functions to create unbound_conf string.

>>> unbound_conf = unbound_conf_gen.generate(json_data)
>>> print(unbound_conf)      
server:
    interface: 127.0.0.1@53
    interface: ::1@53
    interface: 198.51.100.1@8853
    outgoing-interface: 2001:db8:0:2::1
    do-ip6: yes
    do-ip4: yes
    edns-buffer-size: 4096
    username: "jetconf"
    verbosity: 2
    trust-anchor: ". IN DS 19036 8 2 49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5"
    trust-anchor: ". IN DS 20326 8 2 E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D"
    trust-anchor: ". IN DNSKEY 257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0="
    domain-insecure: "bad.example.com"
    domain-insecure: "worse.example.com"
    msg-cache-size: 104857600
    cache-max-ttl: 172800
    cache-min-ttl: 0
    val-override-date: "20181028131530"
    dns64-prefix: 64:ff9b::/96
    root-hints: "/etc/resolver/root.hints"
    harden-glue: yes
    qname-minimisation: yes
    rrset-roundrobin: yes
    do-not-query-localhost: no

stub-zone:
    name: "stub.example.com"
    stub-addr: "192.0.2.1@53"

stub-zone:
    name: "stub.example.net"
    stub-addr: "198.51.100.1@53"

unbound_conf and kresd_conf are raw strings , which can be save as text files.

>>> with open("unbound.conf", "w") as unb_file:
...   unb_file.write(unbound_conf)
1264
unbound_conf_loader.py

Import unbound.unbound_conf_loader class which contains load function for loading Unbound configuration string to python dictionary, which can be validate against resolvers-yang data model and save as Json file.

>>> from resolvers_yang.unbound import unbound_conf_loader

Input parameter of load function is string loaded from unbound.conf file. Returns python dictionary.

>>> with open("unbound.conf", "r") as unb_file:
...   unbconf_data = unb_file.read()

Call load function to load configuration string to dictionary.

>>> json_data = unbound_conf_loader.load(unbconf_data)
>>> json_data
{'cznic-resolver-common:dns-resolver': {'server': {'user-name': 'jetconf'}, 'network': {'listen-interfaces': [{'name': 'interface0', 'ip-address': '127.0.0.1', 'port': 53}, {'name': 'interface1', 'ip-address': '::1', 'port': 53}, {'name': 'interface2', 'ip-address': '198.51.100.1', 'port': 8853}], 'source-address': {'ipv6': '2001:db8:0:2::1'}, 'udp-payload-size': 4096, 'recursion-transport': {'l2-protocols': 'ipv4 ipv6'}}, 'resolver': {'stub-zones': [{'domain': 'stub.example.com', 'nameserver': '192.0.2.1', 'port': 53}, {'domain': 'stub.example.net', 'nameserver': '198.51.100.1', 'port': 53}], 'options': {'glue-checking': True, 'qname-minimisation': True, 'reorder-rrset': True, 'query-loopback': True}, 'hints': {'root-zone-file': '/etc/resolver/root.hints'}}, 'logging': {'verbosity': 2}, 'dnssec': {'trust-anchors': [{'domain': '.', 'auto-update': True, 'trust-anchor': [{'id': 0, 'ds': {'algorithm': 'RSASHA256', 'digest': '49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5', 'digest-type': 'SHA-256', 'key-tag': 19036}}, {'id': 1, 'ds': {'algorithm': 'RSASHA256', 'digest': 'E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D', 'digest-type': 'SHA-256', 'key-tag': 20326}}, {'id': 2, 'dnskey': {'algorithm': 'RSASHA256', 'flags': 'ZONE SEP', 'protocol': 3, 'public-key': 'AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0='}}]}], 'negative-trust-anchors': ['bad.example.com', 'worse.example.com']}, 'cache': {'max-size': 104857600, 'max-ttl': 172800, 'min-ttl': 0}, 'debugging': {'cznic-resolver-unbound:val-override-date': '2018-10-28T13:15:30Z'}, 'dns64': {'prefix': '64:ff9b::/96'}}}

json_data variable is python dictionary, which can be easily validate against data model by Yangson library and saved to Json-encoded file.

>>> model_data = model.from_raw(json_data)
>>> model_data.validate()
>>> from json import dump
>>> with open("unbound-data.json", 'w') as json_file:
...   dump(json_data, json_file, indent=2, sort_keys=False)