Adapted to new version of yangson library, code refactor

parent 40897986
......@@ -81,12 +81,9 @@ def main():
datamodel = DataHelpers.load_data_model("data/", "data/yang-library-data.json")
# Datastore init
datastore = JsonDatastore(datamodel, "jetconf/example-data.json", "DNS data")
datastore = JsonDatastore(datamodel, "jetconf/example-data.json", "DNS data", with_nacm=True)
datastore.load()
datastore.load_yl_data("data/yang-library-data.json")
nacmc = NacmConfig(datastore)
datastore.register_nacm(nacmc)
nacmc.set_ds(datastore)
datastore.get_data_root().validate(ContentType.config)
......
......@@ -23,7 +23,8 @@ CONFIG_HTTP = {
"SERVER_SSL_CERT": "server.crt",
"SERVER_SSL_PRIVKEY": "server.key",
"CA_CERT": "ca.pem"
"CA_CERT": "ca.pem",
"DBG_DISABLE_CERTS": False
}
CONFIG_NACM = {
......
......@@ -4,7 +4,7 @@ GLOBAL:
PIDFILE: "/tmp/jetconf.pid"
PERSISTENT_CHANGES: true
LOG_LEVEL: "debug"
LOG_DBG_MODULES: ["usr_conf_data_handlers", "knot_api"]
LOG_DBG_MODULES: ["usr_conf_data_handlers", "knot_api", "nacm"]
HTTP_SERVER:
DOC_ROOT: "jetconf/doc-root"
......@@ -15,6 +15,7 @@ HTTP_SERVER:
SERVER_SSL_CERT: "jetconf/server.crt"
SERVER_SSL_PRIVKEY: "jetconf/server.key"
CA_CERT: "jetconf/ca.pem"
DBG_DISABLE_CERTS: false
NACM:
ALLOWED_USERS: ["lojza@mail.cz"]
......
......@@ -206,7 +206,7 @@ class UsrChangeJournal:
class BaseDatastore:
def __init__(self, dm: DataModel, name: str=""):
def __init__(self, dm: DataModel, name: str="", with_nacm: bool=False):
self.name = name
self.nacm = None # type: NacmConfig
self._data = None # type: InstanceNode
......@@ -219,9 +219,8 @@ class BaseDatastore:
self.commit_begin_callback = None # type: Callable
self.commit_end_callback = None # type: Callable
# Register NACM module to datastore
def register_nacm(self, nacm_config: "NacmConfig"):
self.nacm = nacm_config
if with_nacm:
self.nacm = NacmConfig(self)
# Returns the root node of data tree
def get_data_root(self, previous_version: int=0) -> InstanceNode:
......@@ -311,23 +310,22 @@ class BaseDatastore:
else:
root = self._data
# n = root.goto(ii)
# sn = n.schema_node
n = root.goto(ii)
sch_pth_list = filter(lambda n: isinstance(n, MemberName), ii)
sch_pth = "".join([str(seg) for seg in sch_pth_list])
sn = self.get_schema_node(sch_pth)
if not yl_data:
if sn.state_roots():
self.commit_begin_callback()
for state_node_pth in sn.state_roots():
sdh = STATE_DATA_HANDLES.get_handler(state_node_pth)
if sdh is not None:
root_val = sdh.update_node(ii, root, True)
root = self._data.update(root_val, raw=True)
else:
raise NoHandlerForStateDataError()
self.commit_end_callback()
state_roots = sn.state_roots()
if not yl_data and state_roots:
self.commit_begin_callback()
for state_node_pth in state_roots:
sdh = STATE_DATA_HANDLES.get_handler(state_node_pth)
if sdh is not None:
root_val = sdh.update_node(ii, root, True)
root = self._data.update(root_val, raw=True)
else:
raise NoHandlerForStateDataError()
self.commit_end_callback()
n = root.goto(ii)
......@@ -347,7 +345,7 @@ class BaseDatastore:
raise NacmForbiddenError()
else:
# Prun subtree data
n = nrpc.check_data_read_path(root, ii)
n = nrpc.check_data_read_path(n, root, ii)
try:
max_depth = int(rpc.qs["depth"][0])
......@@ -434,8 +432,7 @@ class BaseDatastore:
# Create list if necessary
if existing_member is None:
new_n = n.put_member(input_member_name, ArrayValue([]))
existing_member = new_n.member(input_member_name)
existing_member = n.put_member(input_member_name, ArrayValue([]))
# Convert input data from List/Dict to ArrayValue/ObjectValue
new_value_data = member_sn.from_raw([input_member_value])[0]
......@@ -463,8 +460,7 @@ class BaseDatastore:
# Create leaf list if necessary
if existing_member is None:
new_n = n.put_member(input_member_name, ArrayValue([]))
existing_member = new_n.member(input_member_name)
existing_member = n.put_member(input_member_name, ArrayValue([]))
# Convert input data from List/Dict to ArrayValue/ObjectValue
new_value_data = member_sn.from_raw([input_member_value])[0]
......@@ -650,8 +646,8 @@ class BaseDatastore:
class JsonDatastore(BaseDatastore):
def __init__(self, dm: DataModel, json_file: str, name: str = ""):
super().__init__(dm, name)
def __init__(self, dm: DataModel, json_file: str, name: str = "", with_nacm: bool=False):
super().__init__(dm, name, with_nacm)
self.json_file = json_file
def load(self):
......@@ -659,6 +655,9 @@ class JsonDatastore(BaseDatastore):
with open(self.json_file, "rt") as fp:
self._data = self._dm.from_raw(json.load(fp))
if self.nacm is not None:
self.nacm.update()
def load_yl_data(self, filename: str):
self._yang_lib_data = None
with open(filename, "rt") as fp:
......
......@@ -8,9 +8,7 @@ from pytz import timezone
from yangson.instance import InstanceRoute, MemberName, EntryKeys, InstanceIdParser, ResourceIdParser
from yangson.datamodel import DataModel
from .config import CONFIG
CERT_TEST = True
from .config import CONFIG_GLOBAL, CONFIG_HTTP
class PathFormat(Enum):
......@@ -21,7 +19,7 @@ class PathFormat(Enum):
class CertHelpers:
@staticmethod
def get_field(cert: Dict[str, Any], key: str) -> str:
if CERT_TEST and (key == "emailAddress"):
if CONFIG_HTTP["DBG_DISABLE_CERTS"] and (key == "emailAddress"):
return "test-user"
try:
......@@ -99,7 +97,7 @@ class LogHelpers:
module_name_simple = module_name.split(".")[-1]
def module_dbg_logger(msg: str):
if ({module_name_simple, "*"} & set(CONFIG["GLOBAL"]["LOG_DBG_MODULES"])) and (CONFIG["GLOBAL"]["LOG_LEVEL"] == "debug"):
if ({module_name_simple, "*"} & set(CONFIG_GLOBAL["LOG_DBG_MODULES"])) and (CONFIG_GLOBAL["LOG_LEVEL"] == "debug"):
logger = getLogger()
logger.setLevel(logging.DEBUG)
debug(module_name_simple + ": " + msg)
......
......@@ -10,7 +10,7 @@ from yangson.schema import NonexistentSchemaNode
from yangson.instance import NonexistentInstance, InstanceValueError
from yangson.datatype import YangTypeError
from jetconf.knot_api import KnotError
from .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
from .data import (
......@@ -82,7 +82,7 @@ def _get(prot: "H2Protocol", stream_id: int, ds: BaseDatastore, pth: str, yl_dat
("server", CONFIG_HTTP["SERVER_NAME"])
]
try:
lm_time = DateTimeHelpers.to_httpdate_str(n.value.last_modified, CONFIG_GLOBAL["TIMEZONE"])
lm_time = DateTimeHelpers.to_httpdate_str(n.value.timestamp, CONFIG_GLOBAL["TIMEZONE"])
response_headers.append(("Last-Modified", lm_time))
except AttributeError:
# Only arrays and objects have last_modified attribute
......@@ -220,7 +220,7 @@ def _get_staging(prot: "H2Protocol", stream_id: int, ds: BaseDatastore, pth: str
("server", CONFIG_HTTP["SERVER_NAME"])
]
try:
lm_time = DateTimeHelpers.to_httpdate_str(n.value.last_modified, CONFIG_GLOBAL["TIMEZONE"])
lm_time = DateTimeHelpers.to_httpdate_str(n.value.timestamp, CONFIG_GLOBAL["TIMEZONE"])
response_headers.append(("Last-Modified", lm_time))
except AttributeError:
# Only arrays and objects have last_modified attribute
......@@ -279,9 +279,8 @@ def create_get_staging_api(ds: BaseDatastore):
return get_staging_api_closure
def _post(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pth: str):
data_str = data.decode("utf-8")
debug_httph("HTTP data received: " + data_str)
def _post(prot: "H2Protocol", data: str, stream_id: int, ds: BaseDatastore, pth: str):
debug_httph("HTTP data received: " + data)
url_split = pth.split("?")
url_path = url_split[0]
......@@ -297,7 +296,7 @@ def _post(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pt
rpc1.path = url_path
try:
json_data = json.loads(data_str) if len(data_str) > 0 else {}
json_data = json.loads(data) if len(data) > 0 else {}
except ValueError as e:
error("Failed to parse POST data: " + epretty(e))
prot.send_empty(stream_id, "400", "Bad Request")
......@@ -333,7 +332,7 @@ def _post(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pt
def create_post_api(ds: BaseDatastore):
def post_api_closure(prot: "H2Protocol", stream_id: int, headers: OrderedDict, data: bytes):
def post_api_closure(prot: "H2Protocol", stream_id: int, headers: OrderedDict, data: str):
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
info("[{}] api_post: {}".format(username, headers[":path"]))
......@@ -353,9 +352,8 @@ def create_post_api(ds: BaseDatastore):
return post_api_closure
def _put(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pth: str):
data_str = data.decode("utf-8")
debug_httph("HTTP data received: " + data_str)
def _put(prot: "H2Protocol", data: str, stream_id: int, ds: BaseDatastore, pth: str):
debug_httph("HTTP data received: " + data)
url_split = pth.split("?")
url_path = url_split[0]
......@@ -367,7 +365,7 @@ def _put(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pth
rpc1.path = url_path
try:
json_data = json.loads(data_str) if len(data_str) > 0 else {}
json_data = json.loads(data) if len(data) > 0 else {}
except ValueError as e:
error("Failed to parse PUT data: " + epretty(e))
prot.send_empty(stream_id, "400", "Bad Request")
......@@ -398,7 +396,7 @@ def _put(prot: "H2Protocol", data: bytes, stream_id: int, ds: BaseDatastore, pth
def create_put_api(ds: BaseDatastore):
def put_api_closure(prot: "H2Protocol", stream_id: int, headers: OrderedDict, data: bytes):
def put_api_closure(prot: "H2Protocol", stream_id: int, headers: OrderedDict, data: str):
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
info("[{}] api_put: {}".format(username, headers[":path"]))
......@@ -474,10 +472,9 @@ def create_api_delete(ds: BaseDatastore):
def create_api_op(ds: BaseDatastore):
def api_op_closure(prot: "H2Protocol", stream_id: int, headers: OrderedDict, data: bytes):
def api_op_closure(prot: "H2Protocol", stream_id: int, headers: OrderedDict, data: str):
username = CertHelpers.get_field(prot.client_cert, "emailAddress")
info("[{}] invoke_op: {}".format(username, headers[":path"]))
data_str = data.decode("utf-8")
api_pth = headers[":path"][len(API_ROOT_ops):]
op_name_fq = api_pth[1:]
......@@ -492,7 +489,7 @@ def create_api_op(ds: BaseDatastore):
op_name = op_name_splitted[1]
try:
json_data = json.loads(data_str) if len(data_str) > 0 else {}
json_data = json.loads(data) if len(data) > 0 else {}
except ValueError as e:
error("Failed to parse POST data: " + epretty(e))
prot.send_empty(stream_id, "400", "Bad Request")
......
This diff is collapsed.
import io
import asyncio
import ssl
from io import BytesIO
from collections import OrderedDict
from colorlog import error, warning as warn, info, debug
from typing import List, Tuple, Dict, Any, Callable
......@@ -77,7 +77,7 @@ class H2Protocol(asyncio.Protocol):
if isinstance(event, RequestReceived):
# Store request headers
headers = OrderedDict(event.headers)
request_data = RequestData(headers, io.BytesIO())
request_data = RequestData(headers, BytesIO())
self.stream_data[event.stream_id] = request_data
elif isinstance(event, DataReceived):
# Store incoming data
......@@ -150,12 +150,13 @@ class RestServer:
except AttributeError:
info("Python not compiled with ALPN support, using NPN instead.")
ssl_context.set_npn_protocols(["h2"])
# ssl_context.verify_mode = ssl.CERT_REQUIRED
if not CONFIG_HTTP["DBG_DISABLE_CERTS"]:
ssl_context.verify_mode = ssl.CERT_REQUIRED
ssl_context.load_verify_locations(cafile=CONFIG_HTTP["CA_CERT"])
self.loop = asyncio.get_event_loop()
# Each client connection will create a new protocol instance
# Each client connection will create a new H2Protocol instance
listener = self.loop.create_server(H2Protocol, "127.0.0.1", CONFIG_HTTP["PORT"], ssl=ssl_context)
self.server = self.loop.run_until_complete(listener)
......
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