Improved handling of state data

parent 092eb371
......@@ -399,10 +399,10 @@ class BaseDatastore:
sdh = STATE_DATA_HANDLES.get_handler(state_root_sch_pth)
if sdh is not None:
if isinstance(sdh, ContainerNodeHandlerBase):
state_handler_val = sdh.generate_node(ii, root)
state_handler_val = sdh.generate_node(ii, staging)
state_root_n = sdh.schema_node.orphan_instance(state_handler_val)
elif isinstance(sdh, ListNodeHandlerBase):
state_handler_val = sdh.generate_item(ii, root)
state_handler_val = sdh.generate_item(ii, staging)
state_root_n = sdh.schema_node.orphan_entry(state_handler_val)
# Select desired subnode from handler-generated content
......@@ -412,9 +412,35 @@ class BaseDatastore:
raise NoHandlerForStateDataError(rpc.path)
else:
# Request for config data containing state data
# TODO
n = root
pass
n = root.goto(ii)
def _fill_state_roots(node: InstanceNode) -> InstanceNode:
if isinstance(node.value, ObjectValue):
if node.schema_node is state_root_sn.parent:
ii_gen = DataHelpers.node_get_ii(node)
sdh = STATE_DATA_HANDLES.get_handler(state_root_sch_pth)
if sdh is not None:
if isinstance(sdh, ContainerNodeHandlerBase):
state_handler_val = sdh.generate_node(ii_gen, staging)
elif isinstance(sdh, ListNodeHandlerBase):
state_handler_val = sdh.generate_item(ii_gen, staging)
nm_name = state_root_sn.qual_name[0]
node = node.put_member(nm_name, state_handler_val, raw=True).up()
else:
for key in node:
member = node[key]
node = _fill_state_roots(member).up()
elif isinstance(node.value, ArrayValue):
i = 0
arr_len = len(node.value)
while i < arr_len:
node = _fill_state_roots(node[i]).up()
i += 1
return node
n = _fill_state_roots(n)
else:
n = root.goto(ii)
......
......@@ -5,8 +5,9 @@ from enum import Enum
from typing import List, Dict, Union, Any, Iterable
from datetime import datetime
from pytz import timezone
from yangson.instance import InstanceRoute, MemberName, EntryKeys
from yangson.instance import InstanceRoute, MemberName, EntryKeys, InstanceNode, ArrayValue, NonexistentInstance
from yangson.datamodel import DataModel
from yangson.schemanode import ListNode, ContainerNode
from .config import CONFIG_GLOBAL, CONFIG_HTTP
......@@ -56,21 +57,42 @@ class DataHelpers:
dm = DataModel(yl, [module_dir])
return dm
# Parse Instance Identifier from string
# @staticmethod
# def parse_ii(path: str, path_format: PathFormat) -> InstanceRoute:
# if path_format == PathFormat.URL:
# ii = ResourceIdParser(path).parse()
# else:
# ii = InstanceIdParser(path).parse()
#
# return ii
# Convert InstanceRoute or List[InstanceSelector] to string
@staticmethod
def ii2str(ii: Iterable) -> str:
return "".join([str(seg) for seg in ii])
@staticmethod
def node_get_ii(node: InstanceNode) -> InstanceRoute:
m = node
ii_gen = InstanceRoute()
try:
while m:
m_sn = m.schema_node
m_sn_dp = m_sn.data_parent()
if isinstance(m_sn, ListNode):
if isinstance(m.value, ArrayValue):
mn = MemberName(m_sn.qual_name[0], None if m_sn_dp and m_sn.ns == m_sn_dp.ns else m_sn.qual_name[1])
ii_gen.append(mn)
else:
kv = {}
for qk in m_sn.keys:
k = qk[0]
k_ns = None if m_sn_dp and m_sn.ns == m_sn_dp.ns else qk[1]
kv[(k, k_ns)] = m.value.get(k)
ek = EntryKeys(kv)
ii_gen.append(ek)
elif isinstance(m_sn, ContainerNode):
mn = MemberName(m_sn.qual_name[0], None if m_sn_dp and m_sn.ns == m_sn_dp.ns else m_sn.qual_name[1])
ii_gen.append(mn)
m = m.up()
except NonexistentInstance:
pass
ii_gen.reverse()
return ii_gen
class DateTimeHelpers:
@staticmethod
......
......@@ -12,6 +12,7 @@ from yangson.schemanode import NonexistentSchemaNode, ContainerNode, ListNode, G
from yangson.instance import NonexistentInstance, InstanceValueError, RootNode
from yangson.datatype import YangTypeError
from jetconf.knot_api import KnotError
from .config import CONFIG_GLOBAL, CONFIG_HTTP, NACM_ADMINS, API_ROOT_data, API_ROOT_STAGING_data, API_ROOT_ops
from .helpers import CertHelpers, DataHelpers, DateTimeHelpers, ErrorHelpers, LogHelpers, SSLCertT
from .data import (
......@@ -123,7 +124,7 @@ def _get(ds: BaseDatastore, pth: str, username: str, yl_data: bool=False, stagin
except InstanceValueError as e:
warn(epretty(e))
http_resp = HttpResponse.empty(HttpStatus.BadRequest)
except (ConfHandlerFailedError, NoHandlerError) as e:
except (ConfHandlerFailedError, NoHandlerError, KnotError) as e:
error(epretty(e))
http_resp = HttpResponse.empty(HttpStatus.InternalServerError)
finally:
......
import json
from enum import Enum
from typing import List, Union, Dict, Any, Optional
from threading import Lock
......@@ -328,111 +329,10 @@ class KnotConfig(KnotCtl):
except Exception as e:
raise KnotInternalError(str(e))
resp = resp[domain_name]
if domain_name[-1] != ".":
domain_name += "."
zone_template = {
"dns-zones:zone-data": {
"zone": [
{
"name": domain_name,
"class": "IN",
"default-ttl": 3600,
"SOA": {},
"rrset": []
}
]
}
}
zone_out = zone_template["dns-zones:zone-data"]["zone"][0]
soa_out = zone_out["SOA"]
soa = resp[domain_name]["SOA"]
soa_data = soa["data"][0].split()
try:
soa_out["ttl"] = int(soa["ttl"])
soa_out["mname"] = soa_data[0]
soa_out["rname"] = soa_data[1]
soa_out["serial"] = int(soa_data[2])
soa_out["refresh"] = int(soa_data[3])
soa_out["retry"] = int(soa_data[4])
soa_out["expire"] = int(soa_data[5])
soa_out["minimum"] = int(soa_data[6])
except (IndexError, ValueError) as e:
print(str(e))
rrset_out = zone_out["rrset"]
for owner, rrs in resp.items():
# print("rrs={}".format(rrs))
for rr_type, rr in rrs.items():
# print("rr={}".format(rr))
if rr_type not in ("A", "AAAA", "NS", "MX", "TXT", "TLSA", "CNAME"):
continue
ttl = int(rr["ttl"])
rr_data_list = rr["data"]
new_rr_out_rdata_list = []
new_rr_out = {
"owner": owner,
"type": "iana-dns-parameters:" + rr_type,
"ttl": ttl,
"rdata": new_rr_out_rdata_list
}
id_int = 0
for rr_data in rr_data_list:
new_rr_out_rdata_values = {}
new_rr_out_rdata = {
"id": str(id_int),
rr_type: new_rr_out_rdata_values
}
if rr_type in ("A", "AAAA"):
new_rr_out_rdata_values["address"] = rr_data
elif rr_type == "NS":
new_rr_out_rdata_values["nsdname"] = rr_data
elif rr_type == "MX":
rr_data = rr_data.split()
new_rr_out_rdata_values["preference"] = rr_data[0]
new_rr_out_rdata_values["exchange"] = rr_data[1]
elif rr_type == "TXT":
new_rr_out_rdata_values["txt-data"] = rr_data.strip(" \"")
elif rr_type == "TLSA":
cert_usage_enum = {
"0": "PKIX-TA",
"1": "PKIX-EE",
"2": "DANE-TA",
"3": "DANE-EE",
"255": "PrivCert"
}
sel_enum = {
"0": "Cert",
"1": "SPKI",
"255": "PrivSel"
}
match_type_enum = {
"0": "Full",
"1": "SHA2-256",
"2": "SHA2-512",
"255": "PrivMatch"
}
rr_data = rr_data.split()
new_rr_out_rdata_values["certificate-usage"] = cert_usage_enum[rr_data[0]]
new_rr_out_rdata_values["selector"] = sel_enum[rr_data[1]]
new_rr_out_rdata_values["matching-type"] = match_type_enum[rr_data[2]]
new_rr_out_rdata_values["certificate-association-data"] = rr_data[3]
elif rr_type == "CNAME":
new_rr_out_rdata_values["cname"] = rr_data
new_rr_out_rdata_list.append(new_rr_out_rdata)
id_int += 1
rrset_out.append(new_rr_out)
return zone_template
return resp[domain_name]
# Connects to Knot control socket and begins a new transaction (config or zone)
......
import json
from colorlog import error
from yangson.datamodel import DataModel
......@@ -25,15 +27,15 @@ class StateNodeHandlerBase:
class ContainerNodeHandlerBase(StateNodeHandlerBase):
def generate_node(self, node_ii: InstanceRoute, data_root: InstanceNode) -> InstanceNode:
def generate_node(self, node_ii: InstanceRoute, staging: bool) -> JsonNodeT:
pass
class ListNodeHandlerBase(StateNodeHandlerBase):
def generate_list(self, node_ii: InstanceRoute, data_root: InstanceNode) -> InstanceNode:
def generate_list(self, node_ii: InstanceRoute, staging: bool) -> JsonNodeT:
pass
def generate_item(self, node_ii: InstanceRoute, data_root: InstanceNode) -> InstanceNode:
def generate_item(self, node_ii: InstanceRoute, staging: bool) -> JsonNodeT:
pass
......@@ -41,7 +43,7 @@ class ZoneSigningStateHandler(ContainerNodeHandlerBase):
def __init__(self, data_model: DataModel):
super().__init__(data_model, "/dns-server:dns-server-state/zone/dnssec-signing:dnssec-signing")
def generate_node(self, node_ii: InstanceRoute, data_root: InstanceNode) -> InstanceNode:
def generate_node(self, node_ii: InstanceRoute, staging: bool) -> JsonNodeT:
print("zone_state_signing_handler, ii = {}".format(node_ii))
domain_name = node_ii[2].keys.get(("domain", None)) + "."
......@@ -71,7 +73,7 @@ class ZoneStateHandler(ListNodeHandlerBase):
def __init__(self, data_model: DataModel):
super().__init__(data_model, "/dns-server:dns-server-state/zone")
def generate_list(self, node_ii: InstanceRoute, data_root: InstanceNode) -> InstanceNode:
def generate_list(self, node_ii: InstanceRoute, staging: bool) -> JsonNodeT:
zones_list = []
KNOT.knot_connect()
......@@ -94,7 +96,7 @@ class ZoneStateHandler(ListNodeHandlerBase):
return zones_list
def generate_item(self, node_ii: InstanceRoute, data_root: InstanceNode) -> InstanceNode:
def generate_item(self, node_ii: InstanceRoute, staging: bool) -> JsonNodeT:
zone_obj = {}
# Request status of specific zone
......@@ -117,10 +119,130 @@ class ZoneStateHandler(ListNodeHandlerBase):
return zone_obj
class ZoneDataStateHandler(ListNodeHandlerBase):
def __init__(self, data_model: DataModel):
super().__init__(data_model, "/dns-zones-state:zone")
def generate_item(self, node_ii: InstanceRoute, staging: bool) -> JsonNodeT:
# Request contents of specific zone
KNOT.knot_connect()
domain_name = node_ii[1].keys.get(("name", None))
# if domain_name[-1] != ".":
# domain_name_dot = domain_name + "."
# else:
# domain_name_dot = domain_name
resp = KNOT.zone_read(domain_name)
KNOT.knot_disconnect()
zone_out = {
"name": domain_name,
"class": "IN",
"rrset": []
}
rrset_out = zone_out["rrset"]
for owner, rrs in resp.items():
# print("rrs={}".format(rrs))
for rr_type, rr in rrs.items():
# print("rr={}".format(rr))
if rr_type not in ("SOA", "A", "AAAA", "NS", "MX", "TXT", "TLSA", "CNAME"):
continue
ttl = int(rr["ttl"])
rr_data_list = rr["data"]
new_rr_out_rdata_list = []
new_rr_out = {
"owner": owner.rstrip("."),
"type": "iana-dns-parameters:" + rr_type,
"ttl": ttl,
"rdata": new_rr_out_rdata_list
}
for rr_data in rr_data_list:
new_rr_out_rdata_values = {}
new_rr_out_rdata = {
rr_type: new_rr_out_rdata_values
}
if rr_type == "SOA":
rr_data = rr_data.split()
try:
new_rr_out_rdata_values["mname"] = rr_data[0].rstrip(".")
new_rr_out_rdata_values["rname"] = rr_data[1].rstrip(".")
new_rr_out_rdata_values["serial"] = int(rr_data[2])
new_rr_out_rdata_values["refresh"] = int(rr_data[3])
new_rr_out_rdata_values["retry"] = int(rr_data[4])
new_rr_out_rdata_values["expire"] = int(rr_data[5])
new_rr_out_rdata_values["minimum"] = int(rr_data[6])
except (IndexError, ValueError) as e:
print(str(e))
elif rr_type in ("A", "AAAA"):
new_rr_out_rdata_values["address"] = rr_data
elif rr_type == "NS":
new_rr_out_rdata_values["nsdname"] = rr_data.rstrip(".")
elif rr_type == "MX":
rr_data = rr_data.split()
new_rr_out_rdata_values["preference"] = rr_data[0]
new_rr_out_rdata_values["exchange"] = rr_data[1].rstrip(".")
elif rr_type == "TXT":
new_rr_out_rdata_values["txt-data"] = rr_data.strip(" \"")
elif rr_type == "TLSA":
cert_usage_enum = {
"0": "PKIX-TA",
"1": "PKIX-EE",
"2": "DANE-TA",
"3": "DANE-EE",
"255": "PrivCert"
}
sel_enum = {
"0": "Cert",
"1": "SPKI",
"255": "PrivSel"
}
match_type_enum = {
"0": "Full",
"1": "SHA2-256",
"2": "SHA2-512",
"255": "PrivMatch"
}
rr_data = rr_data.split()
new_rr_out_rdata_values["certificate-usage"] = cert_usage_enum[rr_data[0]]
new_rr_out_rdata_values["selector"] = sel_enum[rr_data[1]]
new_rr_out_rdata_values["matching-type"] = match_type_enum[rr_data[2]]
new_rr_out_rdata_values["certificate-association-data"] = rr_data[3]
elif rr_type == "CNAME":
new_rr_out_rdata_values["cname"] = rr_data
new_rr_out_rdata_list.append(new_rr_out_rdata)
rrset_out.append(new_rr_out)
return zone_out
class PokusStateHandler(ContainerNodeHandlerBase):
def __init__(self, data_model: DataModel):
super().__init__(data_model, "/dns-server:dns-server/access-control-list/network/pokus")
def generate_node(self, node_ii: InstanceRoute, staging: bool) -> JsonNodeT:
print("pokus_handler, ii = {}".format(node_ii))
try:
acl_name = node_ii[2].keys.get(("name", None))
except IndexError:
acl_name = None
return {"pok": "Name: {}".format(acl_name)}
# Instantiate state data handlers
def create_zone_state_handlers(handler_list: "StateDataHandlerList", dm: DataModel):
# zssh = ZoneSigningStateHandler(dm)
# handler_list.register_handler(zssh)
zsh = ZoneStateHandler(dm)
handler_list.register_handler(zsh)
\ No newline at end of file
zdsh = ZoneDataStateHandler(dm)
psh = PokusStateHandler(dm)
handler_list.register_handler(zsh)
handler_list.register_handler(zdsh)
handler_list.register_handler(psh)
......@@ -525,6 +525,14 @@ module dns-server {
description
"Identification of a network.";
uses entry-name;
container pokus {
config "false";
leaf pok {
type string;
description
"pokus";
}
}
leaf ip-prefix {
type inet:ip-prefix;
mandatory "true";
......
......@@ -90,6 +90,13 @@
"conformance-type": "implement",
"schema": "https://gitlab.labs.nic.cz/labs/zone-data-yang/raw/master/dns-zone-rpcs.yang"
},
{
"name": "dns-zones-state",
"revision": "2017-01-12",
"namespace": "http://www.nic.cz/ns/yang/dns-zones-state",
"conformance-type": "implement",
"schema": "https://gitlab.labs.nic.cz/labs/zone-data-yang/blob/master/dns-zones-state.yang"
},
{
"name": "iana-dns-parameters",
"revision": "2016-08-03",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment