Refactor of zone data handlers and some datastore methods

parent dd209cd5
......@@ -2,7 +2,7 @@ import json
from threading import Lock
from enum import Enum
from colorlog import error, warning as warn, info
from typing import List, Any, Dict, Callable
from typing import List, Any, Dict, Callable, Optional
from yangson.schema import SchemaNode, NonexistentSchemaNode, ListNode, LeafListNode, SchemaError, SemanticError
from yangson.datamodel import DataModel
......@@ -87,7 +87,7 @@ class ConfHandlerResult(Enum):
class BaseDataListener:
def __init__(self, ds: "BaseDatastore", sch_pth: str):
self._ds = ds
self.ds = ds
self.schema_path = sch_pth # type: str
self.schema_node = ds.get_schema_node(sch_pth) # type: SchemaNode
......@@ -191,15 +191,15 @@ class UsrChangeJournal:
# Set new data root
ds.set_data_root(nr)
# Notify schema node observers
# Run schema node handlers
for cl in self.clists:
for change in cl.journal:
ii = ds.parse_ii(change.rpc_info.path, change.rpc_info.path_format)
ds.notify_edit(ii, change)
# if change.change_type != ChangeType.DELETE:
# ds.notify_edit(ii)
# else:
# ds.notify_edit(ii[0:-1])
try:
ds.run_conf_edit_handler(ii, change)
except Exception as e:
ds.data_root_rollback(1, False)
raise e
# Clear user changelists
self.clists.clear()
......@@ -210,7 +210,8 @@ class BaseDatastore:
self.name = name
self.nacm = None # type: NacmConfig
self._data = None # type: InstanceNode
self._yang_lib_data = None # type: InstanceNode
self._data_history = [] # type: List[InstanceNode]
self._yang_lib_data = None # type: InstanceNode
self._dm = dm # type: DataModel
self._data_lock = Lock()
self._lock_username = None # type: str
......@@ -223,8 +224,11 @@ class BaseDatastore:
self.nacm = nacm_config
# Returns the root node of data tree
def get_data_root(self) -> InstanceNode:
return self._data
def get_data_root(self, previous_version: int=0) -> InstanceNode:
if previous_version > 0:
return self._data_history[-previous_version]
else:
return self._data
def get_yl_data_root(self) -> InstanceNode:
return self._yang_lib_data
......@@ -238,10 +242,17 @@ class BaseDatastore:
else:
raise NoHandlerError("No active changelist for user \"{}\"".format(username))
# Set a new Instance node as data root
# Set a new Instance node as data root, store old root to archive
def set_data_root(self, new_root: InstanceNode):
self._data_history.append(self._data)
self._data = new_root
def data_root_rollback(self, history_steps: int, store_current: bool):
if store_current:
self._data_history.append(self._data)
self._data = self._data_history[-history_steps]
# Get schema node with particular schema address
def get_schema_node(self, sch_pth: str) -> SchemaNode:
sn = self._dm.get_schema_node(sch_pth)
......@@ -259,10 +270,10 @@ class BaseDatastore:
return ii
# Notify data observers about change in datastore
def notify_edit(self, ii: InstanceRoute, ch: DataChange):
def run_conf_edit_handler(self, ii: InstanceRoute, ch: DataChange) -> Optional[ConfHandlerResult]:
h_res = None
try:
# n = self._data.goto(ii)
# sn = n.schema_node
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)
......@@ -285,6 +296,8 @@ class BaseDatastore:
except NonexistentInstance:
warn("Cannnot notify {}, parent container removed".format(ii))
return h_res
# Just get the node, do not evaluate NACM (needed for NACM)
def get_node(self, root: InstanceNode, ii: InstanceRoute) -> InstanceNode:
n = root.goto(ii)
......
from enum import Enum
from typing import List, Union, Dict, Any
from typing import List, Union, Dict, Any, Optional
from threading import Lock
from colorlog import info
......@@ -39,14 +39,18 @@ class KnotConfState(Enum):
class RRecordBase:
def __init__(self, owner_name: str, res_type: str, ttl: int=3600):
self.owner = owner_name
def __init__(self, owner: str, res_type: str, ttl: Optional[int]=None):
self.owner = owner
self.type = res_type
self.ttl = ttl
def rrdata_format(self) -> str:
raise NotImplementedError("Not implemented in base class")
@property
def ttl_str(self):
return str(self.ttl) if self.ttl is not None else None
class SOARecord(RRecordBase):
def __init__(self):
......@@ -65,9 +69,27 @@ class SOARecord(RRecordBase):
)
class NSRecord(RRecordBase):
def __init__(self, owner: str, ttl: Optional[int]=None):
super().__init__(owner, "NS", ttl)
self.nsdname = None # type: str
def rrdata_format(self) -> str:
return self.nsdname
class ARecord(RRecordBase):
def __init__(self, owner_name: str):
super().__init__(owner_name, "A")
def __init__(self, owner: str, ttl: Optional[int]=None):
super().__init__(owner, "A", ttl)
self.address = None # type: str
def rrdata_format(self) -> str:
return self.address
class AAAARecord(RRecordBase):
def __init__(self, owner: str, ttl: Optional[int]=None):
super().__init__(owner, "AAAA", ttl)
self.address = None # type: str
def rrdata_format(self) -> str:
......@@ -75,8 +97,8 @@ class ARecord(RRecordBase):
class MXRecord(RRecordBase):
def __init__(self, owner_name: str):
super().__init__(owner_name, "MX")
def __init__(self, owner: str, ttl: Optional[int]=None):
super().__init__(owner, "MX", ttl)
self.preference = None # type: str
self.exchange = None # type: str
......@@ -176,15 +198,7 @@ class KnotConfig(KnotCtl):
for data_item in data:
self.send_block("conf-set", section=section, identifier=identifier, item=item, zone=zone, data=data_item)
def set_zone_item(self, section=None, identifier=None, item=None, zone=None, owner=None, ttl=None, rtype=None, data=None):
if not self.connected:
raise KnotApiError("Knot socket is closed")
if data is not None:
self.send_block("zone-set", section=section, identifier=identifier, item=item, zone=zone, owner=owner, ttl=ttl, rtype=rtype, data=data)
else:
self.send_block("zone-unset", section=section, identifier=identifier, item=item, zone=zone, owner=owner, ttl=ttl, rtype=rtype, data=data)
# Returns a status data of all or one specific DNS zone
def zone_status(self, domain_name: str=None) -> JsonNodeT:
if not self.connected:
raise KnotApiError("Knot socket is closed")
......@@ -196,6 +210,7 @@ class KnotConfig(KnotCtl):
raise KnotInternalError(str(e))
return resp
# Adds a new DNS zone
def zone_new(self, domain_name: str) -> JsonNodeT:
if not self.connected:
raise KnotApiError("Knot socket is closed")
......@@ -207,6 +222,7 @@ class KnotConfig(KnotCtl):
raise KnotInternalError(str(e))
return resp
# Removes a DNS zone
def zone_remove(self, domain_name: str) -> JsonNodeT:
if not self.connected:
raise KnotApiError("Knot socket is closed")
......@@ -218,27 +234,32 @@ class KnotConfig(KnotCtl):
raise KnotInternalError(str(e))
return resp
# Adds a resource record to DNS zone
def zone_add_record(self, domain_name: str, rr: RRecordBase) -> JsonNodeT:
if not self.connected:
raise KnotApiError("Knot socket is closed")
try:
res_data = rr.rrdata_format()
self.set_zone_item(zone=domain_name, owner=rr.owner, ttl=str(rr.ttl), rtype=rr.type, data=res_data)
debug_knot("Inserting zone \"{}\" RR, type=\"{}\", owner=\"{}\", ttl=\"{}\", data=\"{}\"".format(
domain_name, rr.type, rr.owner, rr.ttl, res_data
self.send_block("zone-set", zone=domain_name, owner=rr.owner, ttl=rr.ttl_str, rtype=rr.type, data=res_data)
debug_knot("Inserting zone \"{}\" RR, type=\"{}\", owner=\"{}\", ttl={}, data=\"{}\"".format(
domain_name, rr.type, rr.owner, rr.ttl_str, res_data
))
resp = self.receive_block()
except Exception as e:
raise KnotInternalError(str(e))
return resp
def zone_del_record(self, domain_name: str, owner: str, rr_type: str) -> JsonNodeT:
# Removes a resource record from DNS zone
# If the zone contains two or more records with the same owner and type, selector parameter can specify
# which one to remove. Usually it is the same as record data.
def zone_del_record(self, domain_name: str, owner: str, rr_type: str, selector: str=None) -> JsonNodeT:
if not self.connected:
raise KnotApiError("Knot socket is closed")
try:
self.set_zone_item(zone=domain_name, owner=owner, rtype=rr_type, data=None)
self.send_block("zone-unset", zone=domain_name, owner=owner, rtype=rr_type, data=selector)
resp = self.receive_block()
except Exception as e:
raise KnotInternalError(str(e))
......
This diff is collapsed.
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