Commit 9aa3665a authored by Pavel Spirek's avatar Pavel Spirek

All dependencies to KnotDNS stripped, Jetconf is now distributed with example...

All dependencies to KnotDNS stripped, Jetconf is now distributed with example dummy 'Jukebox' datamodel and handlers
parent 278d01c1
-----BEGIN CERTIFICATE-----
MIICZjCCAU4CAREwDQYJKoZIhvcNAQEFBQAwSzELMAkGA1UEBhMCQ1oxEzARBgNV
MIIC3TCCAcUCARUwDQYJKoZIhvcNAQEFBQAwSzELMAkGA1UEBhMCQ1oxEzARBgNV
BAoMCkV4YW1wbGUgQ0ExEDAOBgNVBAsMB2V4Y2EuY3oxFTATBgNVBAMMDG1haWxA
ZXhjYS5jejAeFw0xNjA1MDkwODQxNThaFw0xNzA1MDkwODQxNThaMCsxCzAJBgNV
BAYTAkNaMRwwGgYJKoZIhvcNAQkBFg1sb2p6YUBtYWlsLmN6MIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQDZsaIBTzwjoHMGGiiKuxoGwUJCBsAIMX5BUEBCIoX7
A3Yuit3TKWSVuTZrikPEEOXGCfJw3odQAKlK7QU7u732iP0+VIRMxhMh8BGS7p76
QgDOGe+gBYRvuJBPgYdHqWu7kCuL/FrKUeu/Ui4Z/7QP6AbWwT2aSswlJwZWRYhs
vwIDAQABMA0GCSqGSIb3DQEBBQUAA4IBAQBxREnObDzbunZiChhUl0O4m5zRyDpU
51r4PmMNM11MbPB0/a8jNwyXQUrtG4ZRxPDEWxwPCg0mSG+kEq8lkYTOCg8jr0np
YXbFZj4IYiZEyXs25xrqmlMnt2GjP1MBCBPLI7YzItQF4SHd+8U8rKA4bAVrlmwv
ccqWffc+R7LzyAY9N0jTPmRp2u9DwoL9FrgOcfCnyZmbezeGFORXGIHbfIDNvBbU
I9KiQDSWz+b/Zp1n2h4OPR6cjU2M7GhZnPEO6w669EtSDah0VL2U9wjmq4HZUPzY
PUmXUunEsZ5aSaSevRHc2bnuUs5UQUKODqml3NxU3Nx5YtXJtHh+YsUz
ZXhjYS5jejAeFw0xNjExMzAxNTMxNDFaFw0xNzExMzAxNTMxNDFaMB4xHDAaBgkq
hkiG9w0BCQEWDWxvanphQG1haWwuY3owggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAw
ggEKAoIBAQDZ3oi4UzfrrDrgDKkBBpN5OBkpnMkIkFrlDtJgeRZQ2mI3T6Ob1AzP
XwWabHJdsV0Fq5HlMQuLDdEfKLKvHWyZ0TboECKdfsZ6ysNE+Q9iudH+w+bNMNU3
mYGgbSujSb6+ieNauSPd/ffugQHdZF6Zq7R/UJbenl5P9cIBVYemYT3ITxxMduqy
vtmfmhA0PThh63tHA6Y5WDmc8mVjStYkxPMbRZ9sW2AWwtVxqjgU8kf8i8Sdk4zB
orbhjD/6+imkaoPGsg1NuwAv8Ll57EZCFOeMWvD6HOgFfxvWZ6GSrMHlMFCrpwgq
n3NuVEl2Ttt5828epfj/ar0t48mtR113AgMBAAEwDQYJKoZIhvcNAQEFBQADggEB
AA5ubDj3eVokoMRahaUi6KpSF/eLkcG1PbMz5yGfwhYs6QmfElgTFZMakTjVAgHq
idDYOz4TTGB/mPi7YdCtK7r7u6rALzuzz+b/8x1vwsaY+QVUcYpcIvXOcEaQlgbi
yfT3e66mEQ+T7XRktU9P0ey2ITNPWnXhmzrPvPI3qImkF+paO+T0EzhG0/K52vRv
fBqS+bADat7TtSFmbxDKnqTC+Jjo2RlrvmkFt0MAL0p5+T78M/wRQ5JEM1LjrsR2
VenMUBMMi53LBP389Y9h6gwsfQWpZmnUq4yzQocWiwjIQw8QP6oxKBGjlbnA6EM8
NZdikT6UCwEQTtj+HBEBqGI=
-----END CERTIFICATE-----
-----BEGIN RSA PRIVATE KEY-----
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
-----END RSA PRIVATE KEY-----
This diff is collapsed.
......@@ -12,21 +12,11 @@ from yangson.enumerations import ContentType, ValidationScope
from yangson.exceptions import YangsonException
from yangson.schemanode import SchemaError, SemanticError
from . import usr_state_data_handlers
from . import usr_state_data_handlers, usr_conf_data_handlers, usr_op_handlers
from .usr_datastore import UserDatastore
from .rest_server import RestServer
from .config import CONFIG_GLOBAL, CONFIG_KNOT, load_config, print_config
from .data import JsonDatastore
from .config import CONFIG_GLOBAL, CONFIG_NACM, load_config, print_config
from .helpers import DataHelpers, ErrorHelpers
from .handler_list import OP_HANDLERS, STATE_DATA_HANDLES, CONF_DATA_HANDLES
from .knot_api import KNOT, KnotError, knot_connect, knot_disconnect
from .usr_op_handlers import OP_HANDLERS_IMPL
from .usr_conf_data_handlers import (
KnotConfServerListener,
KnotConfLogListener,
KnotConfZoneListener,
KnotConfControlListener,
KnotConfAclListener
)
def main():
......@@ -136,9 +126,6 @@ def main():
signal.signal(signal.SIGTERM, sig_exit_handler)
signal.signal(signal.SIGINT, sig_exit_handler)
# Initialize Knot control interface
KNOT.set_socket(CONFIG_KNOT["SOCKET"])
# Load data model
yang_lib_file = os.path.join(CONFIG_GLOBAL["YANG_LIB_DIR"], "yang-library-data.json")
datamodel = DataHelpers.load_data_model(
......@@ -147,7 +134,7 @@ def main():
)
# Datastore init
datastore = JsonDatastore(datamodel, CONFIG_GLOBAL["DATA_JSON_FILE"], "DNS data", with_nacm=True)
datastore = UserDatastore(datamodel, CONFIG_GLOBAL["DATA_JSON_FILE"], with_nacm=CONFIG_NACM["ENABLED"])
try:
datastore.load()
datastore.load_yl_data(yang_lib_file)
......@@ -156,16 +143,6 @@ def main():
error(ErrorHelpers.epretty(e))
sig_exit_handler(0, None)
# Get KnotDNS configuration
try:
KNOT.knot_connect()
knot_conf_json = KNOT.config_read()
KNOT.knot_disconnect()
new_root = datastore.get_data_root().put_member("dns-server:dns-server", knot_conf_json["dns-server:dns-server"], raw=True).top()
datastore.set_data_root(new_root)
except KnotError as e:
error("Cannot load KnotDNS configuration, reason: {}".format(ErrorHelpers.epretty(e)))
# Validate datastore on startup
try:
datastore.get_data_root().validate(ValidationScope.all, ContentType.config)
......@@ -174,26 +151,14 @@ def main():
error(ErrorHelpers.epretty(e))
sig_exit_handler(0, None)
# Set datastore commit callbacks
datastore.commit_begin_callback = knot_connect
datastore.commit_end_callback = knot_disconnect
# Register configuration data node listeners
CONF_DATA_HANDLES.register(KnotConfServerListener(datastore, "/dns-server:dns-server/server-options"))
CONF_DATA_HANDLES.register(KnotConfLogListener(datastore, "/dns-server:dns-server/knot-dns:log"))
CONF_DATA_HANDLES.register(KnotConfZoneListener(datastore, "/dns-server:dns-server/zones/zone"))
CONF_DATA_HANDLES.register(KnotConfControlListener(datastore, "/dns-server:dns-server/knot-dns:control-socket"))
CONF_DATA_HANDLES.register(KnotConfAclListener(datastore, "/dns-server:dns-server/access-control-list"))
# Register handlers for configuration data
usr_conf_data_handlers.register_conf_handlers(datastore)
# Register op handlers
OP_HANDLERS.register("dns-zone-rpcs:begin-transaction", OP_HANDLERS_IMPL.zone_begin_transaction)
OP_HANDLERS.register("dns-zone-rpcs:commit-transaction", OP_HANDLERS_IMPL.zone_commit_transaction)
OP_HANDLERS.register("dns-zone-rpcs:abort-transaction", OP_HANDLERS_IMPL.zone_abort_transaction)
OP_HANDLERS.register("dns-zone-rpcs:zone-set", OP_HANDLERS_IMPL.zone_set)
OP_HANDLERS.register("dns-zone-rpcs:zone-unset", OP_HANDLERS_IMPL.zone_unset)
# Register handlers for state data
usr_state_data_handlers.register_state_handlers(datastore)
# Create and register state data node listeners
usr_state_data_handlers.create_zone_state_handlers(STATE_DATA_HANDLES, datamodel)
# Register handlers for operations
usr_op_handlers.register_op_handlers()
# Create HTTP server
rest_srv = RestServer()
......@@ -205,4 +170,4 @@ def main():
if __name__ == "__main__":
main()
\ No newline at end of file
main()
......@@ -33,6 +33,7 @@ CONFIG_HTTP = {
}
CONFIG_NACM = {
"ENABLED": True,
"ALLOWED_USERS": []
}
......
......@@ -34,8 +34,15 @@ from yangson.instance import (
from .helpers import PathFormat, ErrorHelpers, LogHelpers, DataHelpers, JsonNodeT
from .config import CONFIG, CONFIG_NACM, CONFIG_HTTP
from .nacm import NacmConfig, Permission, Action, NacmForbiddenError
from .handler_list import OP_HANDLERS, STATE_DATA_HANDLES, CONF_DATA_HANDLES, ConfDataObjectHandler, ConfDataListHandler
from .usr_state_data_handlers import ContainerNodeHandlerBase, ListNodeHandlerBase
from .handler_list import (
OP_HANDLERS,
STATE_DATA_HANDLES,
CONF_DATA_HANDLES,
ConfDataObjectHandler,
ConfDataListHandler,
StateDataContainerHandler,
StateDataListHandler
)
from .errors import JetconfError
epretty = ErrorHelpers.epretty
......@@ -247,11 +254,11 @@ class UsrChangeJournal:
class BaseDatastore:
def __init__(self, dm: DataModel, name: str="", with_nacm: bool=False):
def __init__(self, dm: DataModel, with_nacm: bool=False):
def _blankfn(*args, **kwargs):
pass
self.name = name
self.name = ""
self.nacm = None # type: NacmConfig
self._data = None # type: InstanceNode
self._data_history = [] # type: List[InstanceNode]
......@@ -266,6 +273,10 @@ class BaseDatastore:
if with_nacm:
self.nacm = NacmConfig(self, self._dm)
# Returns DataModel object
def get_dm(self) -> DataModel:
return self._dm
# Returns the root node of data tree
def get_data_root(self, previous_version: int=0) -> InstanceNode:
if previous_version > 0:
......@@ -425,10 +436,10 @@ class BaseDatastore:
# Direct request for the state data
sdh = STATE_DATA_HANDLES.get_handler(state_root_sch_pth)
if sdh is not None:
if isinstance(sdh, ContainerNodeHandlerBase):
if isinstance(sdh, StateDataContainerHandler):
state_handler_val = sdh.generate_node(ii, rpc.username, staging)
state_root_n = sdh.schema_node.orphan_instance(state_handler_val)
elif isinstance(sdh, ListNodeHandlerBase):
elif isinstance(sdh, StateDataListHandler):
if (sn is sdh.schema_node) and isinstance(ii[-1], MemberName):
state_handler_val = sdh.generate_list(ii, rpc.username, staging)
state_root_n = sdh.schema_node.orphan_instance(state_handler_val)
......@@ -457,9 +468,9 @@ class BaseDatastore:
sdh = STATE_DATA_HANDLES.get_handler(state_root_sch_pth)
if sdh is not None:
try:
if isinstance(sdh, ContainerNodeHandlerBase):
if isinstance(sdh, StateDataContainerHandler):
state_handler_val = sdh.generate_node(ii_gen, rpc.username, staging)
elif isinstance(sdh, ListNodeHandlerBase):
elif isinstance(sdh, StateDataListHandler):
state_handler_val = sdh.generate_list(ii_gen, rpc.username, staging)
except Exception as e:
error("Error occured in state data generator (sn: {})".format(state_root_sch_pth))
......@@ -612,9 +623,6 @@ class BaseDatastore:
if existing_member is None:
existing_member = n.put_member(input_member_name, ArrayValue([]))
# Convert input data from List/Dict to ArrayValue/ObjectValue
new_value_item = member_sn.entry_from_raw(input_member_value)
# Get ListNode key names
list_node_keys = member_sn.keys # Key names in the form [(key, ns), ]
......@@ -622,16 +630,16 @@ class BaseDatastore:
# Optimization
if len(existing_member.value) > 0:
list_entry_first = existing_member[0] # type: ArrayEntry
new_member = list_entry_first.insert_before(new_value_item).up()
new_member = list_entry_first.insert_before(input_member_value, raw=True).up()
else:
new_member = existing_member.update([new_value_item])
new_member = existing_member.update([input_member_value], raw=True)
elif (insert == "last") or (insert is None):
# Optimization
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()
new_member = list_entry_last.insert_after(input_member_value, raw=True).up()
else:
new_member = existing_member.update([new_value_item])
new_member = existing_member.update([input_member_value], raw=True)
elif (insert == "before") and (point is not None):
point_keys_val = point.split(",") # List key values passed in the "point" query argument
if len(list_node_keys) != len(point_keys_val):
......@@ -643,7 +651,7 @@ class BaseDatastore:
entry_keys = dict(map(lambda i: (list_node_keys[i], point_keys_val[i]), range(len(list_node_keys))))
entry_sel = EntryKeys(entry_keys)
point_list_entry = entry_sel.goto_step(existing_member) # type: ArrayEntry
new_member = point_list_entry.insert_before(new_value_item).up()
new_member = point_list_entry.insert_before(input_member_value, raw=True).up()
elif (insert == "after") and (point is not None):
point_keys_val = point.split(",") # List key values passed in the "point" query argument
if len(list_node_keys) != len(point_keys_val):
......@@ -655,7 +663,7 @@ class BaseDatastore:
entry_keys = dict(map(lambda i: (list_node_keys[i], point_keys_val[i]), range(len(list_node_keys))))
entry_sel = EntryKeys(entry_keys)
point_list_entry = entry_sel.goto_step(existing_member) # type: ArrayEntry
new_member = point_list_entry.insert_after(new_value_item).up()
new_member = point_list_entry.insert_after(input_member_value, raw=True).up()
else:
raise ValueError("Invalid 'insert'/'point' query values")
elif isinstance(member_sn, LeafListNode):
......@@ -675,14 +683,11 @@ class BaseDatastore:
else:
raise ValueError("Invalid 'insert' query value")
else:
if existing_member is None:
# Create new data node
# Convert input data from List/Dict to ArrayValue/ObjectValue
new_value_item = member_sn.from_raw(input_member_value)
# Create new container member
if existing_member is None:
# Create new node (object member)
new_member = n.put_member(input_member_name, new_value_item)
new_member = n.put_member(input_member_name, input_member_value, raw=True)
else:
# Data node already exists
raise InstanceAlreadyPresent("Member \"{}\" already present in \"{}\"".format(input_member_name, ii))
......@@ -929,8 +934,8 @@ class BaseDatastore:
class JsonDatastore(BaseDatastore):
def __init__(self, dm: DataModel, json_file: str, name: str = "", with_nacm: bool=False):
super().__init__(dm, name, with_nacm)
def __init__(self, dm: DataModel, json_file: str, with_nacm: bool=False):
super().__init__(dm, with_nacm)
self.json_file = json_file
def load(self):
......
from yangson.instance import InstanceRoute, NonexistentInstance
# Base class for all exceptions defined in jetconf
class JetconfError(Exception):
def __init__(self, msg=""):
......@@ -5,3 +8,16 @@ class JetconfError(Exception):
def __str__(self):
return self.msg
class BackendError(JetconfError):
pass
class StateNonexistentInstance(NonexistentInstance):
def __init__(self, ii: InstanceRoute, text: str) -> None:
self.ii = ii
self.text = text
def __str__(self):
return str(self.ii) + ": " + self.text
from typing import List, Tuple, Callable, Any
from yangson.schemanode import SchemaNode
from yangson.schemadata import SchemaData
from yangson.instance import InstanceRoute
from .helpers import JsonNodeT
HandlerSelectorT = Any
# ---------- Conf data base objects ----------
class ConfDataObjectHandler:
def __init__(self, ds: "BaseDatastore", sch_pth: str):
self.ds = ds
......@@ -53,6 +59,31 @@ class ConfDataListHandler:
return self.__class__.__name__ + ": listening at " + self.schema_path
# ---------- State data base objects ----------
class StateDataHandlerBase:
def __init__(self, datastore: "BaseDatastore", schema_path: str):
self.ds = datastore
self.data_model = datastore.get_dm()
self.sch_pth = schema_path
self.schema_node = self.data_model.get_data_node(self.sch_pth)
class StateDataContainerHandler(StateDataHandlerBase):
def generate_node(self, node_ii: InstanceRoute, username: str, staging: bool) -> JsonNodeT:
pass
class StateDataListHandler(StateDataHandlerBase):
def generate_list(self, node_ii: InstanceRoute, username: str, staging: bool) -> JsonNodeT:
pass
def generate_item(self, node_ii: InstanceRoute, username: str, staging: bool) -> JsonNodeT:
pass
# ---------- Handles lists ----------
class BaseHandlerList:
def __init__(self):
self.handlers = [] # type: List[Tuple[HandlerSelectorT, Callable]]
......@@ -69,7 +100,7 @@ class BaseHandlerList:
class OpHandlerList(BaseHandlerList):
def register(self, op_name: str, handler: Callable):
def register(self, handler: Callable, op_name: str):
self.handlers.append((op_name, handler))
def get_handler(self, op_name: str) -> Callable:
......@@ -121,6 +152,8 @@ class StateDataHandlerList:
return None
# ---------- Handler list globals ----------
OP_HANDLERS = OpHandlerList()
STATE_DATA_HANDLES = StateDataHandlerList()
CONF_DATA_HANDLES = ConfDataHandlerList()
......@@ -14,9 +14,9 @@ from yangson.schemanode import ContainerNode, ListNode, GroupNode, LeafListNode,
from yangson.instance import NonexistentInstance, InstanceValueError, RootNode
from yangson.datatype import YangTypeError
from .knot_api import KnotError
from .config import CONFIG_GLOBAL, CONFIG_HTTP, CONFIG_NACM, API_ROOT_data, API_ROOT_STAGING_data, API_ROOT_ops
from .helpers import CertHelpers, DateTimeHelpers, ErrorHelpers, LogHelpers, SSLCertT
from .errors import BackendError
from .nacm import NacmForbiddenError
from .data import (
BaseDatastore,
......@@ -231,7 +231,7 @@ def _get(ds: BaseDatastore, req_headers: OrderedDict, pth: str, username: str, s
ERRTAG_INVVALUE,
exception=e
)
except (ConfHandlerFailedError, NoHandlerError, KnotError, YangsonException) as e:
except (ConfHandlerFailedError, NoHandlerError, YangsonException, BackendError) as e:
http_resp = HttpResponse.error(
HttpStatus.InternalServerError,
RestconfErrType.Protocol,
......@@ -671,18 +671,18 @@ def create_api_op(ds: BaseDatastore):
ERRTAG_OPNOTSUPPORTED,
exception=e
)
except (ConfHandlerFailedError, OpHandlerFailedError) as e:
except (SchemaError, SemanticError) as e:
http_resp = HttpResponse.error(
HttpStatus.InternalServerError,
HttpStatus.BadRequest,
RestconfErrType.Protocol,
ERRTAG_OPFAILED,
ERRTAG_INVVALUE,
exception=e
)
except (SchemaError, SemanticError) as e:
except (ConfHandlerFailedError, OpHandlerFailedError, YangsonException) as e:
http_resp = HttpResponse.error(
HttpStatus.BadRequest,
HttpStatus.InternalServerError,
RestconfErrType.Protocol,
ERRTAG_INVVALUE,
ERRTAG_OPFAILED,
exception=e
)
except ValueError as e:
......
This diff is collapsed.
"""Python libknot interface."""
from .version import get_knot_version
knot_ver = get_knot_version()
if knot_ver < (2, 4):
raise ValueError(
"Installed version of KnotDNS is too old (found: {}.{}). libknot-python only supports KnotDNS version 2.4 and higher.".format(
knot_ver[0],
knot_ver[1]
)
)
This diff is collapsed.
import os
import re
from typing import Tuple
def get_knot_version() -> Tuple[int, int]:
fp = os.popen("knotd --version 2>/dev/null")
knotd_resp = fp.read()
ret_close = fp.close()
if ret_close is not None:
raise ValueError("Cannot determine KnotDNS version. Is 'knotd --version' command functional?")
# 'knotd (Knot DNS), version 2.4.0-dev\n'
try:
ver_major_minor = re.findall("version (\d+)\.(\d+)", knotd_resp)[0]
except IndexError:
raise ValueError("Cannot determine KnotDNS version. Unknown data received from 'knotd --version'")
ver_major_minor_int = tuple(map(lambda n: int(n), ver_major_minor))
return ver_major_minor_int
This diff is collapsed.
from yangson.datamodel import DataModel
from .data import JsonDatastore
class UserDatastore(JsonDatastore):
def __init__(self, dm: DataModel, json_file: str, with_nacm: bool = False):
super().__init__(dm, json_file, with_nacm)
self.name = "Example Data"
# Application-specific init actions can be defined here
from enum import Enum
from typing import Dict, List
from colorlog import error, warning as warn, info
from . import knot_api
from .helpers import JsonNodeT
from .knot_api import SOARecord, ARecord, AAAARecord, MXRecord
from .handler_list import OP_HANDLERS
class KnotZoneCmd(Enum):
......@@ -20,85 +18,18 @@ class KnotOp:
class OpHandlersContainer:
def __init__(self):
self.op_journal = {} # type: Dict[str, List[KnotOp]]
pass
def zone_begin_transaction(self, input_args: JsonNodeT, username: str) -> JsonNodeT:
self.op_journal[username] = []
def jukebox_play_op(self, input_args: JsonNodeT, username: str) -> JsonNodeT:
# Structure of RPC's input and output arguments is defined in YANG data model
# Do something
info("Called operation 'jukebox_play_op' by user '{}':".format(username))
info("Playlist name: {}".format(input_args["example-jukebox:playlist"]))
info("Song number: {}".format(input_args["example-jukebox:song-number"]))
def zone_commit_transaction(self, input_args: JsonNodeT, username: str) -> JsonNodeT:
try:
usr_op_journal = self.op_journal[username]
except KeyError:
warn("zone_commit_transaction: Nothing to commit")
return
# Connect to Knot socket and start zone transaction
knot_api.KNOT.knot_connect()
knot_api.KNOT.begin_zone()
for knot_op in usr_op_journal:
input_args = knot_op.op_input
domain = input_args["dns-zone-rpcs:zone"]
if knot_op.cmd == KnotZoneCmd.SET:
rr_type = input_args["dns-zone-rpcs:type"][0]
if rr_type == "SOA":
rrdata = input_args["dns-zone-rpcs:SOA"]
rr = SOARecord()
rr.ttl = input_args["dns-zone-rpcs:ttl"]
rr.mname = rrdata["mname"]
rr.rname = rrdata["rname"]
rr.serial = rrdata["serial"]
rr.refresh = rrdata["refresh"]
rr.retry = rrdata["retry"]
rr.expire = rrdata["expire"]
rr.minimum = rrdata["minimum"]
knot_api.KNOT.zone_add_record(domain, rr)
elif rr_type == "A":
rrdata = input_args["dns-zone-rpcs:A"]
rr = ARecord(input_args["dns-zone-rpcs:owner"], input_args["dns-zone-rpcs:ttl"])
rr.address = rrdata["address"]
knot_api.KNOT.zone_add_record(domain, rr)
elif rr_type == "AAAA":
rrdata = input_args["dns-zone-rpcs:AAAA"]
rr = AAAARecord(input_args["dns-zone-rpcs:owner"], input_args["dns-zone-rpcs:ttl"])
rr.address = rrdata["address"]
knot_api.KNOT.zone_add_record(domain, rr)
elif rr_type == "MX":
rrdata = input_args["dns-zone-rpcs:MX"]
rr = MXRecord(input_args["dns-zone-rpcs:owner"], input_args["dns-zone-rpcs:ttl"])
rr.address = rrdata["address"]
knot_api.KNOT.zone_add_record(domain, rr)
elif knot_op.cmd == KnotZoneCmd.UNSET:
pass
knot_api.KNOT.commit()
knot_api.KNOT.knot_disconnect()
del self.op_journal[username]
def zone_abort_transaction(self, input_args: JsonNodeT, username: str) -> JsonNodeT:
try:
del self.op_journal[username]
except KeyError:
warn("zone_abort_transaction: Nothing to abort")
def zone_set(self, input_args: JsonNodeT, username: str) -> JsonNodeT:
try:
usr_op_journal = self.op_journal[username]
except KeyError:
warn("zone_set: Op transaction not started")
return
usr_op_journal.append(KnotOp(KnotZoneCmd.SET, input_args))
def zone_unset(self, input_args: JsonNodeT, username: str) -> JsonNodeT:
try:
usr_op_journal = self.op_journal[username]
except KeyError:
warn("zone_set: Op transaction not started")
return
usr_op_journal.append(KnotOp(KnotZoneCmd.UNSET, input_args))
OP_HANDLERS_IMPL = OpHandlersContainer()
OP_HANDLERS_IMPL = OpHandlersContainer()
def register_op_handlers():
OP_HANDLERS.register(OP_HANDLERS_IMPL.jukebox_play_op, "example-jukebox:play")
This diff is collapsed.
#!/bin/bash
CLIENT_CERT="/home/pspirek/sslclient/pavel_curl.pem"
echo "--- conf-start 1"
POST_DATA='{ "dns-server:input": {"name": "Edit 1"} }'
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
echo "--- DEL newzone.cz"
URL="https://127.0.0.1:8443/restconf/data/dns-server:dns-server/zones/zone=newzone.cz"
curl -v --http2 -k --cert-type PEM -E $CLIENT_CERT -X DELETE "$URL" 2>/dev/null
echo "--- conf-commit"
URL="https://127.0.0.1:8443/restconf/operations/dns-server:conf-commit"
curl -v --http2 -k --cert-type PEM -E $CLIENT_CERT -X POST "$URL" 2>/dev/null
from yangson import DataModel
module_dir = "/Users/lhotka/Projects/jetconf/yang"
with open("/Users/lhotka/Projects/jetconf/data/yang-library-data.json",
encoding="utf-8") as infile:
txt = infile.read()
dm = DataModel.from_yang_library(txt, module_dir)
nacm = dm.get_schema_node("/ietf-netconf-acm:nacm")
print(nacm.default_deny)
rpcname = dm.get_data_node("/ietf-netconf-acm:nacm/rule-list/rule/rpc-name")
print(rpcname)
module dns-parameters {
yang-version "1.1";
namespace "http://www.nic.cz/ns/yang/dns-parameters";
prefix "dnspars";
organization
"CZ.NIC, z. s. p. o.";
contact
"Editor:   Ladislav Lhotka
          <mailto:lhotka@nic.cz>";
description
"This module translates IANA registry of Domain Name System (DNS)
parameters to YANG enumeration types.
TODO: Complete the enumerations.";
reference
"IANA: Domain Name System (DNS) Parameters.
https://www.iana.org/assignments/dns-parameters/dns-parameters.xml";
revision 2016-01-13 {
description
"Initial revision.";
reference
"TODO: put git tag here";
}
typedef dns-class {
type enumeration {
enum IN {
value "1";
description
"Internet";
reference
"RFC 1035: Domain Names - Implementation and
Specification.";
}
enum CH {
value "3";
description
"Chaos";
reference
"D. Moon, \"Chaosnet\", A.I. Memo 628, Massachusetts
Institute of Technology Artificial Intelligence
Laboratory, June 1981.";
}
enum HS {
value "4";
description
"Hesiod";
reference
"Dyer, S., and F. Hsu, \"Hesiod\", Project Athena Technical
Plan – Name Service, April 1987.";
}
enum NONE {
value "254";
description
"QCLASS NONE";
reference
"RFC 2136: Dynamic Updates in the Domain Name System (DNS
UPDATE).";
}
enum ANY {
value "255";
description
"QCLASS *";
reference
"RFC 1035: Domain Names - Implementation and
Specification.";