State data handlers, code, refactor and more

parent be371ddc
Pipeline #352 skipped
......@@ -4,13 +4,13 @@ import logging
import sys
from importlib import import_module
from . import op_handlers
from . import usr_op_handlers, usr_state_data_handlers
from .rest_server import RestServer
from .config import load_config, print_config
from .nacm import NacmConfig
from .data import JsonDatastore, BaseDataListener, SchemaNode, InstanceIdentifier
from .helpers import DataHelpers
from .handler_list import OP_HANDLERS
from .handler_list import OP_HANDLERS, STATE_DATA_HANDLES
class MyInfoDataListener(BaseDataListener):
......@@ -44,7 +44,10 @@ def main():
zone_listener1.add_schema_node("/ietf-netconf-acm:nacm/rule-list/rule")
# Register op handlers
OP_HANDLERS.register_handler("generate-key", op_handlers.sign_op_handler)
OP_HANDLERS.register_handler("generate-key", usr_op_handlers.sign_op_handler)
# Create and register state data handlers
usr_state_data_handlers.create_zone_state_handlers(STATE_DATA_HANDLES, datamodel)
# Create HTTP server
rest_srv = RestServer()
......
This diff is collapsed.
-----BEGIN CERTIFICATE-----
MIICZjCCAU4CAQ0wDQYJKoZIhvcNAQEFBQAwSzELMAkGA1UEBhMCQ1oxEzARBgNV
MIICZjCCAU4CAREwDQYJKoZIhvcNAQEFBQAwSzELMAkGA1UEBhMCQ1oxEzARBgNV
BAoMCkV4YW1wbGUgQ0ExEDAOBgNVBAsMB2V4Y2EuY3oxFTATBgNVBAMMDG1haWxA
ZXhjYS5jejAeFw0xNjAzMDgxNTIzMTNaFw0xNjA0MDcxNTIzMTNaMCsxCzAJBgNV
ZXhjYS5jejAeFw0xNjA1MDkwODQxNThaFw0xNzA1MDkwODQxNThaMCsxCzAJBgNV
BAYTAkNaMRwwGgYJKoZIhvcNAQkBFg1sb2p6YUBtYWlsLmN6MIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQDZsaIBTzwjoHMGGiiKuxoGwUJCBsAIMX5BUEBCIoX7
A3Yuit3TKWSVuTZrikPEEOXGCfJw3odQAKlK7QU7u732iP0+VIRMxhMh8BGS7p76
QgDOGe+gBYRvuJBPgYdHqWu7kCuL/FrKUeu/Ui4Z/7QP6AbWwT2aSswlJwZWRYhs
vwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBq7cnlwqSNza5kL4O2rLevwnF3BVfi
B4UlDJePne+c8ymGLtMHa1xevHpLVdCJOkF18zaBwxaVzQGlILu8yqtBZjbXnOV5
fNsajzo6FyifKnd6OB/ckt2wCi4+QYdttJc7YNn9Dx4PHoRLwCzshIjOY5p5xIzI
9pCVUgc1bNO5QwgmJpoeumjN4ml2v+JDiYu0cNXTJP9tyX22YAxXkYRDkfnGQQb/
wo0Jt4KpPtRg8NSHYswumWlWDJuHKqauck6RjKDSGV+ifv6COoUbT83eiC+46PTy
iJBhiz0zScDzMyOjDhv/cyxnfO/3+Z7BOrmWz9faGRlLnc4OdJ+7TupX
vwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBxREnObDzbunZiChhUl0O4m5zRyDpU
51r4PmMNM11MbPB0/a8jNwyXQUrtG4ZRxPDEWxwPCg0mSG+kEq8lkYTOCg8jr0np
YXbFZj4IYiZEyXs25xrqmlMnt2GjP1MBCBPLI7YzItQF4SHd+8U8rKA4bAVrlmwv
ccqWffc+R7LzyAY9N0jTPmRp2u9DwoL9FrgOcfCnyZmbezeGFORXGIHbfIDNvBbU
I9KiQDSWz+b/Zp1n2h4OPR6cjU2M7GhZnPEO6w669EtSDah0VL2U9wjmq4HZUPzY
PUmXUunEsZ5aSaSevRHc2bnuUs5UQUKODqml3NxU3Nx5YtXJtHh+YsUz
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
MIICXAIBAAKBgQDZsaIBTzwjoHMGGiiKuxoGwUJCBsAIMX5BUEBCIoX7A3Yuit3T
......
{
"ietf-netconf-acm:nacm": {
"enable-nacm": true,
"read-default": "deny",
"read-default": "permit",
"write-default": "deny",
"exec-default": "deny",
"denied-operations": 123,
......@@ -85,7 +85,7 @@
},
{
"name": "permit-zone-access",
"path": "/dns-server:dns-server/zones/zone",
"path": "/dns-server:dns-server/zones",
"access-operations": "*",
"comment": "Users can write other zones.",
"action": "permit"
......
from typing import List, Tuple, Callable
from typing import List, Tuple, Callable, Any
from .data import BaseDatastore, PathFormat
from yangson.context import Context
HandlerSelectorT = Any
class OpHandlerList:
class BaseHandlerList:
def __init__(self):
self.handlers = [] # type: List[Tuple[str, Callable]]
self.handlers = [] # type: List[Tuple[HandlerSelectorT, Callable]]
self.default_handler = None # type: Callable
def register_handler(self, name: str, handler: Callable):
self.handlers.append((name, handler))
raise NotImplementedError("Not implemented in base class")
def register_default_handler(self, handler: Callable):
self.default_handler = handler
def get_handler(self, name: str) -> Callable:
raise NotImplementedError("Not implemented in base class")
class OpHandlerList(BaseHandlerList):
def register_handler(self, name: str, handler: Callable):
self.handlers.append((name, handler))
def get_handler(self, name: str) -> Callable:
for h in self.handlers:
if h[0] == name:
......@@ -20,4 +32,53 @@ class OpHandlerList:
return self.default_handler
class StateDataHandlerList:
def __init__(self):
self.handlers = []
self.ds = None # type: BaseDatastore
# def register_handler(self, ii_str: str, handler: Callable):
# ii = self.ds.parse_ii(ii_str, PathFormat.XPATH)
# self.handlers.append((ii, handler))
#
# def get_handler(self, ii: InstanceIdentifier, allow_superior: bool=True) -> Callable:
# if allow_superior:
# ii_local = ii.copy()
# while ii_local:
# dn = self.ds.get_node(ii_local)
# for h in self.handlers:
# if self.ds.get_node(h[0]).value is dn.value:
# return h[1]
# ii_local.pop()
# else:
# for h in self.handlers:
# if h[0] == ii:
# return h[1]
#
# return self.default_handler
def register_handler(self, handler):
saddr = Context.path2route(handler.sch_pth)
self.handlers.append((saddr, handler))
def get_handler(self, sch_pth: str, allow_superior: bool = True) -> Callable:
saddr = Context.path2route(sch_pth)
if allow_superior:
while saddr:
for h in self.handlers:
if h[0] == saddr:
return h[1]
saddr.pop()
else:
for h in self.handlers:
if h[0] == saddr:
return h[1]
return None
# def set_ds(self, ds: BaseDatastore):
# self.ds = ds
OP_HANDLERS = OpHandlerList()
STATE_DATA_HANDLES = StateDataHandlerList()
......@@ -37,7 +37,7 @@ class DataHelpers:
def load_data_model(module_dir: str, yang_library_file: str) -> DataModel:
with open(yang_library_file) as ylfile:
yl = ylfile.read()
dm = DataModel.from_yang_library(yl, module_dir)
dm = DataModel(yl, [module_dir])
return dm
......
......@@ -11,7 +11,7 @@ from yangson.instance import NonexistentInstance, InstanceTypeError, DuplicateMe
from .config import CONFIG_GLOBAL, CONFIG_HTTP, NACM_ADMINS, API_ROOT_data, API_ROOT_ops
from .helpers import CertHelpers, DataHelpers, DateTimeHelpers, ErrorHelpers
from .data import BaseDatastore, Rpc, DataLockError, NacmForbiddenError, NoHandlerForOpError
from .data import BaseDatastore, Rpc, DataLockError, NacmForbiddenError, NoHandlerForOpError, InstanceAlreadyPresent
QueryStrT = Dict[str, List[str]]
epretty = ErrorHelpers.epretty
......@@ -196,7 +196,8 @@ def _post(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pt
try:
ds.lock_data(username)
ins_pos = (query_string.get("insert") or [None])[0]
ds.create_node_rpc(rpc1, json_data, insert=ins_pos)
point = (query_string.get("point") or [None])[0]
ds.create_node_rpc(rpc1, json_data, insert=ins_pos, point=point)
prot.send_empty(stream_id, "201", "Created")
except DataLockError as e:
warn(epretty(e))
......@@ -216,6 +217,9 @@ def _post(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pt
except InstanceTypeError as e:
warn(epretty(e))
prot.send_empty(stream_id, "400", "Bad Request")
except InstanceAlreadyPresent as e:
warn(epretty(e))
prot.send_empty(stream_id, "400", "Bad Request")
finally:
ds.unlock_data()
......@@ -247,10 +251,6 @@ def _put(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pth
url_split = pth.split("?")
url_path = url_split[0]
if len(url_split) > 1:
query_string = parse_qs(url_split[1])
else:
query_string = {}
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
......@@ -262,7 +262,7 @@ def _put(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pth
try:
ds.lock_data(username)
ds.put_node_rpc(rpc1, json_data)
ds.update_node_rpc(rpc1, json_data)
prot.send_empty(stream_id, "204", "No Content", False)
except DataLockError as e:
warn(epretty(e))
......@@ -304,10 +304,6 @@ def create_put_api(ds: BaseDatastore):
def _delete(prot: "H2Protocol", stream_id: int, ds: BaseDatastore, pth: str):
url_split = pth.split("?")
url_path = url_split[0]
if len(url_split) > 1:
query_string = parse_qs(url_split[1])
else:
query_string = {}
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
......
......@@ -81,7 +81,7 @@ class H2Protocol(asyncio.Protocol):
elif isinstance(event, DataReceived):
self.http_handle_upload(event.data, event.stream_id)
elif isinstance(event, RemoteSettingsChanged):
self.conn.acknowledge_settings(event)
self.conn.update_settings(event.changed_settings)
def http_handle_upload(self, data: bytes, stream_id: int):
try:
......
from datetime import datetime
from typing import Dict, Any
from yangson.datamodel import DataModel
from yangson.instance import InstanceIdentifier, Instance
from .libknot.control import KnotCtl
JsonNodeT = Dict[str, Any]
class StateNodeHandlerBase:
def __init__(self, data_model: DataModel, ctl: KnotCtl):
self.data_model = data_model
self.knotctl = ctl
self.member_handlers = {} # type: Dict[str, StateNodeHandlerBase]
def add_member_handler(self, member: str, handler: "StateNodeHandlerBase"):
self.member_handlers[member] = handler
def update_node(self, node_ii: InstanceIdentifier, data_root: Instance) -> Instance:
pass
class ZoneSigningStateHandler(StateNodeHandlerBase):
def __init__(self, data_model: DataModel, ctl: KnotCtl):
super().__init__(data_model, ctl)
self.sch_pth = "/dns-server:dns-server-state/zone/dnssec-signing:dnssec-signing"
self.schema_node = data_model.get_data_node(self.sch_pth)
def update_node(self, node_ii: InstanceIdentifier, data_root: Instance) -> Instance:
print("zone_state_signing_handler, ii = {}".format(node_ii))
zone_name = node_ii[2].keys.get("domain")
zone_signing = {
"key": [
{
"key-id": "d3a9fd3b36a6be275adea2b67c6e82b27ca30e90",
"key-tag": 30348,
"algorithm": "RSASHA256",
"length": 2048,
"flags": "zone-key secure-entry-point",
"created": "2015-06-18T18:02:45+02:00",
"publish": "2015-06-18T19:00:00+02:00",
# "activate": str(datetime.now()),
"retire": "2015-07-18T18:02:45+02:00",
"remove": "2015-07-25T00:00:00+02:00"
}
]
}
old_node = data_root.goto(node_ii[0:4])
new_node = self.schema_node.from_raw(zone_signing)
new_inst = old_node.update(new_node)
return new_inst
class ZoneStateHandler(StateNodeHandlerBase):
def __init__(self, data_model: DataModel, ctl: KnotCtl):
super().__init__(data_model, ctl)
self.sch_pth = "/dns-server:dns-server-state/zone"
self.schema_node = data_model.get_data_node(self.sch_pth)
def update_node(self, node_ii: InstanceIdentifier, data_root: Instance) -> Instance:
print("zone_state_handler, ii = {}".format(node_ii))
# Request status of specific zone
if len(node_ii) > 2:
zone_name = node_ii[2].keys.get("domain")
self.knotctl.send_block("zone-status", zone=zone_name)
resp = self.knotctl.receive_block()
resp = resp.get(zone_name + ".")
zone_obj = {
"domain": zone_name,
"class": "IN",
"serial": int(resp.get("serial")[0]),
"server-role": resp.get("type")[0]
}
old_node = data_root.goto(node_ii[0:3])
new_node = self.schema_node.from_raw([zone_obj])[0]
new_inst = old_node.update(new_node)
for m, h in self.member_handlers.items():
new_inst = new_inst.new_member(m, h.update_node(node_ii, data_root).value).up()
# Request status of all zones
else:
self.knotctl.send_block("zone-status")
resp = self.knotctl.receive_block()
zones_list = []
for zone_name, zone_status in resp.items():
zone_obj = {
"domain": zone_name[0:-1],
"class": "IN",
"serial": int(zone_status.get("serial")[0]),
"server-role": zone_status.get("type")[0]
}
zones_list.append(zone_obj)
old_node = data_root.goto(node_ii[0:2])
new_node = self.schema_node.from_raw(zones_list)
new_inst = old_node.update(new_node)
for m, h in self.member_handlers.items():
new_inst = new_inst.new_member(m, h.update_node(node_ii, data_root).value).up()
return new_inst
# Create handler hierarchy
def create_zone_state_handlers(handler_list: "StateDataHandlerList", dm: DataModel):
ctl = KnotCtl()
ctl.connect("/tmp/knottest-1462525244-b67pmzm_/ddns/ttl/knot1/knot.sock")
zssh = ZoneSigningStateHandler(dm, ctl)
handler_list.register_handler(zssh)
zsh = ZoneStateHandler(dm, ctl)
# zsh.add_member_handler("dnssec-signing:dnssec-signing", zssh)
handler_list.register_handler(zsh)
\ No newline at end of file
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