knot_api: Knot transaction is started only once during commit

parent b497351e
......@@ -28,7 +28,7 @@ from yangson.instance import (
ArrayEntry
)
from .helpers import PathFormat, ErrorHelpers, LogHelpers, DataHelpers
from .helpers import PathFormat, ErrorHelpers, LogHelpers, DataHelpers, JsonNodeT
from .config import CONFIG
from .nacm import NacmConfig, Permission, Action
from .handler_list import OP_HANDLERS, STATE_DATA_HANDLES, CONF_DATA_HANDLES
......@@ -141,8 +141,9 @@ class ChangeList:
class UsrChangeJournal:
def __init__(self, root_origin: InstanceNode):
def __init__(self, root_origin: InstanceNode, transaction_opts: Optional[JsonNodeT]):
self.root_origin = root_origin
self.transaction_opts = transaction_opts
self.clists = [] # type: List[ChangeList]
def cl_new(self, cl_name: str):
......@@ -175,7 +176,6 @@ class UsrChangeJournal:
return chl_json
def commit(self, ds: "BaseDatastore"):
# Set new data root
if hash(ds.get_data_root()) == hash(self.root_origin):
info("Commiting new configuration (swapping roots)")
# Set new root
......@@ -202,6 +202,10 @@ class UsrChangeJournal:
new_data_valid = False
if new_data_valid:
# Call commit begin hook
if ds.commit_begin_callback is not None:
ds.commit_begin_callback(self.transaction_opts)
# Set new data root
ds.set_data_root(nr)
......@@ -218,6 +222,10 @@ class UsrChangeJournal:
# Clear user changelists
self.clists.clear()
# Call commit end hook
if ds.commit_end_callback is not None:
ds.commit_end_callback(self.transaction_opts)
class BaseDatastore:
def __init__(self, dm: DataModel, name: str="", with_nacm: bool=False):
......@@ -429,14 +437,14 @@ class BaseDatastore:
if insert == "first":
# Optimization
if len(existing_member) > 0:
if len(existing_member.value) > 0:
list_entry_first = existing_member[0] # type: ArrayEntry
new_member = list_entry_first.insert_after(new_value_item).up()
new_member = list_entry_first.insert_before(new_value_item).up()
else:
new_member = existing_member.update(new_value_list)
elif (insert == "last") or insert is None:
# Optimization
if len(existing_member) > 0:
if len(existing_member.value) > 0:
list_entry_last = existing_member[-1] # type: ArrayEntry
new_member = list_entry_last.insert_after(new_value_item).up()
else:
......@@ -529,17 +537,17 @@ class BaseDatastore:
if nrpc.check_rpc_name(rpc.op_name) == Action.DENY:
raise NacmForbiddenError("Op \"{}\" invocation denied for user \"{}\"".format(rpc.op_name, rpc.username))
ret_data = {}
if rpc.op_name == "conf-start":
if self._usr_journals.get(rpc.username) is None:
self._usr_journals[rpc.username] = UsrChangeJournal(self._data)
try:
cl_name = rpc.op_input_args["name"]
except (TypeError, KeyError):
raise ValueError("This operation expects \"name\" input parameter")
transaction_opts = rpc.op_input_args.get("options")
if self._usr_journals.get(rpc.username) is None:
self._usr_journals[rpc.username] = UsrChangeJournal(self._data, transaction_opts)
self._usr_journals[rpc.username].cl_new(cl_name)
ret_data = {"status": "OK"}
elif rpc.op_name == "conf-list":
......@@ -564,23 +572,17 @@ class BaseDatastore:
elif rpc.op_name == "conf-commit":
usr_journal = self._usr_journals.get(rpc.username)
if usr_journal is not None:
if self.commit_begin_callback is not None:
self.commit_begin_callback()
try:
self.lock_data(rpc.username)
old_root = self._data
usr_journal.commit(self)
if CONFIG["GLOBAL"]["PERSISTENT_CHANGES"] is True:
self.save()
finally:
self.unlock_data()
if self.commit_end_callback is not None:
self.commit_end_callback()
del self._usr_journals[rpc.username]
else:
warn("Nothing to commit")
info("[{}]: Nothing to commit".format(rpc.username))
ret_data = \
{
......
This diff is collapsed.
......@@ -8,7 +8,7 @@ from .config import CONFIG
from .helpers import LogHelpers
KNOT = None # type: KnotConfig
JsonNodeT = Union[Dict[str, Any], List]
JsonNodeT = Union[Dict[str, Any], List[Any], str, int]
debug_knot = LogHelpers.create_module_dbg_logger(__name__)
......@@ -48,7 +48,7 @@ class RRecordBase:
raise NotImplementedError("Not implemented in base class")
@property
def ttl_str(self):
def ttl_str(self) -> Optional[str]:
return str(self.ttl) if self.ttl is not None else None
......@@ -266,12 +266,26 @@ class KnotConfig(KnotCtl):
return resp
def knot_connect():
def knot_connect(transaction_opts: Optional[JsonNodeT]):
debug_knot("Connecting to KNOT socket")
KNOT.knot_connect()
if transaction_opts in ("config", None):
debug_knot("Starting new KNOT config transaction")
KNOT.begin()
elif transaction_opts == "zone":
debug_knot("Starting new KNOT zone transaction")
KNOT.begin_zone()
def knot_disconnect(transaction_opts: Optional[JsonNodeT]):
if transaction_opts in ("config", None):
debug_knot("Commiting KNOT config transaction")
KNOT.commit()
elif transaction_opts == "zone":
debug_knot("Commiting KNOT zone transaction")
KNOT.commit_zone()
def knot_disconnect():
debug_knot("Disonnecting from KNOT socket")
KNOT.knot_disconnect()
......
......@@ -20,8 +20,6 @@ class KnotConfServerListener(BaseDataListener):
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_nv = self.ds.get_data_root().goto(base_ii).value
knot_api.KNOT.begin()
knot_api.KNOT.set_item(section="server", item="comment", data=base_nv.get("description"))
knot_api.KNOT.set_item(section="server", item="async-start", data=base_nv.get("knot-dns:async-start"))
knot_api.KNOT.set_item(section="server", item="nsid", data=base_nv.get("nsid-identity", {}).get("nsid"))
......@@ -42,7 +40,6 @@ class KnotConfServerListener(BaseDataListener):
knot_api.KNOT.set_item(section="server", item="udp-workers", data=base_nv.get("resources", {}).get("knot-dns:udp-workers"))
knot_api.KNOT.set_item(section="server", item="rate-limit-table-size", data=base_nv.get("response-rate-limiting", {}).get("table-size"))
knot_api.KNOT.commit()
return ConfHandlerResult.OK
......@@ -54,7 +51,6 @@ class KnotConfLogListener(BaseDataListener):
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_nv = self.ds.get_data_root().goto(base_ii).value
knot_api.KNOT.begin()
knot_api.KNOT.set_item(section="log", data=None)
for logitem in base_nv:
......@@ -68,7 +64,6 @@ class KnotConfLogListener(BaseDataListener):
knot_api.KNOT.set_item(section="log", identifier=tgt, item="zone", data=logitem.get("zone"))
knot_api.KNOT.set_item(section="log", identifier=tgt, item="any", data=logitem.get("any"))
knot_api.KNOT.commit()
return ConfHandlerResult.OK
......@@ -80,8 +75,6 @@ class KnotConfZoneListener(BaseDataListener):
base_ii_str = self.schema_path
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
knot_api.KNOT.begin()
# Create new zone
if (ii == base_ii) and (ch.change_type == ChangeType.CREATE):
domain = ch.data["zone"]["domain"]
......@@ -122,7 +115,6 @@ class KnotConfZoneListener(BaseDataListener):
knot_api.KNOT.set_item_list(section="zone", zone=domain, item="module", data=qm_str_list)
knot_api.KNOT.set_item(section="zone", zone=domain, item="semantic-checks", data=zone_nv.get("knot-dns:semantic-checks"))
knot_api.KNOT.commit()
return ConfHandlerResult.OK
......@@ -134,9 +126,7 @@ class KnotConfControlListener(BaseDataListener):
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_nv = self.ds.get_data_root().goto(base_ii).value
knot_api.KNOT.begin()
knot_api.KNOT.set_item(section="control", item="listen", data=base_nv.get("unix"))
knot_api.KNOT.commit()
return ConfHandlerResult.OK
......@@ -166,7 +156,6 @@ class KnotConfAclListener(BaseDataListener):
base_ii = DataHelpers.parse_ii(base_ii_str, PathFormat.URL)
base_nv = self.ds.get_data_root().goto(base_ii).value
knot_api.KNOT.begin()
knot_api.KNOT.set_item(section="acl", data=None)
if (len(ii) > len(base_ii)) and isinstance(ii[len(base_ii)], EntryKeys):
......@@ -182,7 +171,6 @@ class KnotConfAclListener(BaseDataListener):
print("acl nv={}".format(acl_nv))
self._process_list_item(acl_nv)
knot_api.KNOT.commit()
return ConfHandlerResult.OK
......@@ -244,9 +232,7 @@ class KnotZoneDataListener(BaseDataListener):
soarr.minimum = soa["minimum"]
debug_confh("KnotApi: adding new SOA RR to zone \"{}\"".format(domain_name))
knot_api.KNOT.begin_zone()
knot_api.KNOT.zone_add_record(domain_name, soarr)
knot_api.KNOT.commit_zone()
# Add resource record to particular zone
elif (
......@@ -268,9 +254,7 @@ class KnotZoneDataListener(BaseDataListener):
new_rr = self._rr_from_rdata_item(domain_name, rr_owner, rr_ttl, rr_type, rdata_item)
if new_rr is not None:
debug_confh("KnotApi: adding new {} RR to zone \"{}\"".format(rr_type, domain_name))
knot_api.KNOT.begin_zone()
knot_api.KNOT.zone_add_record(domain_name, new_rr)
knot_api.KNOT.commit_zone()
# Add resource record to particular zone (only specific "rdata" item)
elif (
......@@ -297,9 +281,7 @@ class KnotZoneDataListener(BaseDataListener):
if new_rr is not None:
debug_confh("KnotApi: adding new {} RR to zone \"{}\"".format(rr_type, domain_name))
knot_api.KNOT.begin_zone()
knot_api.KNOT.zone_add_record(domain_name, new_rr)
knot_api.KNOT.commit_zone()
# Delete resource record from particular zone
elif (
......@@ -315,9 +297,7 @@ class KnotZoneDataListener(BaseDataListener):
rr_type = keys_ii_seg.keys["type"][0]
debug_confh("KnotApi: deleting {} RR from zone \"{}\"".format(rr_type, domain_name))
knot_api.KNOT.begin_zone()
knot_api.KNOT.zone_del_record(domain_name, rr_owner, rr_type)
knot_api.KNOT.commit_zone()
# Delete resource record from particular zone (only specific "rdata" item)
elif (
......@@ -338,9 +318,7 @@ class KnotZoneDataListener(BaseDataListener):
rr_sel = self._rr_from_rdata_item(domain_name, rr_owner, 0, rr_type, rdata_item)
debug_confh("KnotApi: deleting {} RR from zone \"{}\"".format(rr_type, domain_name))
knot_api.KNOT.begin_zone()
knot_api.KNOT.zone_del_record(domain_name, rr_owner, rr_type, selector=rr_sel.rrdata_format())
knot_api.KNOT.commit_zone()
else:
return ConfHandlerResult.ERROR
......
......@@ -3,7 +3,7 @@
CLIENT_CERT="/home/pspirek/sslclient/pavel_curl.pem"
echo "--- conf-start 1"
POST_DATA='{ "dns-server:input": {"name": "Edit 1"} }'
POST_DATA='{ "dns-server:input": {"name": "Edit 1", "options": "config"} }'
URL="https://127.0.0.1:8443/restconf/operations/dns-server:conf-start"
curl -v --http2 -k --cert-type PEM -E $CLIENT_CERT -X POST -d "$POST_DATA" "$URL" 2>/dev/null
......@@ -19,7 +19,7 @@ curl -v --http2 -k --cert-type PEM -E $CLIENT_CERT -X POST "$URL" 2>/dev/null
sleep 2
echo "--- zone conf-start 1"
POST_DATA='{ "dns-server:input": {"name": "Zone data edit 1"} }'
POST_DATA='{ "dns-server:input": {"name": "Zone data edit 1", "options": "zone"} }'
URL="https://127.0.0.1:8443/restconf/operations/dns-server:conf-start"
curl -v --http2 -k --cert-type PEM -E $CLIENT_CERT -X POST -d "$POST_DATA" "$URL" 2>/dev/null
......
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