Commit 0c2662dd authored by Pavel Spirek's avatar Pavel Spirek

Better error handling in KNOT API

parent b11279c1
......@@ -3,7 +3,7 @@ import getopt
import logging
import sys
from colorlog import info, warning as warn
from colorlog import info, warning as warn, error
from importlib import import_module
from yangson.instance import InstancePath, NonexistentInstance, ObjectValue, EntryKeys
from . import usr_op_handlers, usr_state_data_handlers
......@@ -11,9 +11,11 @@ from .rest_server import RestServer
from .config import CONFIG, load_config, print_config
from .nacm import NacmConfig
from .data import JsonDatastore, BaseDataListener, SchemaNode, PathFormat, ChangeType, DataChange, ConfHandlerResult
from .helpers import DataHelpers
from .helpers import DataHelpers, ErrorHelpers
from .handler_list import OP_HANDLERS, STATE_DATA_HANDLES, CONF_DATA_HANDLES
from .knot_api import KNOT, KnotConfig, SOARecord
from .knot_api import KNOT, KnotConfig, SOARecord, KnotError
epretty = ErrorHelpers.epretty
def knot_connect():
......@@ -217,11 +219,13 @@ class KnotZoneDataListener(BaseDataListener):
elif (len(ii) > len(base_ii)) and isinstance(ii[len(base_ii) + 1], EntryKeys):
zone_name = ii[len(base_ii) + 1].keys["name"]
print("--- Zone \"{}\" resource {}".format(zone_name, ch.change_type.name.lower()))
if ch.change_type == ChangeType.CREATE:
soa = ch.data.get("SOA")
if soa is not None:
print("writing soa {}".format(soa))
KNOT.begin_zone()
soarr = SOARecord(zone_name)
soarr.mname = soa["mname"]
soarr.rname = soa["rname"]
......@@ -230,8 +234,12 @@ class KnotZoneDataListener(BaseDataListener):
soarr.retry = soa["retry"]
soarr.expire = soa["expire"]
soarr.minimum = soa["minimum"]
KNOT.zone_add_record(zone_name, soarr)
resp = KNOT.zone_add_record(zone_name, soarr)
print("resp = {}".format(resp))
KNOT.commit_zone()
return ConfHandlerResult.OK
......
......@@ -47,7 +47,7 @@ class DataLockError(Exception):
return self.msg
class NoHandlerError(Exception):
class InstanceAlreadyPresent(Exception):
def __init__(self, msg=""):
self.msg = msg
......@@ -55,7 +55,7 @@ class NoHandlerError(Exception):
return self.msg
class InstanceAlreadyPresent(Exception):
class HandlerError(Exception):
def __init__(self, msg=""):
self.msg = msg
......@@ -63,6 +63,10 @@ class InstanceAlreadyPresent(Exception):
return self.msg
class NoHandlerError(HandlerError):
pass
class NoHandlerForOpError(NoHandlerError):
pass
......
......@@ -9,6 +9,7 @@ from typing import Dict, List
from yangson.schema import NonexistentSchemaNode
from yangson.instance import NonexistentInstance, InstanceTypeError, DuplicateMember
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
from .data import \
......@@ -528,5 +529,8 @@ def create_api_op(ds: BaseDatastore):
except NoHandlerForOpError:
warn("Nonexistent handler for operation \"{}\"".format(op_name))
prot.send_empty(stream_id, "400", "Bad Request")
except KnotError as e:
warn(epretty(e))
prot.send_empty(stream_id, "500", "Internal Server Error")
return api_op_closure
......@@ -7,13 +7,7 @@ from .libknot.control import KnotCtl, KnotCtlType
KNOT = None # type: KnotConfig
class KnotConfState(Enum):
NONE = 0
CONF = 1
ZONE = 2
class KnotStateError(Exception):
class KnotError(Exception):
def __init__(self, msg=""):
self.msg = msg
......@@ -21,6 +15,24 @@ class KnotStateError(Exception):
return self.msg
class KnotApiStateError(KnotError):
pass
class KnotInternalError(KnotError):
pass
class KnotApiError(KnotError):
pass
class KnotConfState(Enum):
NONE = 0
CONF = 1
ZONE = 2
class RRecordBase:
def __init__(self, owner_name: str, res_type: str, ttl: int=3600):
self.owner = owner_name
......@@ -77,10 +89,10 @@ class KnotConfig(KnotCtl):
def knot_connect(self):
if self.connected:
raise Exception("Knot socket already opened")
raise KnotApiError("Knot socket already opened")
if not self.socket_lock.acquire(blocking=True, timeout=5):
raise Exception("Cannot acquire Knot socket lock")
raise KnotApiError("Cannot acquire Knot socket lock")
self.connect(self.sock_path)
self.connected = True
......@@ -106,19 +118,18 @@ class KnotConfig(KnotCtl):
self.send_block("conf-commit")
self.conf_state = KnotConfState.NONE
else:
raise KnotStateError()
raise KnotApiStateError()
def commit_zone(self):
if self.conf_state == KnotConfState.ZONE:
self.send_block("zone-commit")
self.conf_state = KnotConfState.NONE
else:
raise KnotStateError()
raise KnotApiStateError()
def set_item(self, item=None, section=None, identifier=None, zone=None, data: str=None):
if not self.connected:
raise Exception("Knot socket is closed")
raise KnotApiError("Knot socket is closed")
if data is not None:
self.send_block("conf-set", section=section, identifier=identifier, item=item, zone=zone, data=data)
......@@ -127,7 +138,7 @@ class KnotConfig(KnotCtl):
def set_item_list(self, item=None, section=None, identifier=None, zone=None, data: List[str]=None):
if not self.connected:
raise Exception("Knot socket is closed")
raise KnotApiError("Knot socket is closed")
self.send_block("conf-unset", section=section, identifier=identifier, item=item, zone=zone)
if data is None:
......@@ -138,7 +149,7 @@ class KnotConfig(KnotCtl):
def set_zone_item(self, section=None, identifier=None, item=None, zone=None, owner=None, ttl=None, type=None, data=None):
if not self.connected:
raise Exception("Knot socket is closed")
raise KnotApiError("Knot socket is closed")
if data is not None:
self.send_block("zone-add", section=section, identifier=identifier, item=item, zone=zone, owner=owner, ttl=ttl, type=type, data=data)
......@@ -146,12 +157,23 @@ class KnotConfig(KnotCtl):
self.send_block("zone-remove", section=section, identifier=identifier, item=item, zone=zone, owner=owner, ttl=ttl, type=type, data=data)
def zone_new(self, domain_name: str) -> str:
self.set_item(section="zone", item="domain", data=domain_name)
resp = self.receive_block()
if not self.connected:
raise KnotApiError("Knot socket is closed")
try:
self.set_item(section="zone", item="domain", data=domain_name)
resp = self.receive_block()
except Exception as e:
raise KnotInternalError(str(e))
return resp
def zone_add_record(self, domain_name: str, rr: RRecordBase) -> str:
self.set_zone_item(zone=domain_name, owner=rr.owner, ttl="3600", type=rr.type, data=rr.rrdata_format())
if not self.connected:
raise KnotApiError("Knot socket is closed")
resp = self.receive_zone_block()
try:
self.set_zone_item(zone=domain_name, owner=rr.owner, ttl="3600", type=rr.type, data=rr.rrdata_format())
resp = self.receive_zone_block()
except Exception as e:
raise KnotInternalError(str(e))
return resp
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