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 {