Commit 78340a58 authored by Pavel Spirek's avatar Pavel Spirek

Better error reporting, minor fixes

parent a4357ea7
PROJECT = jetconf
VERSION = 0.3.3
VERSION = 0.3.4
.PHONY = tags deps install-deps test
tags:
......
......@@ -679,7 +679,7 @@ class BaseDatastore:
# Unlock datastore data
def unlock_data(self):
self._data_lock.release()
debug_data("Released datastore lockfor user \"{}\"".format(self._lock_username))
debug_data("Released datastore lock for user \"{}\"".format(self._lock_username))
self._lock_username = None
# Load data from persistent storage
......
import sys
import logging
from colorlog import debug, getLogger
......@@ -97,12 +98,12 @@ class DateTimeHelpers:
class ErrorHelpers:
@staticmethod
def epretty(e: BaseException, module_name: str=None) -> str:
err_str = e.__class__.__name__ + ": " + str(e)
if module_name is not None:
return "In module " + module_name + ": " + err_str
else:
return err_str
def epretty(e: BaseException) -> str:
ex_type, ex_val, ex_tb = sys.exc_info()
line_no = ex_tb.tb_lineno
filename = ex_tb.tb_frame.f_code.co_filename
return "{}: {} (file {}, line {})".format(e.__class__.__name__, str(e), filename, line_no)
class LogHelpers:
......
......@@ -6,12 +6,12 @@ from collections import OrderedDict
from enum import Enum
from colorlog import error, warning as warn, info
from urllib.parse import parse_qs
from datetime import datetime
from typing import Dict, List, Tuple, Any, Optional, Callable
from yangson.exceptions import YangsonException, NonexistentSchemaNode, SchemaError, SemanticError
from yangson.schemanode import ContainerNode, ListNode, GroupNode, LeafNode
from yangson.instance import NonexistentInstance, InstanceValueError, RootNode
from yangson.instvalue import ArrayValue
from . import config
from .helpers import CertHelpers, DateTimeHelpers, ErrorHelpers, LogHelpers, SSLCertT
......@@ -202,15 +202,16 @@ class HttpHandlersImpl:
"unknown_request"
)
@staticmethod
def _get_yl_date() -> str:
try:
yang_lib_date_ts = os.path.getmtime(os.path.join(config.CFG.glob["YANG_LIB_DIR"], "yang-library-data.json"))
yang_lib_date = datetime.fromtimestamp(yang_lib_date_ts).strftime("%Y-%m-%d")
except OSError:
yang_lib_date = None
def _get_yl_date(self) -> str:
yl_modules = self.ds.get_yl_data_root()["ietf-yang-library:modules-state"]["module"].value # type: ArrayValue
revision_val = None
for module in yl_modules:
if module["name"] == "ietf-yang-library":
revision_val = module["revision"]
break
return yang_lib_date
return revision_val
def get_api_root(self, headers: OrderedDict, data: Optional[str], client_cert: SSLCertT):
# Top level api resource (appendix B.1.1)
......@@ -372,7 +373,7 @@ class HttpHandlersImpl:
api_pth = headers[":path"][len(config.CFG.api_root_ops):].rstrip("/")
op_name_fq = api_pth[1:].split("/", maxsplit=1)[0]
op_names_dict = dict(map(lambda n: (n[0], None), self.ds.handlers.op.handlers))
op_names_dict = dict(map(lambda n: (n, None), self.ds.handlers.op.handlers))
if api_pth == "":
# GET root
......
......@@ -39,13 +39,6 @@ class Jetconf:
self.fl = -1
raise JetconfInitError("Jetconf already running (pidfile exists)")
# Set signal handlers
# def sig_exit_handler(signum, frame):
# self.exit_clean(0)
#
# signal.signal(signal.SIGTERM, sig_exit_handler)
# signal.signal(signal.SIGINT, sig_exit_handler)
# Import backend modules
backend_package = self.config.glob["BACKEND_PACKAGE"]
try:
......
......@@ -290,6 +290,10 @@ class UserRuleSet:
self.default_write = config.default_write
self.default_exec = config.default_exec
self.rule_lists = []
self.rule_tree = None
if not self.nacm_enabled:
return
user_groups = list(filter(lambda x: username in x.users, config.nacm_groups))
user_groups_names = list(map(lambda x: x.name, user_groups))
......
......@@ -4,15 +4,15 @@ import ssl
from io import BytesIO
from collections import OrderedDict
from colorlog import error, warning as warn, info
from typing import List, Tuple, Dict, Callable, Optional
from typing import Dict, Optional
from h2.config import H2Configuration
from h2.connection import H2Connection
from h2.errors import ErrorCodes as H2ErrorCodes
from h2.exceptions import ProtocolError
from h2.events import DataReceived, RequestReceived, RemoteSettingsChanged, StreamEnded, WindowUpdated
from h2.events import DataReceived, RequestReceived, RemoteSettingsChanged, StreamEnded, WindowUpdated, ConnectionTerminated
from . import config, http_handlers as handlers
from . import config
from .helpers import SSLCertT, LogHelpers
from .data import BaseDatastore
from .http_handlers import (
......@@ -44,6 +44,7 @@ class ResponseData:
class H2Protocol(asyncio.Protocol):
HTTP_HANDLERS = None # type: HttpHandlersImpl
LOOP = None # type: asyncio.BaseEventLoop
def __init__(self):
self.conn = H2Connection(H2Configuration(client_side=False, header_encoding="utf-8"))
......@@ -64,7 +65,7 @@ class H2Protocol(asyncio.Protocol):
if agreed_protocol is None:
error("Connection error, client does not support HTTP/2")
transport.close()
self.transport.close()
else:
self.conn.initiate_connection()
......@@ -87,6 +88,7 @@ class H2Protocol(asyncio.Protocol):
# Check if incoming data are not excessively large
if (stream_data.data.tell() + len(event.data)) < (config.CFG.http["UPLOAD_SIZE_LIMIT"] * 1048576):
stream_data.data.write(event.data)
self.conn.acknowledge_received_data(len(event.data), event.stream_id)
else:
stream_data.data_overflow = True
self.conn.reset_stream(event.stream_id, error_code=H2ErrorCodes.ENHANCE_YOUR_CALM)
......@@ -131,15 +133,12 @@ class H2Protocol(asyncio.Protocol):
self.conn.update_settings(changed_settings)
elif isinstance(event, WindowUpdated):
try:
debug_srv(
"str {} nw={}".format(event.stream_id, self.conn.local_flow_control_window(event.stream_id))
)
debug_srv("str {} nw={}".format(event.stream_id, self.conn.local_flow_control_window(event.stream_id)))
self.send_response_continue(event.stream_id)
except (ProtocolError, KeyError) as e:
# debug_srv("wupdexception strid={}: {}".format(event.stream_id, str(e)))
pass
# else:
# print(type(event))
debug_srv("wupexception strid={}: {}".format(event.stream_id, str(e)))
elif isinstance(event, ConnectionTerminated):
self.transport.close()
dts = self.conn.data_to_send()
if dts:
......@@ -153,6 +152,29 @@ class H2Protocol(asyncio.Protocol):
url_path = headers[":path"].split("?")[0].rstrip("/")
method = headers[":method"]
################
# if url_path == "/evtest":
# self.ev_stream_start_response(stream_id)
# i = 0
#
# def cb():
# self.ev_stream_send_data("ahoj\ncau\n1", stream_id)
#
# nonlocal i
# i += 1
# if i < 5:
# self.LOOP.call_later(1, cb)
# elif stream_id in self.conn.streams.keys():
# self.conn.end_stream(stream_id)
#
# dts = self.conn.data_to_send()
# if dts:
# self.transport.write(dts)
#
# cb()
# return
###############
if method == "HEAD":
h = self.HTTP_HANDLERS.list.get("GET", url_path)
else:
......@@ -223,6 +245,30 @@ class H2Protocol(asyncio.Protocol):
self.conn.send_data(stream_id, bytes(), end_stream=True)
del self.resp_stream_data[stream_id]
def ev_stream_start_response(self, stream_id: int):
resp_headers = (
(":status", "200"),
("Transfer-Encoding", "Chunked"),
("Content-Type", "text/event-stream"),
("Server", config.CFG.http["SERVER_NAME"]),
("Cache-Control", "No-Cache"),
("Access-Control-Allow-Origin", "*"),
("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE"),
("Access-Control-Allow-Headers", "Content-Type")
)
self.conn.send_headers(stream_id, resp_headers)
def ev_stream_send_data(self, data: str, stream_id: int):
if stream_id not in self.conn.streams.keys():
return
data_lines = data.splitlines()
data_lines_pfxed = list(map(lambda l: "data: " + l + "\n", data_lines))
data_to_send = "".join(data_lines_pfxed) + "\n"
self.conn.send_data(stream_id, data_to_send.encode(), end_stream=False)
class RestServer:
def __init__(self):
......@@ -253,6 +299,8 @@ class RestServer:
)
self.server = self.loop.run_until_complete(listener)
H2Protocol.LOOP = self.loop
# Register HTTP handlers
@staticmethod
def register_api_handlers(datastore: BaseDatastore):
......
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