Changed PUT and POST data input format + many improvements

parent ad2ae089
import json
import logging
from threading import Lock
import colorlog
import sys
import copy
from threading import Lock
from enum import Enum
from colorlog import error, warning as warn, info, debug
from typing import List, Any, Dict, TypeVar, Tuple, Set
import copy
import yangson.instance
from yangson.instance import Instance, NonexistentInstance, InstanceError, ArrayValue, ObjectValue, MemberName, EntryKeys, EntryIndex
from yangson import DataModel
from yangson.datamodel import InstanceIdentifier
from yangson.datamodel import InstanceIdentifier, DataModel
from yangson.instance import \
Instance, \
NonexistentInstance, \
InstanceError, \
ArrayValue, \
ObjectValue, \
MemberName, \
EntryKeys, \
EntryIndex
from .helpers import DataHelpers
......@@ -21,8 +28,9 @@ class PathFormat(Enum):
class NacmForbiddenError(Exception):
def __init__(self, msg="Access to data node rejected by NACM"):
def __init__(self, msg="Access to data node rejected by NACM", rule=None):
self.msg = msg
self.rulename = rule
class DataLockError(Exception):
......@@ -38,6 +46,7 @@ class Rpc:
def __init__(self):
self.username = None # type: str
self.path = None # type: str
self.qs = None # type: Dict[str, List[str]]
self.path_format = PathFormat.URL # type: PathFormat
......@@ -101,75 +110,68 @@ class BaseDatastore:
ii = self.parse_ii(rpc.path, rpc.path_format)
n = self._data.goto(ii)
# if self.nacm:
# nrpc = NacmRpc(self.nacm, self, rpc.username)
# if nrpc.check_data_node_path(ii, Permission.NACM_ACCESS_READ) == Action.DENY:
# raise NacmForbiddenError()
# else:
# # Prun subtree data
# n = nrpc.check_data_read_path(ii)
value_keys = value.keys()
if len(value_keys) > 1:
raise ValueError("Received data containing more than one instance")
val_key = tuple(value_keys)[0]
val_data = value[val_key]
existing_member = None
try:
existing_member = n.member(val_key)
except NonexistentInstance:
pass
if existing_member is None:
# Create new data node
data_doc = DataHelpers.node2doc(ii + [MemberName(val_key)], val_data)
data_doc_inst = self._dm.from_raw(data_doc)
new_value = data_doc_inst.goto(ii).value
if self.nacm:
nrpc = NacmRpc(self.nacm, self, rpc.username)
if nrpc.check_data_node_path(ii, Permission.NACM_ACCESS_CREATE) == Action.DENY:
raise NacmForbiddenError()
if isinstance(n.value, ObjectValue):
# Only one member can be appended at time
value_keys = value.keys()
if len(value_keys) > 1:
raise ValueError("Received data contains more than one object")
new_value_data = new_value[val_key]
recv_object_key = tuple(value_keys)[0]
recv_object_value = value[recv_object_key]
new_n = n.new_member(val_key, new_value_data)
# Check if member is not already present in data
existing_member = None
try:
existing_member = n.member(recv_object_key)
except NonexistentInstance:
pass
if existing_member is not None:
raise InstanceAlreadyPresent("InstanceAlreadyPresent")
# Create new member
new_member_ii = ii + [MemberName(recv_object_key)]
data_doc = DataHelpers.node2doc(new_member_ii, recv_object_value)
data_doc_inst = self._dm.from_raw(data_doc)
new_value = data_doc_inst.goto(new_member_ii).value
new_n = n.new_member(recv_object_key, new_value)
self._data = new_n.top()
elif isinstance(existing_member.value, ArrayValue):
elif isinstance(n.value, ArrayValue):
# Append received node to list
data_doc = DataHelpers.node2doc(ii + [MemberName(val_key)], [val_data])
data_doc = DataHelpers.node2doc(ii, [value])
print(data_doc)
data_doc_inst = self._dm.from_raw(data_doc)
new_value = data_doc_inst.goto(ii).value
new_value_data = new_value[val_key][0]
if insert == "first":
new_n = existing_member.update(ArrayValue(val=[new_value_data] + existing_member.value))
new_n = n.update(ArrayValue(val=new_value + n.value))
else:
new_n = existing_member.update(ArrayValue(val=existing_member.value + [new_value_data]))
new_n = n.update(ArrayValue(val=n.value + new_value))
self._data = new_n.top()
else:
raise InstanceAlreadyPresent("InstanceAlreadyPresent")
raise ValueError("Child node can only be appended to Object or Array")
if not isinstance(n.value, ObjectValue):
error("create_node: target resource not an object")
def put_node_rpc(self, rpc: Rpc, value: Any):
ii = self.parse_ii(rpc.path, rpc.path_format)
n = self._data.goto(ii)
# if self.nacm:
# nrpc = NacmRpc(self.nacm, self, rpc.username)
# if nrpc.check_data_node_path(ii, Permission.NACM_ACCESS_READ) == Action.DENY:
# raise NacmForbiddenError()
# else:
# # Prun subtree data
# n = nrpc.check_data_read_path(ii)
value_keys = value.keys()
if len(value_keys) > 1:
raise ValueError("Received data containing more than one instance")
if self.nacm:
nrpc = NacmRpc(self.nacm, self, rpc.username)
if nrpc.check_data_node_path(ii, Permission.NACM_ACCESS_UPDATE) == Action.DENY:
raise NacmForbiddenError()
inst_val = tuple(value_keys)[0]
data_doc = DataHelpers.node2doc(ii, value)
data_doc_inst = self._dm.from_raw(data_doc)
new_value = data_doc_inst.goto(ii).value
new_n = n.update(inst_val)
new_n = n.update(new_value)
self._data = new_n.top()
def delete_node_rpc(self, rpc: Rpc, insert=None, point=None):
......@@ -178,13 +180,10 @@ class BaseDatastore:
n_parent = n.up()
last_isel = ii[-1]
# if self.nacm:
# nrpc = NacmRpc(self.nacm, self, rpc.username)
# if nrpc.check_data_node_path(ii, Permission.NACM_ACCESS_READ) == Action.DENY:
# raise NacmForbiddenError()
# else:
# # Prun subtree data
# n = nrpc.check_data_read_path(ii)
if self.nacm:
nrpc = NacmRpc(self.nacm, self, rpc.username)
if nrpc.check_data_node_path(ii, Permission.NACM_ACCESS_DELETE) == Action.DENY:
raise NacmForbiddenError()
if isinstance(last_isel, EntryIndex):
new_n = n_parent.remove_entry(last_isel.index)
......@@ -251,7 +250,13 @@ def test():
n = data.get_node_rpc(rpc)
info("Result =")
print(n.value)
if json.loads(json.dumps(n.value)) == [{'name': 'test1', 'type': 'knot-dns:synth-record'}, {'name': 'test2', 'type': 'knot-dns:synth-record'}]:
expected_value = \
[
{'name': 'test1', 'type': 'knot-dns:synth-record'},
{'name': 'test2', 'type': 'knot-dns:synth-record'}
]
if json.loads(json.dumps(n.value)) == expected_value:
info("OK")
else:
warn("FAILED")
......
This diff is collapsed.
import json
import collections
import copy
from threading import Lock
from enum import Enum
from colorlog import error, warning as warn, info, debug
from typing import List, Set
from yangson.instance import \
Instance, \
NonexistentInstance, \
......@@ -133,14 +133,14 @@ class NacmConfig:
self._lock_username = None
try:
nacm_data_root = self.nacm_ds.get_data_root().member("ietf-netconf-acm:nacm")
self.nacm_ds.get_data_root().member("ietf-netconf-acm:nacm")
except NonexistentInstance:
raise ValueError("Data does not contain \"ietf-netconf-acm:nacm\" root element")
self.update(nacm_data_root)
self.update()
# Fills internal read-only data structures
def update(self, nacm_data: Instance):
def update(self):
lock_res = self.internal_data_lock.acquire(blocking=True, timeout=1)
if not lock_res:
error("NACM update: cannot acquire data lock")
......@@ -149,7 +149,7 @@ class NacmConfig:
self.nacm_groups = []
self.rule_lists = []
nacm_json = nacm_data.value
nacm_json = self.nacm_ds.get_data_root().member("ietf-netconf-acm:nacm").value
self.enabled = nacm_json["enable-nacm"]
# NACM not enabled, no need to continue
......
......@@ -10,7 +10,7 @@ from h2.events import DataReceived, RequestReceived, RemoteSettingsChanged
import jetconf.http_handlers as handlers
from .config import CONFIG_HTTP, NACM_API_ROOT_data, API_ROOT_data, load_config, print_config
from .nacm import NacmConfig
from .data import JsonDatastore, Rpc, NacmForbiddenError, DataLockError, InstanceAlreadyPresent
from .data import JsonDatastore
# Function(method, path) -> bool
......@@ -114,18 +114,29 @@ def run():
ex_datastore.register_nacm(nacmc)
# Register HTTP handlers
get_api = handlers.create_get_api(ex_datastore)
get_nacm_api = handlers.create_get_nacm_api(ex_datastore)
put_post_nacm_api = handlers.create_put_post_nacm_api(ex_datastore)
api_get_root = handlers.api_root_handler
api_get = handlers.create_get_api(ex_datastore)
api_post = handlers.create_post_api(ex_datastore)
api_put = handlers.create_put_api(ex_datastore)
api_delete = handlers.create_api_delete(ex_datastore)
nacm_api_get = handlers.create_get_nacm_api(ex_datastore)
nacm_api_post = handlers.create_post_nacm_api(ex_datastore)
nacm_api_put = handlers.create_put_nacm_api(ex_datastore)
nacm_api_delete = handlers.create_nacm_api_delete(ex_datastore)
h2_handlers = HandlerList()
h2_handlers.register_handler(lambda m, p: (m == "POST") and (p.startswith(NACM_API_ROOT_data)), put_post_nacm_api)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["API_ROOT"]), api_get_root)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(API_ROOT_data)), api_get)
h2_handlers.register_handler(lambda m, p: (m == "POST") and (p.startswith(API_ROOT_data)), api_post)
h2_handlers.register_handler(lambda m, p: (m == "PUT") and (p.startswith(API_ROOT_data)), api_put)
h2_handlers.register_handler(lambda m, p: (m == "DELETE") and (p.startswith(API_ROOT_data)), api_delete)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["NACM_API_ROOT"]), api_get_root)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(NACM_API_ROOT_data)), nacm_api_get)
h2_handlers.register_handler(lambda m, p: (m == "POST") and (p.startswith(NACM_API_ROOT_data)), nacm_api_post)
h2_handlers.register_handler(lambda m, p: (m == "PUT") and (p.startswith(NACM_API_ROOT_data)), nacm_api_put)
h2_handlers.register_handler(lambda m, p: (m == "DELETE") and (p.startswith(NACM_API_ROOT_data)), nacm_api_delete)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["NACM_API_ROOT"]), handlers.api_root_handler)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p == CONFIG_HTTP["API_ROOT"]), handlers.api_root_handler)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(NACM_API_ROOT_data)), get_nacm_api)
h2_handlers.register_handler(lambda m, p: (m == "GET") and (p.startswith(API_ROOT_data)), get_api)
h2_handlers.register_handler(lambda m, p: m == "GET", handlers.get_file)
# HTTP server init
......
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