#include <confd_lib.h> #include <confd_dp.h>
struct confd_daemon_ctx *confd_init_daemon( | const char *name) ; |
int confd_set_daemon_flags( | struct confd_daemon_ctx *dx, |
int flags) ; |
void confd_release_daemon( | struct confd_daemon_ctx *dx) ; |
int confd_connect( | struct confd_daemon_ctx *dx, |
int sock, | |
enum confd_sock_type type, | |
const struct sockaddr *srv, | |
int addrsz) ; |
int confd_register_trans_cb( | struct confd_daemon_ctx *dx, |
const struct confd_trans_cbs *trans) ; |
int confd_register_db_cb( | struct confd_daemon_ctx *dx, |
const struct confd_db_cbs *dbcbs) ; |
int confd_register_range_data_cb( | struct confd_daemon_ctx *dx, |
const struct confd_data_cbs *data, | |
const confd_value_t *lower, | |
const confd_value_t *upper, | |
int numkeys, | |
const char *fmt, | |
...) ; |
int confd_register_data_cb( | struct confd_daemon_ctx *dx, |
const struct confd_data_cbs *data) ; |
int confd_register_usess_cb( | struct confd_daemon_ctx *dx, |
const struct confd_usess_cbs *ucb) ; |
int ncs_register_service_cb( | struct confd_daemon_ctx *dx, |
const struct ncs_service_cbs *scb) ; |
int ncs_register_nano_service_cb( | struct confd_daemon_ctx *dx, |
const char *component_type, | |
const char *state, | |
const struct ncs_nano_service_cbs *scb) ; |
int confd_register_done( | struct confd_daemon_ctx *dx) ; |
int confd_fd_ready( | struct confd_daemon_ctx *dx, |
int fd) ; |
void confd_trans_set_fd( | struct confd_trans_ctx *tctx, |
int sock) ; |
int confd_data_reply_value( | struct confd_trans_ctx *tctx, |
const confd_value_t *v) ; |
int confd_data_reply_value_array( | struct confd_trans_ctx *tctx, |
const confd_value_t *vs, | |
int n) ; |
int confd_data_reply_tag_value_array( | struct confd_trans_ctx *tctx, |
const confd_tag_value_t *tvs, | |
int n) ; |
int confd_data_reply_next_key( | struct confd_trans_ctx *tctx, |
const confd_value_t *v, | |
int num_vals_in_key, | |
long next) ; |
int confd_data_reply_not_found( | struct confd_trans_ctx *tctx) ; |
int confd_data_reply_found( | struct confd_trans_ctx *tctx) ; |
int confd_data_reply_next_object_array( | struct confd_trans_ctx *tctx, |
const confd_value_t *v, | |
int n, | |
long next) ; |
int confd_data_reply_next_object_tag_value_array( | struct confd_trans_ctx *tctx, |
const confd_tag_value_t *tv, | |
int n, | |
long next) ; |
int confd_data_reply_next_object_arrays( | struct confd_trans_ctx *tctx, |
const struct confd_next_object *obj, | |
int nobj, | |
int timeout_millisecs) ; |
int confd_data_reply_next_object_tag_value_arrays( | struct confd_trans_ctx *tctx, |
const struct confd_tag_next_object *tobj, | |
int nobj, | |
int timeout_millisecs) ; |
int confd_data_reply_attrs( | struct confd_trans_ctx *tctx, |
const confd_attr_value_t *attrs, | |
int num_attrs) ; |
int ncs_service_reply_proplist( | struct confd_trans_ctx *tctx, |
const struct ncs_name_value *proplist, | |
int num_props) ; |
int ncs_nano_service_reply_proplist( | struct confd_trans_ctx *tctx, |
const struct ncs_name_value *proplist, | |
int num_props) ; |
int confd_delayed_reply_ok( | struct confd_trans_ctx *tctx) ; |
int confd_delayed_reply_error( | struct confd_trans_ctx *tctx, |
const char *errstr) ; |
int confd_data_set_timeout( | struct confd_trans_ctx *tctx, |
int timeout_secs) ; |
void confd_trans_seterr( | struct confd_trans_ctx *tctx, |
const char *fmt, | |
...) ; |
void confd_trans_seterr_extended( | struct confd_trans_ctx *tctx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
const char *fmt, | |
...) ; |
int confd_trans_seterr_extended_info( | struct confd_trans_ctx *tctx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
confd_tag_value_t *error_info, | |
int n, | |
const char *fmt, | |
...) ; |
void confd_db_seterr( | struct confd_db_ctx *dbx, |
const char *fmt, | |
...) ; |
void confd_db_seterr_extended( | struct confd_db_ctx *dbx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
const char *fmt, | |
...) ; |
int confd_db_seterr_extended_info( | struct confd_db_ctx *dbx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
confd_tag_value_t *error_info, | |
int n, | |
const char *fmt, | |
...) ; |
int confd_db_set_timeout( | struct confd_db_ctx *dbx, |
int timeout_secs) ; |
int confd_aaa_reload( | const struct confd_trans_ctx *tctx) ; |
int confd_install_crypto_keys( | struct confd_daemon_ctx* dtx) ; |
void confd_register_trans_validate_cb( | struct confd_daemon_ctx *dx, |
const struct confd_trans_validate_cbs *vcbs) ; |
int confd_register_valpoint_cb( | struct confd_daemon_ctx *dx, |
const struct confd_valpoint_cb *vcb) ; |
int confd_register_range_valpoint_cb( | struct confd_daemon_ctx *dx, |
struct confd_valpoint_cb *vcb, | |
const confd_value_t *lower, | |
const confd_value_t *upper, | |
int numkeys, | |
const char *fmt, | |
...) ; |
int confd_delayed_reply_validation_warn( | struct confd_trans_ctx *tctx) ; |
int confd_register_action_cbs( | struct confd_daemon_ctx *dx, |
const struct confd_action_cbs *acb) ; |
int confd_register_range_action_cbs( | struct confd_daemon_ctx *dx, |
const struct confd_action_cbs *acb, | |
const confd_value_t *lower, | |
const confd_value_t *upper, | |
int numkeys, | |
const char *fmt, | |
...) ; |
void confd_action_set_fd( | struct confd_user_info *uinfo, |
int sock) ; |
void confd_action_seterr( | struct confd_user_info *uinfo, |
const char *fmt, | |
...) ; |
void confd_action_seterr_extended( | struct confd_user_info *uinfo, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
const char *fmt, | |
...) ; |
int confd_action_seterr_extended_info( | struct confd_user_info *uinfo, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
confd_tag_value_t *error_info, | |
int n, | |
const char *fmt, | |
...) ; |
int confd_action_reply_values( | struct confd_user_info *uinfo, |
confd_tag_value_t *values, | |
int nvalues) ; |
int confd_action_reply_command( | struct confd_user_info *uinfo, |
char **values, | |
int nvalues) ; |
int confd_action_reply_rewrite( | struct confd_user_info *uinfo, |
char **values, | |
int nvalues, | |
char **unhides, | |
int nunhides) ; |
int confd_action_reply_rewrite2( | struct confd_user_info *uinfo, |
char **values, | |
int nvalues, | |
char **unhides, | |
int nunhides, | |
struct confd_rewrite_select **selects, | |
int nselects) ; |
int confd_action_reply_completion( | struct confd_user_info *uinfo, |
struct confd_completion_value *values, | |
int nvalues) ; |
int confd_action_reply_range_enum( | struct confd_user_info *uinfo, |
char **values, | |
int keysize, | |
int nkeys) ; |
int confd_action_delayed_reply_ok( | struct confd_user_info *uinfo) ; |
int confd_action_delayed_reply_error( | struct confd_user_info *uinfo, |
const char *errstr) ; |
int confd_action_set_timeout( | struct confd_user_info *uinfo, |
int timeout_secs) ; |
int confd_register_notification_stream( | struct confd_daemon_ctx *dx, |
const struct confd_notification_stream_cbs *ncbs, | |
struct confd_notification_ctx **nctx) ; |
int confd_notification_send( | struct confd_notification_ctx *nctx, |
struct confd_datetime *time, | |
confd_tag_value_t *values, | |
int nvalues) ; |
int confd_notification_replay_complete( | struct confd_notification_ctx *nctx) ; |
int confd_notification_replay_failed( | struct confd_notification_ctx *nctx) ; |
int confd_notification_reply_log_times( | struct confd_notification_ctx *nctx, |
struct confd_datetime *creation, | |
struct confd_datetime *aged) ; |
void confd_notification_set_fd( | struct confd_notification_ctx *nctx, |
int fd) ; |
void confd_notification_set_snmp_src_addr( | struct confd_notification_ctx *nctx, |
const struct confd_ip *src_addr) ; |
int confd_notification_set_snmp_notify_name( | struct confd_notification_ctx *nctx, |
const char *notify_name) ; |
void confd_notification_seterr( | struct confd_notification_ctx *nctx, |
const char *fmt, | |
...) ; |
void confd_notification_seterr_extended( | struct confd_notification_ctx *nctx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
const char *fmt, | |
...) ; |
int confd_notification_seterr_extended_info( | struct confd_notification_ctx *nctx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
confd_tag_value_t *error_info, | |
int n, | |
const char *fmt, | |
...) ; |
int confd_register_snmp_notification( | struct confd_daemon_ctx *dx, |
int fd, | |
const char *notify_name, | |
const char *ctx_name, | |
struct confd_notification_ctx **nctx) ; |
int confd_notification_send_snmp( | struct confd_notification_ctx *nctx, |
const char *notification, | |
struct confd_snmp_varbind *varbinds, | |
int num_vars) ; |
int confd_register_notification_snmp_inform_cb( | struct confd_daemon_ctx *dx, |
const struct confd_notification_snmp_inform_cbs *cb) ; |
int confd_notification_send_snmp_inform( | struct confd_notification_ctx *nctx, |
const char *notification, | |
struct confd_snmp_varbind *varbinds, | |
int num_vars, | |
const char *cb_id, | |
int ref) ; |
int confd_register_notification_sub_snmp_cb( | struct confd_daemon_ctx *dx, |
const struct confd_notification_sub_snmp_cb *cb) ; |
int confd_notification_flush( | struct confd_notification_ctx *nctx) ; |
int confd_register_auth_cb( | struct confd_daemon_ctx *dx, |
const struct confd_auth_cb *acb) ; |
void confd_auth_seterr( | struct confd_auth_ctx *actx, |
const char *fmt, | |
...) ; |
int confd_register_authorization_cb( | struct confd_daemon_ctx *dx, |
const struct confd_authorization_cbs *acb) ; |
int confd_access_reply_result( | struct confd_authorization_ctx *actx, |
int result) ; |
int confd_authorization_set_timeout( | struct confd_authorization_ctx *actx, |
int timeout_secs) ; |
int confd_register_error_cb( | struct confd_daemon_ctx *dx, |
const struct confd_error_cb *ecb) ; |
void confd_error_seterr( | struct confd_user_info *uinfo, |
const char *fmt, | |
...) ; |
The libconfd
shared library is used to
connect to the ConfD Data Provider API. The purpose of this API is
to provide callback hooks so that user-written data providers can
provide data stored externally to ConfD. ConfD needs this
information in order to drive its northbound agents.
The library is also used to populate items in the data model which are not data or configuration items, such as statistics items from the device.
The library consists of a number of API functions whose purpose is to install different callback functions at different points in the data model tree which is the representation of the device configuration. Read more about callpoints in tailf_yang_extensions(5). Read more about how to use the library in the User Guide chapters on Operational data and External data.
Initializes a new daemon context or returns NULL on failure. For most of the library functions described here a daemon_ctx is required, so we must create a daemon context before we can use them. The daemon context contains a d_opaque pointer which can be used by the application to pass application specific data into the callback functions.
The name
parameter is used in
various debug printouts and and is also used to uniquely
identify the daemon. The confd --status
will use this name when indicating which callpoints are
registered.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_PROTOUSAGE
This function modifies the API behaviour according to the
flags ORed into the flags
argument. It should
be called immediately after creating the daemon context with
confd_init_daemon()
. The following flags are
available:
CONFD_DAEMON_FLAG_STRINGSONLY
If this flag is used, the callback functions described below will only receive string values for all instances of confd_value_t (i.e. the type is always C_BUF). The callbacks must also give only string values in their reply functions. This feature can be useful for proxy-type applications that are unaware of the types of all elements, i.e. data model agnostic.
CONFD_DAEMON_FLAG_REG_REPLACE_DISCONNECT
By default, if one daemon replaces a callpoint
registration made by another daemon, this is only logged, and
no action is taken towards the daemon that has "lost" its
registration. This can be useful in some scenarios, e.g. it is
possible to have an "initial default" daemon providing "null"
data for many callpoints, until the actual data provider
daemons have registered. If a daemon uses the
CONFD_DAEMON_FLAG_REG_REPLACE_DISCONNECT
flag, it will instead be disconnected from ConfD if any of its
registrations are replaced by another daemon, and can take
action as appropriate.
CONFD_DAEMON_FLAG_NO_DEFAULTS
This flag tells ConfD that the daemon does not store
default values. By default, ConfD assumes that the daemon
doesn't know about default values, and thus whenever default
values come into effect, ConfD will issue
set_elem()
callbacks to set those values,
even if they have not actually been set by the northbound
agent. Similarly set_case()
will be
issued with the default case for choices that have one.
When the
CONFD_DAEMON_FLAG_NO_DEFAULTS
flag is
set, ConfD will only issue set_elem()
callbacks when values have been explicitly set, and
set_case()
when a case has been selected
by explicitly setting an element in the
case. Specifically:
When a list entry or presence container is created, there will be no callbacks for descendant leafs with default value, or descendant choices with default case, unless values have been explicitly set.
When a leaf with a default value is deleted,
a remove()
callback will be issued
instead of a set_elem()
with the
default value.
When the current case in a choice with
default case is deleted without another case being
selected, the set_case()
callback will be invoked with the case value given as NULL
instead of the default case.
A daemon that has the
CONFD_DAEMON_FLAG_NO_DEFAULTS
flag set
must reply to get_elem()
and the other callbacks that request leaf values with a value of
type C_DEFAULT, rather than the actual default value, when the
default value for a leaf is in effect. It must
also reply to get_case()
with C_DEFAULT when
the default case is in effect.
Returns all memory that has been allocated by
confd_init_daemon()
and other functions for
the daemon context. The control socket as well as all the worker
sockets must be closed by the application (before or after
confd_release_daemon()
has been called).
int confd_connect( | struct confd_daemon_ctx *dx, |
int sock, | |
enum confd_sock_type type, | |
const struct sockaddr *srv, | |
int addrsz) ; |
Connects to the ConfD daemon. The dx
parameter is a daemon context acquired through a call to
confd_init_daemon()
.
There are two different types of connected sockets between an external daemon and ConfD.
CONTROL_SOCKET
The first socket that is connected must always be a
control socket. All requests from ConfD to create new
transactions will arrive on the control socket, but it is also
used for a number of other requests that are expected to
complete quickly - the general rule is that all callbacks that
do not have a corresponding init()
callback are in fact control socket requests. There can only
be one control socket for a given daemon context.
WORKER_SOCKET
We must always create at least one worker socket. All
transaction, data, validation, and action callbacks, except
the init()
callbacks, use a worker socket.
It is possible for a daemon to have multiple worker sockets,
and the init()
callback (see e.g.
confd_register_trans_cb()
) must
indicate which worker socket should be used for the subsequent
requests. This makes it possible for an application
to be multi-threaded, where different threads can be used for
different transactions.
Returns CONFD_OK when successful or CONFD_ERR on connection error.
All the callbacks that are invoked via these sockets are
subject to timeouts configured in confd.conf
,
see confd.conf(5). The
callbacks invoked via the control socket must generate a reply back
to ConfD within the time configured for
/confdConfig/capi/newSessionTimeout
, the callbacks
invoked via a worker socket within the time configured for
/confdConfig/capi/queryTimeout
. If either timeout is
exceeded, the daemon will be considered dead, and ConfD will
disconnect it by closing the control and worker
sockets.
If this call fails (i.e. does not return CONFD_OK), the socket descriptor must be closed and a new socket created before the call is re-attempted.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_PROTOUSAGE
This function registers transaction callback functions. A transaction is a ConfD concept. There may be multiple sources of data for the device configuration.
In order to orchestrate transactions with multiple sources of data, ConfD implements a two-phase commit protocol towards all data sources that participate in a transaction.
Each NETCONF operation will be an individual ConfD transaction. These transactions are typically very short lived. Transactions originating from the CLI or the Web UI have longer life. The ConfD transaction can be viewed as a conceptual state machine where the different phases of the transaction are different states and the invocations of the callback functions are state transitions. The following ASCII art depicts the state machine.
+-------+ | START | +-------+ | init() | v read() +------+ finish() ------> | READ | --------------------> START +------+ ^ | trans_unlock() | | trans_lock() | v read() +----------+ finish() ------> | VALIDATE | -----------------> START +----------+ | write_start() | v write() +-------+ finish() -------> | WRITE | -------------------> START +-------+ | prepare() | v +----------+ commit() +-----------+ | PREPARED | -----------> | COMMITTED | +----------+ +-----------+ | abort() | | | finish() v | +---------+ v | ABORTED | START +---------+ | finish() | v START
The struct confd_trans_cbs is defined as:
struct confd_trans_cbs { int (*init)(struct confd_trans_ctx *tctx); int (*trans_lock)(struct confd_trans_ctx *sctx); int (*trans_unlock)(struct confd_trans_ctx *sctx); int (*write_start)(struct confd_trans_ctx *sctx); int (*prepare)(struct confd_trans_ctx *tctx); int (*abort)(struct confd_trans_ctx *tctx); int (*commit)(struct confd_trans_ctx *tctx); int (*finish)(struct confd_trans_ctx *tctx); void (*interrupt)(struct confd_trans_ctx *tctx); };
Transactions can be performed towards fours different kind of storages.
CONFD_CANDIDATE
If the system has been configured so that the external database owns the candidate data share, we will have to execute candidate transactions here. Usually ConfD owns the candidate and in that case the external database will never see any CONFD_CANDIDATE transactions.
CONFD_RUNNING
This is a transaction towards the actual running configuration of the device. All write operations in a CONFD_RUNNING transaction must be propagated to the individual subsystems that use this configuration data.
CONFD_STARTUP
If the system has ben configured to support the NETCONF startup capability, this is a transaction towards the startup database.
CONFD_OPERATIONAL
This value indicates a transaction towards writable
operational data. This transaction is used only if there
are non-config data marked as tailf:writable
true
in the YANG module.
Currently, these transaction are only started by the SNMP agent, and only when writable operational data is SET over SNMP.
Which type we have is indicated through the
confd_dbname
field in the
confd_trans_ctx.
A transaction, regardless of whether it originates from the NETCONF agent, the CLI or the Web UI, has several distinct phases:
init()
This callback must always be implemented. All other
callbacks are optional. This means that if the callback is set
to NULL, ConfD will treat it as an implicit CONFD_OK.
libconfd
will allocate a transaction
context on behalf of the transaction and give this newly
allocated structure as an argument to the
init()
callback. The structure is defined
as:
struct confd_user_info { int af; /* AF_INET | AF_INET6 */ union { struct in_addr v4; /* address from where the */ struct in6_addr v6; /* user session originates */ } ip; u_int16_t port; /* source port */ char username[MAXUSERNAMELEN]; /* who is the user */ int usid; /* user session id */ char context[MAXCTXLEN]; /* cli | webui | netconf | */ /* noaaa | any MAAPI string */ enum confd_proto proto; /* which protocol */ struct confd_action_ctx actx; /* used during action call */ time_t logintime; enum confd_usess_lock_mode lmode; /* the lock we have (only from */ /* maapi_get_user_session()) */ char snmp_v3_ctx[255]; /* SNMP context for SNMP sessions */ /* empty string ("") for non-SNMP sessions */ char clearpass[255]; /* if have the pass, it's here */ /* only if confd internal ssh is used */ int flags; /* CONFD_USESS_FLAG_... */ void *u_opaque; /* Private User data */ /* ConfD internal fields */ char *errstr; /* for error formatting callback */ int refc; };
struct confd_trans_ctx { int fd; /* trans (worker) socket */ int vfd; /* validation worker socket */ struct confd_daemon_ctx *dx; /* our daemon ctx */ enum confd_trans_mode mode; enum confd_dbname dbname; struct confd_user_info *uinfo; void *t_opaque; /* Private User data (transaction) */ void *v_opaque; /* Private User data (validation) */ struct confd_error error; /* user settable via */ /* confd_trans_seterr*() */ struct confd_tr_item *accumulated; int thandle; /* transaction handle */ void *cb_opaque; /* private user data from */ /* data callback registration */ void *vcb_opaque; /* private user data from */ /* validation callback registration */ int secondary_index; /* if != 0: secondary index number */ /* for list traversal */ int validation_info; /* CONFD_VALIDATION_FLAG_XXX */ char *callpoint_opaque; /* tailf:opaque for callpoint in data model */ char *validate_opaque; /* tailf:opaque for validation point in data model */ union confd_request_data request_data; /* info from northbound agent */ int hide_inactive; /* if != 0: config data with CONFD_ATTR_INACTIVE should be hidden */ /* ConfD internal fields */ int index; /* array pos */ int lastop; /* remember what we were doing */ int last_proto_op; /* ditto */ int seen_reply; /* have we seen a reply msg */ int query_ref; /* last query ref for this trans */ int in_num_instances; u_int32_t num_instances; long nextarg; struct confd_data_cbs *next_dcb; confd_hkeypath_t *next_kp; struct confd_tr_item *lastack; /* tail of acklist */ int refc; };
This callback is required to prepare for future
read/write operations towards the data source. It could be
that a file handle or socket must be established. The place to
do that is usually the init()
callback.
The init()
callback is conceptually
invoked at the start of the transaction, but as an
optimization, ConfD will as far as possible delay the actual
invocation for a given daemon until it is required. In case
of a read-only transaction, or a daemon that is only providing
operational data, this can have the result that a daemon will
not have any callbacks at all invoked (if none of the data
elements that it provides are accessed).
The callback must also indicate to
libconfd
which WORKER_SOCKET should be
used for future communications in this transaction. This is
the mechanism which is used by libconfd to distribute work
among multiple worker threads in the database application. If
another thread than the thread which owns the CONTROL_SOCKET
should be used, it is up to the application to somehow notify
that thread.
The choice of descriptor is done through the API call
confd_trans_set_fd()
which sets the fd
field in the
transaction context.
The callback must return CONFD_OK, CONFD_DELAYED_RESPONSE or CONFD_ERR.
The transaction then enters READ state, where ConfD will
perform a series of read()
operations.
trans_lock()
This callback is invoked when the validation phase of the transaction starts. If the underlying database supports real transactions, it is usually appropriate to start such a native transaction here.
The callback must return CONFD_OK,
CONFD_DELAYED_RESPONSE, CONFD_ERR, or CONFD_ALREADY_LOCKED.
The transaction enters VALIDATE state, where ConfD will
perform a series of read()
operations.
The trans lock is set until either
trans_unlock()
or
finish()
is called. ConfD ensures that a
trans_lock is set on a single transaction only. In the case of
the CONFD_DELAYED_RESPONSE - to later indicate that the
database is already locked, use the
confd_delayed_reply_error()
function with
the special error string "locked". An alternate way to
indicate that the database is already locked is to use
confd_trans_seterr_extended()
(see below)
with CONFD_ERRCODE_IN_USE - this is the only way to give a
message in the "delayed" case. If this function is used, the
callback must return CONFD_ERR in the "normal" case, and in
the "delayed" case
confd_delayed_reply_error()
must be
called with a NULL argument after
confd_trans_seterr_extended()
.
trans_unlock()
This callback is called when the validation of the transaction failed, or the validation is triggered explicitly (i.e. not part of a 'commit' operation). This is common in the CLI and the Web UI where the user can enter invalid data. Transactions that originate from NETCONF will never trigger this callback. If the underlying database supports real transactions and they are used, the transaction should be aborted here.
The callback must return CONFD_OK, CONFD_DELAYED_RESPONSE or CONFD_ERR. The transaction re-enters READ state.
write_start()
This callback is invoked when the validation succeeded and the write phase of the transaction starts. If the underlying database supports real transactions, it is usually appropriate to start such a native transaction here.
The transaction enters the WRITE state. No more
read()
operations will be performed by
ConfD.
The callback must return CONFD_OK, CONFD_DELAYED_RESPONSE, CONFD_ERR, or CONFD_IN_USE.
If CONFD_IN_USE is returned, the transaction is
restarted, i.e. it effectively returns to the READ state. To
give this return code after CONFD_DELAYED_RESPONSE, use the
confd_delayed_reply_error()
function with
the special error string "in_use". An alternative for both
cases is to use
confd_trans_seterr_extended()
(see below)
with CONFD_ERRCODE_IN_USE - this is the only way to give a
message in the "delayed" case. If this function is used, the
callback must return CONFD_ERR in the "normal" case, and in
the "delayed" case
confd_delayed_reply_error()
must be
called with a NULL argument after
confd_trans_seterr_extended()
.
prepare()
If we have multiple sources of data it is highly recommended that the callback is implemented. The callback is called at the end of the transaction, when all read and write operations for the transaction have been performed and the transaction should prepare to commit.
This callback should allocate the resources necessary for the commit, if any. The callback must return CONFD_OK, CONFD_DELAYED_RESPONSE, CONFD_ERR, or CONFD_IN_USE.
If CONFD_IN_USE is returned, the transaction is
restarted, i.e. it effectively returns to the READ state. To
give this return code after CONFD_DELAYED_RESPONSE, use the
confd_delayed_reply_error()
function with
the special error string "in_use". An alternative for both
cases is to use
confd_trans_seterr_extended()
(see below)
with CONFD_ERRCODE_IN_USE - this is the only way to give a
message in the "delayed" case. If this function is used, the
callback must return CONFD_ERR in the "normal" case, and in
the "delayed" case
confd_delayed_reply_error()
must be
called with a NULL argument after
confd_trans_seterr_extended()
.
commit()
This callback is optional. This callback is responsible for writing the data to persistent storage. Must return CONFD_OK, CONFD_DELAYED_RESPONSE or CONFD_ERR.
abort()
This callback is optional. This callback is responsible
for undoing whatever was done in the
prepare()
phase. Must return CONFD_OK,
CONFD_DELAYED_RESPONSE or CONFD_ERR.
finish()
This callback is optional. This callback is responsible
for releasing resources allocated in the
init()
phase. In particular, if the
application choose to use the t_opaque
field in the confd_trans_ctx to hold any
resources, these resources must be released here.
interrupt()
This callback is optional. Unlike the other transaction
callbacks, it does not imply a change of the transaction state,
it is instead a notification that the user running the
transaction requested that it should be interrupted (e.g. Ctrl-C
in the CLI). Also unlike the other transaction callbacks, the
callback request is sent asynchronously on the control socket.
Registering this callback may be useful for a configuration data
provider that has some (transaction or data) callbacks which
require extensive processing - the callback could then determine
whether one of these callbacks is being processed, and if
feasible return an error from that callback instead of
completing the processing. In that case,
confd_trans_seterr_extended()
with
code
CONFD_ERRCODE_INTERRUPT
should be
used.
All the callback functions (except
interrupt()
) must return CONFD_OK,
CONFD_DELAYED_RESPONSE or CONFD_ERR.
It is often useful to associate an error string with a
CONFD_ERR return value. This can be done through a call to
confd_trans_seterr()
or
confd_trans_seterr_extended()
.
Depending on the situation (original caller) the error string gets propagated to the CLI, the Web UI or the NETCONF manager.
We may also optionally have a set of callback functions which span over several ConfD transactions.
If the system is configured in such a way so that the external database owns the candidate data store we must implement four callback functions to do this. If ConfD owns the candidate the candidate callbacks should be set to NULL.
If ConfD owns the candidate, and ConfD has been configured to
support confirmed-commit
, then three
checkpointing functions must be implemented; otherwise these should
be set to NULL. When confirmed-commit
is
enabled, the user can commit the candidate with a timeout. Unless a
confirming commit is given by the user before the timer expires, the
system must rollback to the previous running configuration. This
mechanism is controlled by the checkpoint callbacks. See further
below.
An external database may also (optionally) support the lock/unlock and lock_partial/unlock_partial operations. This is only interesting if there exists additional locking mechanisms towards the database - such as an external CLI which can lock the database, or if the external database owns the candidate.
Finally, the external database may optionally validate a
candidate configuration. Configuration validation is preferably done
through ConfD - however if a system already has implemented
extensive configuration validation - the
candidate_validate()
callback can be
used.
The struct confd_db_cbs structure looks like:
struct confd_db_cbs { int (*candidate_commit)(struct confd_db_ctx *dbx, int timeout); int (*candidate_confirming_commit)(struct confd_db_ctx *dbx); int (*candidate_reset)(struct confd_db_ctx *dbx); int (*candidate_chk_not_modified)(struct confd_db_ctx *dbx); int (*candidate_rollback_running)(struct confd_db_ctx *dbx); int (*candidate_validate)(struct confd_db_ctx *dbx); int (*add_checkpoint_running)(struct confd_db_ctx *dbx); int (*del_checkpoint_running)(struct confd_db_ctx *dbx); int (*activate_checkpoint_running)(struct confd_db_ctx *dbx); int (*copy_running_to_startup)(struct confd_db_ctx *dbx); int (*running_chk_not_modified)(struct confd_db_ctx *dbx); int (*lock)(struct confd_db_ctx *dbx, enum confd_dbname dbname); int (*unlock)(struct confd_db_ctx *dbx, enum confd_dbname dbname); int (*lock_partial)(struct confd_db_ctx *dbx, enum confd_dbname dbname, int lockid, confd_hkeypath_t paths[], int npaths); int (*unlock_partial)(struct confd_db_ctx *dbx, enum confd_dbname dbname, int lockid); int (*delete_config)(struct confd_db_ctx *dbx, enum confd_dbname dbname); };
If we have an externally implemented candidate, that is if
confd.conf item
/confdConfig/datastores/candidate/implementation
is set to "external", we must implement the 5 candidate callbacks.
Otherwise (recommended) they must be set to NULL.
If implementation is "external", all databases (if there are
more than one) MUST take care of the candidate for their part of the
configuration data tree. If ConfD is configured to use an external
database for parts of the configuration, and the built-in CDB database
is used for some parts, CDB will handle the candidate for its part.
See also misc/extern_candidate
in the examples
collection.
The callback functions are are the following:
candidate_commit()
This function should copy the candidate DB into the
running DB. If timeout
!= 0, we should be
prepared to do a rollback or act on a
candidate_confirming_commit()
. The
timeout
parameter can not be used to set
a timer for when to rollback; this timer is handled by the
ConfD daemon. If we terminate without having acted on the
candidate_confirming_commit()
, we MUST
restart with a rollback. Thus we must remember that we are
waiting for a
candidate_confirming_commit()
and we must
do so on persistent storage. Must only be implemented when the
external database owns the candidate.
candidate_confirming_commit()
If the timeout
in the
candidate_commit()
function is != 0, we
will be either invoked here or in the
candidate_rollback_running()
function
within timeout
seconds. candidate_confirming_commit()
should make the commit persistent, whereas a call to
candidate_rollback_running()
would copy
back the previous running configuration to running.
candidate_rollback_running()
If for some reason, apart from a timeout, something goes
wrong, we get invoked in the
candidate_rollback_running()
function. The function should copy back the previous running
configuration to running.
candidate_reset()
This function is intended to copy the current running
configuration into the candidate. It is invoked whenever the
NETCONF operation <discard-changes>
is executed or when a lock is released without committing.
candidate_chk_not_modified()
This function should check to see if the candidate has been modified or not. Returns CONFD_OK if no modifications has been done since the last commit or reset, and CONFD_ERR if any uncommitted modifications exist.
candidate_validate()
This callback is optional. If implemented, the task of
the callback is to validate the candidate configuration. Note
that the running database can be validated by the database in
the prepare()
callback.
candidate_validate()
is only meaningful
when an explicit validate operation is received, e.g. through
NETCONF.
add_checkpoint_running()
This function should be implemented only when ConfD owns the candidate, and confirmed-commit is enabled.
It is responsible for creating a checkpoint of the current running configuration and storing the checkpoint in non-volatile memory. When the system restarts this function should check if there is a checkpoint available, and use the checkpoint instead of running.
del_checkpoint_running()
This function should delete a checkpoint created by
add_checkpoint_running()
. It is called by
ConfD when a confirming commit is received.
activate_checkpoint_running()
This function should rollback running to the checkpoint
created by add_checkpoint_running()
. It
is called by ConfD when the timer expires or if the user
session expires.
copy_running_to_startup()
This function should copy running to startup. It only needs to be implemented if the startup data store is enabled.
running_chk_not_modified()
This function should check to see if running has been modified or not. It only needs to be implemented if the startup data store is enabled. Returns CONFD_OK if no modifications have been done since the last copy of running to startup, and CONFD_ERR if any modifications exist.
lock()
This should only be implemented if our database supports
locking from other sources than through ConfD. In this case
both the lock/unlock and lock_partial/unlock_partial callbacks
must be implemented. If a lock on the whole database is set
through e.g. NETCONF, ConfD will first make sure that no other
ConfD transaction has locked the database. Then it will call
lock()
to make sure that the database is
not locked by some other source (such as a non-ConfD CLI).
Returns CONFD_OK on success, and CONFD_ERR if the lock was
already held by an external entity.
unlock()
Unlocks the database.
lock_partial()
This should only be implemented if our database supports
locking from other sources than through ConfD, see
lock()
above. This callback is invoked
if a northbound agent requests a partial lock. The
paths[]
argument is an
npaths
long array of hkeypaths that
identify the leafs and/or subtrees that are to be locked. The
lockid
is a reference that will be used
on a subsequent corresponding
unlock_partial()
invocation.
unlock_partial()
Unlocks the partial lock that was requested with
lockid
.
delete_config()
Will be called for 'startup' or 'candidate' only. The database is supposed to be set to erased.
All the above callback functions must return either CONFD_OK
or CONFD_ERR. If the system is configured so that ConfD owns the
candidate, then obviously the candidate related functions need not
be implemented. If the system is configured to not do confirmed
commit, candidate_confirming_commit()
and
candidate_commit()
need not to be
implemented.
It is often interesting to associate an error string with a
CONFD_ERR return value. In particular the
validate()
callback must typically indicate
which item was invalid and why. This can be done through a call to
confd_db_seterr()
or
confd_db_seterr_extended()
.
Depending on the situation (original caller) the error string is propagated to the CLI, the Web UI or the NETCONF manager.
This function registers the data manipulation callbacks. The data model defines a number of "callpoints". Each callpoint must have an associated set of data callbacks.
Thus if our database application serves three different callpoints in the data model we must install three different sets of data manipulation callbacks - one set at each callpoint.
The data callbacks either return data back to ConfD or they do
not. For example the create()
callback does not
return data whereas the get_next()
callback
does. All the callbacks that return data do so through API
functions, not by means of return values from the function
itself.
The struct confd_data_cbs is defined as:
struct confd_data_cbs { char callpoint[MAX_CALLPOINT_LEN]; /* where in the XML tree do we */ /* want this struct */ /* Only necessary to have this cb if our data model has */ /* typeless optional nodes or oper data lists w/o keys */ int (*exists_optional)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp); int (*get_elem)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp); int (*get_next)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, long next); int (*set_elem)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, confd_value_t *newval); int (*create)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp); int (*remove)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp); /* optional (find list entry by key/index values) */ int (*find_next)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, enum confd_find_next_type type, confd_value_t *keys, int nkeys); /* optional optimizations */ int (*num_instances)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp); int (*get_object)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp); int (*get_next_object)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, long next); int (*find_next_object)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, enum confd_find_next_type type, confd_value_t *keys, int nkeys); /* next two are only necessary if 'choice' is used */ int (*get_case)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, confd_value_t *choice); int (*set_case)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, confd_value_t *choice, confd_value_t *caseval); /* next two are only necessary for config data providers, and only if /confdConfig/enableAttributes is 'true' */ int (*get_attrs)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, u_int32_t *attrs, int num_attrs); int (*set_attr)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, u_int32_t attr, confd_value_t *v); /* only necessary if "ordered-by user" is used */ int (*move_after)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, confd_value_t *prevkeys); /* only for per-transaction-invoked transaction hook */ int (*write_all)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp); void *cb_opaque; /* private user data */ };
One of the parameters to the callback is a confd_hkeypath_t (h - as in hashed keypath). This is fully described in confd_types(3).
The cb_opaque
element can be used to pass
arbitrary data to the callbacks, e.g. when the same set of callbacks
is used for multiple callpoints. It is made available to the
callbacks via an element with the same name in the transaction
context (tctx
argument), see the structure
definition above.
If the tailf:opaque
substatement has been used with
the tailf:callpoint
statement in the data model, the
argument string is made available to the callbacks via the
callpoint_opaque
element in the transaction
context.
When use of the CONFD_ATTR_INACTIVE
attribute is enabled in the ConfD configuration
(/confdConfig/enableAttributes
and
/confdConfig/enableInactive
both set to
true
), read callbacks
(get_elem()
etc) for configuration data must
observe the current value of the hide_inactive
element in the transaction context. If it is non-zero, those callbacks
must act as if data with the CONFD_ATTR_INACTIVE
attribute set does not exist.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_PROTOUSAGE
get_elem()
This callback function needs to return the value of a specific leaf. Assuming we have the following data model:
container servers { tailf:callpoint mycp; list server { key name; max-elements 64; leaf name { type string; } leaf ip { type inet:ip-address; } leaf port { type inet:port-number; } } }
For example the value of the ip
leaf in the server entry whose key is "www" can be returned
separately. The way to return a single data item is through
confd_data_reply_value()
.
The callback must return CONFD_OK on success, CONFD_ERR
on error or CONFD_DELAYED_RESPONSE if the reply value is not
yet available. In the latter case the application must at a
later stage call confd_data_reply_value()
(or confd_delayed_reply_ok()
for a
write operation). If an error is discovered at the time of a
delayed reply, the error is signaled through a call to
confd_delayed_reply_error()
If the leaf does not exist the callback must call
confd_data_reply_not_found()
. If the
leaf has a default value defined in the data model, and no
value has been set, the callback should use
confd_data_reply_value()
with a value of
type C_DEFAULT - this makes it possible for northbound agents
to leave such leafs out of the data returned to the
user/manager (if requested).
The implementation of get_elem()
must be prepared to return values for all the leafs including the
key(s). When ConfD invokes get_elem()
on
a key leaf it is an existence test. The application should
verify whether the object exists or not.
get_next()
This callback makes it possible for ConfD to traverse a
set of list entries. The
next
parameter will be
-1
on the first invocation. This function
should reply by means of the function
confd_data_reply_next_key()
.
If the list has a tailf:secondary-index
statement (see tailf_yang_extensions(5)),
and the entries are supposed to be retrieved according to
one of the secondary indexes, the variable
tctx->secondary_index
will be set to a
value greater than 0
, indicating which
secondary-index is used. The first secondary-index in the
definition is identified with the value 1
,
the second with 2
, and so on. confdc can
be used to generate #define
s for the
index names. If no secondary indexes are defined, or if the
sort order should be according to the key values,
tctx->secondary_index
is
0
.
To signal that no more entries exist, we reply with a
NULL pointer as the key value in the
confd_data_reply_next_key()
function.
The callback must return CONFD_OK on success, CONFD_ERR
on error or CONFD_DELAYED_RESPONSE if the reply value is not
yet available. In the latter case the application must at a
later stage call
confd_data_reply_next_key()
.
For a list that does not specify a non-default sort
order by means of a ordered-by user
or
tailf:sort-order
statement, ConfD assumes that list
entries are ordered strictly by increasing key (or secondary
index) values. Thus for correct operation, we must observe this
order when returning list entries in a sequence of
get_next()
calls.
set_elem()
This callback writes the value of a leaf. Note that an optional leaf with a type other than empty is created by a call to this function. The callback must return CONFD_OK on success, CONFD_ERR on error or CONFD_DELAYED_RESPONSE.
create()
This callback creates a new list entry, a
presence
container, or a leaf of type
empty
. In the case of the servers
data model
above, this function need to create a new server
entry. Must return CONFD_OK on success, CONFD_ERR on error,
CONFD_DELAYED_RESPONSE or CONFD_ACCUMULATE.
The data provider is responsible for maintaining the order of
list entries. If the list is marked as ordered-by
user
in the YANG data model, the
create()
callback must add the list entry to
the end of the list.
remove()
This callback is used to remove an existing list entry, a
presence
container, or an optional leaf
and all its sub nodes (if
any). When we use the YANG choice
statement in the
data model, it may also be used to remove nodes that are not
optional as such when a different case
(or none) is
selected. I.e. it must always be possible to remove cases in a
choice.
Must return CONFD_OK on success, CONFD_ERR on error, CONFD_DELAYED_RESPONSE or CONFD_ACCUMULATE.
exists_optional()
If we have presence
containers or leafs of
type empty
, we cannot use the
get_elem()
callback to read the value of such a
node, since it does not have a type. An example of a
data model could be:
container bs { presence ""; tailf:callpoint bcp; list b { key name; max-elements 64; leaf name { type string; } container opt { presence ""; leaf ii { type int32; } } leaf foo { type empty; } } }
The above YANG fragment has 3 nodes that may or may not
exist and that do not have a type.
If we do not have any such elements, nor any operational data
lists without keys (see below), we do not need to implement the
exists_optional()
callback and can set it
to NULL.
If we have the above data model, we must implement the
exists_optional()
, and our implementation
must be prepared to reply on calls of the function for the
paths /bs
, /bs/b/opt
, and
/bs/b/foo
. The leaf /bs/b/opt/ii
is
not mandatory, but it does have a type namely
int32, and thus the existence of that leaf will
be determined through a call to the
get_elem()
callback.
The exists_optional()
callback may
also be invoked by ConfD as "existence test" for an entry in an
operational data list without keys (see the Operational Data chapter in
the User Guide). Normally this existence test is done
with a get_elem()
request for the first
key, but since there are no keys, this callback is used
instead. Thus if we have such lists, we must also implement this
callback, and handle a request where the keypath identifies a
list entry.
The callback must reply to ConfD using either the
confd_data_reply_not_found()
or the
confd_data_reply_found()
function.
The callback must return CONFD_OK on success, CONFD_ERR on error or CONFD_DELAYED_RESPONSE if the reply value is not yet available.
find_next()
This optional callback can be registered to optimize cases
where ConfD wants to start a list traversal at some other point
than at the first entry of the list, or otherwise make a "jump"
in a list traversal. If the callback is not registered, ConfD
will use a sequence of get_next()
calls to
find the desired list entry.
Where the get_next()
callback
provides a next
parameter to indicate
which keys should be returned, this callback instead provides a
type
parameter and a set of values to
indicate which keys should be returned. Just like for
get_next()
, the callback should reply by calling
confd_data_reply_next_key()
with the keys
for the requested list entry.
The keys
parameter is a pointer to
a nkeys
elements long array of key
values, or secondary index-leaf values (see below). The
type
can have one of two values:
CONFD_FIND_NEXT
The callback should always reply with the key values
for the first list entry after the
one indicated by the keys
array,
and a next
value appropriate for
retrieval of subsequent entries. The
keys
array may not correspond to an
actual existing list entry - the callback must return the
keys for the first existing entry that is "later" in the
list order than the keys provided by the
callback. Furthermore the number of values provided in the
array (nkeys
) may be fewer than the
number of keys (or number of index-leafs for a
secondary-index) in the data model, possibly even zero.
This means that only the first
nkeys
values are provided, and the
remaining ones should be taken to have a value "earlier"
than the value for any existing list entry.
CONFD_FIND_SAME_OR_NEXT
If the values in the keys
array completely identify an actual existing list entry,
the callback should reply with the keys for this list
entry and a corresponding next
value. Otherwise the same logic as described for
CONFD_FIND_NEXT
should be used.
The dp/find_next
example in the
bundled examples collection has an implementation of the
find_next()
callback for a list with two
integer keys. It shows how the type
value
and the provided keys need to be combined in order to find the
requested entry - or find that no entry matching the request
exists.
If the list has a tailf:secondary-index
statement (see tailf_yang_extensions(5)),
the callback must examine the value of the
tctx->secondary_index
variable, as described
for the get_next()
callback. If
tctx->secondary_index
has a value greater
than 0
, the keys
and
nkeys
parameters do not represent key
values, but instead values for the index leafs specified by the
tailf:index-leafs
statement for the secondary
index. The callback should however still reply with the actual
key values for the list entry in the
confd_data_reply_next_key()
call.
Once we have called
confd_data_reply_next_key()
, ConfD will use
get_next()
(or
get_next_object()
) for any subsequent
entry-by-entry list traversal - however we can request that this
traversal should be done using find_next()
(or find_next_object()
) instead, by passing
-1
for the next
parameter to
confd_data_reply_next_key()
. In this case
ConfD will always invoke
find_next()
/find_next_object()
with type
CONFD_FIND_NEXT
, and the (complete) set of
keys from the previous reply.
In the case of list traversal by means of a
secondary index, the secondary index values must be unique for
entry-by-entry traversal with
find_next()
/find_next_object()
to be possible. Thus we can not pass -1
for
the next
parameter to
confd_data_reply_next_key()
in this case if
the secondary index values are not unique.
To signal that no entry matching the request exists,
i.e. we have reached the end of the list while evaluating the
request, we reply with a NULL pointer as the key value in the
confd_data_reply_next_key()
function.
For a list that does not specify a non-default sort
order by means of a ordered-by user
or
tailf:sort-order
statement, ConfD assumes that list
entries are ordered strictly by increasing key
values.
If we have registered find_next()
(or
find_next_object()
), it is not strictly
necessary to also register get_next()
(or
get_next_object()
) - except for the case of
traversal by secondary index when the secondary index values are
not unique, see above. If a northbound agent does a get_next
request, and neither get_next()
nor
get_next_object()
is registered, ConfD will
instead invoke find_next()
(or
find_next_object()
), the same way as if
-1
had been passed for the
next
parameter to
confd_data_reply_next_key()
as described
above - the actual next
value passed is
ignored. The very first get_next request for a traversal
(i.e. where the next
parameter would be
-1
) will cause a find_next invocation with
type
CONFD_FIND_NEXT
and nkeys
== 0, i.e. no keys
provided.
The callback must return CONFD_OK on success, CONFD_ERR
on error or CONFD_DELAYED_RESPONSE if the reply value is not
yet available. In the latter case the application must at a
later stage call
confd_data_reply_next_key()
.
num_instances()
This callback can optionally be implemented. The purpose
is to return the number of entries in a list. If
the callback is set to NULL, whenever ConfD needs to calculate
the number of entries in a certain list, ConfD
will iterate through the entries by means of consecutive
calls to the get_next()
callback.
If we have a large number of entries and it is computationally cheap to calculate the number of entries in a list, it may be worth the effort to implement this callback for performance reasons.
The number of entries is returned in an
confd_value_t value of type C_INT32. The value is
returned through a call to
confd_data_reply_value()
, see code
example below:
int num_instances; confd_value_t v; CONFD_SET_INT32(&v, num_instances); confd_data_reply_value(trans_ctx, &v); return CONFD_OK;
Must return CONFD_OK on success, CONFD_ERR on error or CONFD_DELAYED_RESPONSE.
get_object()
The implementation of this callback is also optional. The
purpose of the callback is to return an entire object, i.e. a
list entry, in one
swoop. If the callback is not implemented, ConfD will retrieve
the whole object through a series of calls to
get_elem()
.
The callback will only be called for list entries
- i.e. get_elem()
is still needed for
leafs that are not defined in a list, but if there are no such
leafs in the part of the data model covered by a given
callpoint, the get_elem()
callback may be
omitted when get_object()
is
registered. This has the drawback that ConfD will have to invoke
get_object() even if only a single leaf in a list entry is
needed though, e.g. for the existence test mentioned for
get_elem()
.
When ConfD invokes the
get_elem()
callback, it is the
responsibility of the application to issue calls to the reply
function confd_data_reply_value()
. The
get_object()
callback cannot use this
function since it needs to return a sequence of values. The
get_object()
callback must use either the
confd_data_reply_value_array()
function
or the confd_data_reply_tag_value_array()
function. See the description of these functions below for
the details of the arguments passed. If the entry requested
does not exist, the callback must call
confd_data_reply_not_found()
.
Remember, the callback
exists_optional()
must always be
implemented when we have presence
containers or leafs of
type empty
. If we also choose to implement the
get_object()
callback, ConfD can
sometimes derive the existence of such a node through a
previous call to get_object()
. This is
however not always the case, thus even if we implement
get_object()
, we must also implement
exists_optional()
if we have such nodes.
If we pass an array of values which does not comply with the rules for the above functions, ConfD will notice and an error is reported to the agent which issued the request. A message is also logged to ConfD's developerLog.
The callback must return CONFD_OK on success, CONFD_ERR on error or CONFD_DELAYED_RESPONSE if the reply value is not yet available.
get_next_object()
The implementation of this callback is also
optional. Similar to the get_object()
callback the purpose of this callback is to return an entire
object, or even multiple objects, in one swoop. It combines the
functionality of get_next()
and
get_object()
into a single callback, and
adds the possibility to return multiple objects. Thus
we need only implement this callback if it very important to
be able to traverse a list very fast. If the callback is not
implemented, ConfD will retrieve the whole object through a
series of calls to get_next()
and
consecutive calls to either get_elem()
or
get_object()
.
When we have registered
get_next_object()
, it is not strictly
necessary to also register get_next()
, but
omitting get_next()
may have a serious
performance impact, since there are cases (e.g. CLI tab
completion) when ConfD only wants to retrieve the keys for a
list. In such a case, if we have only registered
get_next_object()
, all the data for the
list will be retrieved, but everything except the keys will be
discarded. Also note that even if we have registered
get_next_object()
, at least one of
the get_elem()
and
get_object()
callbacks must be
registered.
Similar to the get_next()
callback,
if the next
parameter is
-1
ConfD wants to retrieve the first entry
in the list.
Similar to the get_next()
callback,
if the tctx->secondary_index
parameter is
greater than 0
ConfD wants to retrieve the
entries in the order defined by the secondary index.
Similar to the get_object()
callback, get_next_object()
needs to
reply with an entire object expressed as either an array of
confd_value_t values or an array of
confd_tag_value_t values. It must also indicate
which is the next entry in the list
similar to the get_next()
callback. The
two functions
confd_data_reply_next_object_array()
and
confd_data_reply_next_object_tag_value_array()
are use to convey the return values for one object from the
get_next_object()
callback.
If we want to reply with multiple objects, we must instead
use one of the functions
confd_data_reply_next_object_arrays()
and
confd_data_reply_next_object_tag_value_arrays()
.
These functions take an "array of object arrays", where each
element in the array corresponds to the reply for a single
object with
confd_data_reply_next_object_array()
and
confd_data_reply_next_object_tag_value_array()
,
respectively.
If we pass an array of values which does not comply with the rules for the above functions, ConfD will notice and an error is reported to the agent which issued the request. A message is also logged to ConfD's developerLog.
The callback must return CONFD_OK on success, CONFD_ERR on error or CONFD_DELAYED_RESPONSE if the reply value is not yet available.
find_next_object()
The implementation of this callback is also
optional. It relates to get_next_object()
in exactly the same way as find_next()
relates to get_next()
. I.e. instead of a
parameter next
, we get a
type
parameter and a set of key values,
or secondary index-leaf values, to indicate which object or
objects to return to ConfD via one of the reply functions.
Similar to the get_next_object()
callback, if the tctx->secondary_index
parameter is greater than 0
ConfD wants to
retrieve the entries in the order defined by the secondary
index. And as described for the
find_next()
callback, in this case the
keys
and nkeys
parameters represent values for the index leafs specified by the
tailf:index-leafs
statement for the secondary
index.
Similar to the get_next_object()
callback, the callback can use any of the functions
confd_data_reply_next_object_array()
,
confd_data_reply_next_object_tag_value_array()
,
confd_data_reply_next_object_arrays()
, and
confd_data_reply_next_object_tag_value_arrays()
to return one or more objects to ConfD.
If we pass an array of values which does not comply with the rules for the above functions, ConfD will notice and an error is reported to the agent which issued the request. A message is also logged to ConfD's developerLog.
The callback must return CONFD_OK on success, CONFD_ERR on error or CONFD_DELAYED_RESPONSE if the reply value is not yet available.
get_case()
This callback only needs to be implemented if we use the
YANG choice
statement in the part of the data model
that our data provider is responsible for, but when we use
choice, the callback is required. It should return the
currently selected case
for the choice given by the
choice
argument -
kp
is the path to the container or list
entry where the choice is defined.
In the general case, where there may be multiple levels of
choice
statements without intervening
container
or list
statements in the
data model, the choice is represented as an array of
confd_value_t elements with the type C_XMLTAG,
terminated by an element with the type C_NOEXISTS. This array
gives a reversed path with alternating choice and case names,
from the data node given by kp
to the
specific choice that the callback request pertains to - similar
to how a confd_hkeypath_t gives a path through the
data tree.
If we don't have such "nested" choices in the data model,
we can ignore this array aspect, and just treat the
choice
argument as a single
confd_value_t value. The case is always represented
as a confd_value_t with the type C_XMLTAG. I.e. we
can use CONFD_GET_XMLTAG() to get the choice tag from
choice
and CONFD_SET_XMLTAG() to set the
case tag for the reply value. The callback should use
confd_data_reply_value()
to return the case
value to ConfD, or
confd_data_reply_not_found()
for an
optional choice without default case if no case is currently
selected. If an optional choice with default case does not have
a selected case, the callback should use
confd_data_reply_value()
with a value of
type C_DEFAULT.
Must return CONFD_OK on success, CONFD_ERR on error, or CONFD_DELAYED_RESPONSE.
set_case()
This callback is completely optional, and will only be
invoked (if registered) if we use the YANG choice
statement and provide configuration data. The callback sets the
currently selected case
for the choice given by the
kp
and choice
arguments, and is mainly intended to make it easier to support
the get_case()
callback. ConfD will
additionally invoke the remove()
callback
for all nodes in the previously selected case, i.e. if we
register set_case()
, we
do not need to analyze set_elem()
callbacks to determine the currently selected case, or figure
out which nodes that should be deleted.
For a choice without a mandatory true
statement, it is possible to have no case at all selected. To
indicate that the previously selected case should be deleted
without selecting another case, the callback will be invoked
with NULL for the caseval
argument.
The callback must return CONFD_OK on success, CONFD_ERR on error, CONFD_DELAYED_RESPONSE or CONFD_ACCUMULATE.
get_attrs()
This callback only needs to be implemented for callpoints
specified for configuration data, and only if attributes are
enabled in the ConfD configuration
(/confdConfig/enableAttributes
set to
true
). These
are the currently supported attributes:
/* CONFD_ATTR_TAGS: value is C_LIST of C_BUF/C_STR */ #define CONFD_ATTR_TAGS 0x80000000 /* CONFD_ATTR_ANNOTATION: value is C_BUF/C_STR */ #define CONFD_ATTR_ANNOTATION 0x80000001 /* CONFD_ATTR_INACTIVE: value is C_BOOL 1 (i.e. "true") */ #define CONFD_ATTR_INACTIVE 0x00000000
The attrs
parameter is an array of
attributes of length num_attrs
, giving
the requested attributes - if num_attrs
is 0, all attributes are requested. If the node given by
kp
does not exist, the callback should
reply by calling
confd_data_reply_not_found()
, otherwise it
should call confd_data_reply_attrs()
, even
if no attributes are set.
It is very important to observe this distinction,
i.e. to use confd_data_reply_not_found()
when the node doesn't exist, since ConfD may use
get_attrs()
as an existence check when
attributes are enabled. (This avoids doing one callback request
for existence check and another to collect the
attributes.)
Must return CONFD_OK on success, CONFD_ERR on error, or CONFD_DELAYED_RESPONSE.
set_attr()
This callback also only needs to be implemented for
callpoints specified for configuration data, and only if
attributes are enabled in the ConfD configuration
(/confdConfig/enableAttributes
set to
true
). See
get_attrs()
above for the supported
attributes.
The callback should set the attribute
attr
for the node given by
kp
to the value
v
. If the callback is invoked with NULL
for the value argument, it means that the attribute should be
deleted.
The callback must return CONFD_OK on success, CONFD_ERR on error, CONFD_DELAYED_RESPONSE or CONFD_ACCUMULATE.
move_after()
This callback only needs to be implemented if we provide
configuration data that has YANG lists with a ordered-by
user
statement. The
callback moves the list entry given by
kp
. If prevkeys
is
NULL, the entry is moved first in the list, otherwise it is
moved after the entry given by prevkeys
.
In this case prevkeys
is a pointer to an
array of key values identifying an entry in the list. The array
is terminated with an element that has type C_NOEXISTS.
The callback must return CONFD_OK on success, CONFD_ERR on error, CONFD_DELAYED_RESPONSE or CONFD_ACCUMULATE.
write_all()
This callback will only be invoked for a transaction hook
specified with tailf:invocation-mode
per-transaction;
- see the chapter Transformations, Hooks, Hidden
Data and Symlinks in the User Guide. It is also the only
callback that is invoked for such a hook. The callback is
expected to make all the modifications to the current
transaction that hook functionality requires. The
kp
parameter is currently always NULL,
since the callback does not pertain to any particular data
node.
The callback must return CONFD_OK on success, CONFD_ERR on error, or CONFD_DELAYED_RESPONSE.
The six write callbacks (excluding
write_all()
), namely
set_elem()
,
create()
, remove()
,
set_case()
, set_attr()
,
and move_after()
may return the
value CONFD_ACCUMULATE. If CONFD_ACCUMULATE is returned the
library will accumulate the written values as a linked list of
operations. This list can later be traversed in either of the
transaction callbacks prepare()
or
commit()
.
This provides trivial transaction support for applications that want to implement the ConfD two-phase commit protocol but lacks an underlying database with proper transaction support. The write operations are available as a linked list of confd_tr_item structs:
struct confd_tr_item { char *callpoint; enum confd_tr_op op; confd_hkeypath_t *hkp; confd_value_t *val; confd_value_t *choice; /* only for set_case */ u_int32_t attr; /* only for set_attr */ struct confd_tr_item *next; };
The list is available in the transaction context in the
field accumulated
. The entire list and
its content will be automatically freed by the library once the
transaction finishes.
int confd_register_range_data_cb( | struct confd_daemon_ctx *dx, |
const struct confd_data_cbs *data, | |
const confd_value_t *lower, | |
const confd_value_t *upper, | |
int numkeys, | |
const char *fmt, | |
...) ; |
This is a variant of
confd_register_data_cb()
which registers a set
of callbacks for a range of list entries. There can thus be multiple
sets of C functions registered on the same callpoint, even by
different daemons. The lower
and
upper
parameters are two
numkeys
long arrays of key values, which define
the endpoints of the list range.
It is also possible to do a "default" registration, by
giving lower
and upper
as NULL (numkeys
is ignored). The callbacks for
the default registration will be invoked when the keys are not in any
of the explicitly registered ranges.
The fmt
and remaining
parameters specify a string path for the list that the
keys apply to, in the same form as for the
confd_lib_maapi(3) and
confd_lib_cdb(3)
functions. However if the list is a sublist to another list, the key
element for the parent list(s) may be completely omitted, to indicate
that the registration applies to all entries for the parent list(s)
(similar to CDB subscription paths).
An example that registers one set of callbacks for the range
/servers/server{aaa}
-
/servers/server{mzz}
and another set for
/servers/server{naa}
-
/servers/server{zzz}
:
confd_value_t lower, upper; CONFD_SET_STR(&lower, "aaa"); CONFD_SET_STR(&upper, "mzz"); if (confd_register_range_data_cb(dctx, &data_cb1, &lower, &upper, 1, "/servers/server") == CONFD_ERR) confd_fatal("Failed to register data cb\n"); CONFD_SET_STR(&lower, "naa"); CONFD_SET_STR(&upper, "zzz"); if (confd_register_range_data_cb(dctx, &data_cb2, &lower, &upper, 1, "/servers/server") == CONFD_ERR) confd_fatal("Failed to register data cb\n");
In this example, as in most cases where this function is used,
the data model defines a list with a single key, and
numkeys
is thus always
1
. However it can also be used for lists that
have multiple keys, in which case the upper
and
lower
arrays may be populated with multiple
keys, upto however many keys the data model specifies for the list,
and numkeys
gives the number of keys in the
arrays. If fewer keys than specified in the data model are given, the
registration covers all possible values for the remaining keys, i.e.
they are effectively wildcarded.
While traversal of a list with range registrations will always
invoke e.g. get_next()
only for actually
registered ranges, it is also possible that a request from a
northbound interface is made for data in a specific list entry. If the
registrations do not cover all possible key values, such a request
could be for a list entry that does not fall in any of the registered
ranges, which will result in a "no registration" error. To avoid the
error, we can either restrict the type of the keys such that only
values that fall in the registered ranges are valid, or, for
operational data, use a "default" registration as described above. In
this case the daemon with the "default" registration would just reply
with confd_data_reply_not_found()
for all
requests for specific data, and
confd_data_reply_next_key()
with NULL for the key
values for all get_next()
etc requests.
For a given callpoint name, there can only be either one non-range registration or a number of range registrations that all pertain to the same list. If a range registration is done after a non-range registration or vice versa, or if a range registration is done with a different list path than earlier range registrations, the latest registration completely replaces the earlier one(s). If we want to register for the same ranges in different lists, we must thus have a unique callpoint for each list.
Range registrations can not be used for lists that have
the tailf:secondary-index
extension, since there is no
way for ConfD to traverse the registrations in secondary-index
order.
This function can be used to register information callbacks that are invoked for user session start and stop. The struct confd_usess_cbs is defined as:
struct confd_usess_cbs { void (*start)(struct confd_daemon_ctx *dx, struct confd_user_info *uinfo); void (*stop)(struct confd_daemon_ctx *dx, struct confd_user_info *uinfo); };
Both callbacks are optional. They can be used e.g. for a
multi-threaded daemon to manage a pool of worker threads, by
allocating worker threads to user sessions. In this case we would
ideally allocate a worker thread the first time an
init()
callback for a given user session
requires a worker socket to be assigned, and use only the
stop()
usess callback to release the worker
thread - using the start()
callback to allocate
a worker thread would often mean that we allocated a thread that was
never used. The u_opaque
element in the
struct confd_user_info can be used to manage
such allocations.
These callbacks will only be invoked if the daemon has
also registered other callbacks. Furthermore, as an optimization,
ConfD will delay the invocation of the start()
callback until some other callback is invoked. This means that if no
other callbacks for the daemon are invoked for the duration of a user
session, neither start()
nor
stop()
will be invoked for that user session.
If we want timely notification of start and stop for all user
sessions, we can subscribe to CONFD_NOTIF_AUDIT
events, see confd_lib_events(3).
When we call confd_register_done()
(see below), the start()
callback (if
registered) will be invoked for each user session that already
exists.
When we have registered all the callbacks for a daemon (including the other types described below if we have them), we must call this function to synchronize with ConfD. No callbacks will be invoked until it has been called, and after the call, no further registrations are allowed.
The database application owns all data provider sockets to
ConfD and is responsible for the polling of these sockets. When
one of the ConfD sockets has I/O ready to read, the application
must invoke confd_fd_ready()
on the
socket. This function will:
Read data from ConfD
Unmarshal this data
Invoke the right callback with the right arguments
When this function reads the request from from ConfD it will
block on read()
, thus if it is important for
the application to have nonblocking I/O, the application must
dispatch I/O from ConfD in a separate thread.
The function returns the return value from the callback
function, normally CONFD_OK (0), or CONFD_ERR (-1) on error and
CONFD_EOF (-2) when the socket to ConfD has been closed. Thus
CONFD_ERR can mean either that the callback function that was
invoked returned CONFD_ERR, or that some error condition occurred
within the confd_fd_ready()
function. These
cases can be distinguished via confd_errno
, which
will be set to CONFD_ERR_EXTERNAL if CONFD_ERR comes from the
callback function. Thus a correct call to
confd_fd_ready()
looks like:
struct pollfd set[n]; /* ...... */ if (set[0].revents & POLLIN) { if ((ret = confd_fd_ready(dctx, mysock)) == CONFD_EOF) { confd_fatal("ConfD socket closed\n"); } else if (ret == CONFD_ERR && confd_errno != CONFD_ERR_EXTERNAL) { confd_fatal("Error on ConfD socket request: %s (%d): %s\n", confd_strerror(confd_errno), confd_errno, confd_lasterr()); } }
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_PROTOUSAGE, CONFD_ERR_EXTERNAL
Associate a worker socket with the transaction, or validation
phase. This function must be called in the transaction and validation
init()
callbacks - a minimal implementation of a
transaction init()
callback looks like:
static int init(struct confd_trans_ctx *tctx) { confd_trans_set_fd(tctx, workersock); return CONFD_OK; }
This function is used to return a single data item to ConfD.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADTYPE
This function is used to return an array of values,
corresponding to a complete list entry, to ConfD. It can be
used by the optional get_object()
callback. The
vs
array is populated with
n
values according to the specification of the
Value Array format in the XML STRUCTURES
section of the confd_types(3) manual
page.
In the easiest case, similar to the "servers" example above, we can construct a reply array as follows:
struct in_addr ip4 = my_get_ip(.....); confd_value_t ret[3]; CONFD_SET_STR(&ret[0], "www"); CONFD_SET_IPV4(&ret[1], ip4); CONFD_SET_UINT16(&ret[2], 80); confd_data_reply_value_array(tctx, ret, 3);
Any containers inside the object must also be passed in the
array. For example an entry in the b
list used in the explanation for
exists_optional()
would have to be passed
as:
confd_value_t ret[4]; CONFD_SET_STR(&ret[0], "b_name"); CONFD_SET_XMLTAG(&ret[1], myprefix_opt, myprefix__ns); CONFD_SET_INT32(&ret[2], 77); CONFD_SET_NOEXISTS(&ret[3]); confd_data_reply_value_array(tctx, ret, 4);
Thus, a container or a leaf of type empty
must be
passed as its equivalent XML tag if it exists. If a
presence
container or leaf of type empty
does not exist, it must be passed as a
value of C_NOEXISTS. In the example above, the leaf foo
does
not exist, thus the contents of position 3
in the
array.
If a presence
container does not exist, its non existing
values must not be passed - it suffices to say that the container
itself does not exist. In the example above, the opt
container did
exist and thus we also had to pass the contained value(s), the ii
leaf.
Hence, the above example represents:
<b> <name>b_name</name> <opt> <ii>77</ii> </opt> </b>
int confd_data_reply_tag_value_array( | struct confd_trans_ctx *tctx, |
const confd_tag_value_t *tvs, | |
int n) ; |
This function is used to return an array of values,
corresponding to a complete list entry, to ConfD. It can be
used by the optional get_object()
callback. The
tvs
array is populated with
n
values according to the specification of the
Tagged Value Array format in the XML STRUCTURES
section of the confd_types(3) manual
page.
I.e. the difference from
confd_data_reply_value_array()
is that the
values are tagged with the node names from the data model - this means
that non-existing values can simply be omitted from the array, per the
specification above. Additionally the key leafs can be omitted,
since they are already known by ConfD - if the key leafs are
included, they will be ignored. Finally, in e.g. the case of a
container with both config and non-config data, where the config
data is in CDB and only the non-config data provided by the
callback, the config elements can be omitted (for
confd_data_reply_value_array()
they must be
included as C_NOEXISTS elements).
However, although the tagged value array format can represent
nested lists, these must not be passed via this
function, since the get_object()
callback only
pertains to a single entry of one list. Nodes
representing sub-lists must thus be omitted from the
array, and ConfD will issue separate
get_object()
invocations to retrieve the data
for those.
Using the same examples as above, in the "servers" case, we can construct a reply array as follows:
struct in_addr ip4 = my_get_ip(.....); confd_tag_value_t ret[2]; int n = 0; CONFD_SET_TAG_IPV4(&ret[n], myprefix_ip, ip4); n++; CONFD_SET_TAG_UINT16(&ret[n], myprefix_port, 80); n++; confd_data_reply_tag_value_array(tctx, ret, n);
An entry in the b
list used in the explanation for
exists_optional()
would be passed as:
confd_tag_value_t ret[3]; int n = 0; CONFD_SET_TAG_XMLBEGIN(&ret[n], myprefix_opt, myprefix__ns); n++; CONFD_SET_TAG_INT32(&ret[n], myprefix_ii, 77); n++; CONFD_SET_TAG_XMLEND(&ret[n], myprefix_opt, myprefix__ns); n++; confd_data_reply_tag_value_array(tctx, ret, n);
The C_XMLEND element is not strictly necessary in this case,
since there are no subsequent elements in the array. However it
would have been required if the optional foo
leaf had existed,
thus it is good practice to always include both the C_XMLBEGIN and
C_XMLEND elements for nested containers (if they exist, that is -
otherwise neither must be included).
int confd_data_reply_next_key( | struct confd_trans_ctx *tctx, |
const confd_value_t *v, | |
int num_vals_in_key, | |
long next) ; |
This function is used by the get_next()
and
find_next()
callbacks to return the next key.
A list may have multiple
key leafs specified in the data model. The parameter
num_vals_in_key
indicates the number of key
values, i.e. the length of the v
array. In
the typical case with all lists having just a single key
leaf specified, num_vals_in_key
is always
1.
The long next
will be passed into the
next invocation of the get_next()
callback if it has a value other than -1
.
Thus this value provides a means for the application to
traverse the data. Since this is long it is
possible to pass a void* pointing to the next
list entry in the application - effectively passing a pointer to
confd and getting it back in the next invocation of
get_next()
.
To indicate that no more entries exist, we reply with a NULL
pointer for the v
array. The values of the
num_vals_in_key
and next
parameters are ignored in this case.
Passing the value -1
for
next
has a special meaning. It tells ConfD that
we want the next request for this list traversal to use the
find_next()
(or
find_next_object()
) callback instead of
get_next()
(or
get_next_object()
).
In the case of list traversal by means of a secondary
index, the secondary index values must be unique for entry-by-entry
traversal with
find_next()
/find_next_object()
to be possible. Thus we can not pass -1
for the
next
parameter in this case if the secondary
index values are not unique.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADTYPE
This function is used by the get_elem()
and exists_optional()
callbacks to indicate to
ConfD that a list entry or node does not exist.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS
This function is used by the
exists_optional()
callback to indicate to ConfD
that a node does exist.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS
int confd_data_reply_next_object_array( | struct confd_trans_ctx *tctx, |
const confd_value_t *v, | |
int n, | |
long next) ; |
This function is used by the optional
get_next_object()
and
find_next_object()
callbacks to return an entire
object including its keys, as well as the next
parameter that has the same function as for
confd_data_reply_next_key()
.
It combines the functions of
confd_data_reply_next_key()
and
confd_data_reply_value_array()
.
The array of confd_value_t elements must be
populated in exactly the same manner as for
confd_data_reply_value_array()
and the
long next
is used in the same manner as the
equivalent next
parameter in
confd_data_reply_next_key()
. To indicate the
end of the list we - similar to
confd_data_reply_next_key()
- pass a NULL
pointer for the value array.
If we are replying to a get_next_object()
or find_next_object()
request for an operational data list without keys (see the Operational Data chapter in the
User Guide), we must include the "pseudo" key in the array, as
the first element (i.e. preceding the actual leafs from the data
model).
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADTYPE
int confd_data_reply_next_object_tag_value_array( | struct confd_trans_ctx *tctx, |
const confd_tag_value_t *tv, | |
int n, | |
long next) ; |
This function is used by the optional
get_next_object()
and
find_next_object()
callbacks to return an entire
object including its keys, as well as the next
parameter that has the same function as for
confd_data_reply_next_key()
.
It combines the functions of
confd_data_reply_next_key()
and
confd_data_reply_tag_value_array()
.
Similar to how the
confd_data_reply_value_array()
has its
companion function
confd_data_reply_tag_value_array()
if we want
to return an object as an array of confd_tag_value_t
values instead of an array of confd_value_t values, we
can use this function instead of
confd_data_reply_next_object_array()
when we
wish to return values from the
get_next_object()
callback.
The array of confd_tag_value_t elements must be
populated in exactly the same manner as for
confd_data_reply_tag_value_array()
(except that
the key values must be included), and the long
next
is used in the same manner as the equivalent
next
parameter in
confd_data_reply_next_key()
.
The key leafs must always be given as the first
elements of the array, and in the order specified in the data model.
To indicate the end of the list we - similar to
confd_data_reply_next_key()
- pass a NULL pointer
for the value array.
If we are replying to a get_next_object()
or find_next_object()
request for an operational data list without keys (see the Operational Data chapter in the
User Guide), the "pseudo" key must be included, as the first
element in the array, with a tag value of 0 - i.e. it can be set with
code like this:
confd_tag_value_t tv[7]; CONFD_SET_TAG_INT64(&tv[0], 0, 42);
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADTYPE
int confd_data_reply_next_object_arrays( | struct confd_trans_ctx *tctx, |
const struct confd_next_object *obj, | |
int nobj, | |
int timeout_millisecs) ; |
This function is used by the optional
get_next_object()
and
find_next_object()
callbacks to return multiple
objects including their keys, in confd_value_t form.
The struct confd_next_object is defined as:
I.e. it corresponds exactly to the data provided for a call of
confd_data_reply_next_object_array()
. The
parameter obj
is a pointer to an
nobj
elements long array of such structs. We
can also pass a timeout value for ConfD's caching of the returned data
via timeout_millisecs
. If we pass 0 for this
parameter, the value configured via
/confdConfig/capi/objectCacheTimeout
in
confd.conf
(see confd.conf(5)) will be
used.
The cache in ConfD may become invalid (e.g. due to timeout)
before all the returned list entries have been used, and ConfD may
then need to issue a new callback request based on an "intermediate"
next
value. This is done exactly as for the
single-entry case, i.e. if next
is
-1
, find_next_object()
(or
find_next()
) will be used, with the keys from the
"previous" entry, otherwise get_next_object()
(or
get_next()
) will be used, with the given
next
value.
Thus a data provider can choose to give next
values that uniquely identify list entries if that is convenient, or
otherwise use -1
for all next
elements - or a combination, e.g. -1
for all but
the last entry. If any next
value is given as
-1
, at least one of the
find_next()
and
find_next_object()
callbacks must be
registered.
To indicate the end of the list we can either pass a NULL
pointer for the obj
array, or pass an array
where the last struct confd_next_object element has the
v
element set to NULL. The latter is preferable,
since we can then combine the final list entries with the end-of-list
indication in the reply to a single callback invocation.
When next
values other than
-1
are used, these must remain valid even after
the end of the list has been reached, since ConfD may still need to
issue a new callback request based on an "intermediate"
next
value as described above. They can be
discarded (e.g. allocated memory released) when a new
get_next_object()
or
find_next_object()
callback request for the same
list in the same transaction has been received, or at the end of the
transaction.
In the case of list traversal by means of a secondary
index, the secondary index values must be unique for entry-by-entry
traversal with
find_next_object()
/find_next()
to be possible. Thus we can not use -1
for the
next
element in this case if the
secondary index values are not unique.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADTYPE
int confd_data_reply_next_object_tag_value_arrays( | struct confd_trans_ctx *tctx, |
const struct confd_tag_next_object *tobj, | |
int nobj, | |
int timeout_millisecs) ; |
This function is used by the optional
get_next_object()
and
find_next_object()
callbacks to return multiple
objects including their keys, in confd_tag_value_t form.
The struct confd_tag_next_object is defined as:
I.e. it corresponds exactly to the data provided for a call of
confd_data_reply_next_object_tag_value_array()
. The
parameter tobj
is a pointer to an
nobj
elements long array of such structs. We
can also pass a timeout value for ConfD's caching of the returned data
via timeout_millisecs
. If we pass 0 for this
parameter, the value configured via
/confdConfig/capi/objectCacheTimeout
in
confd.conf
(see confd.conf(5)) will be
used.
The cache in ConfD may become invalid (e.g. due to timeout)
before all the returned list entries have been used, and ConfD may
then need to issue a new callback request based on an "intermediate"
next
value. This is done exactly as for the
single-entry case, i.e. if next
is
-1
, find_next_object()
(or
find_next()
) will be used, with the keys from the
"previous" entry, otherwise get_next_object()
(or
get_next()
) will be used, with the given
next
value.
Thus a data provider can choose to give next
values that uniquely identify list entries if that is convenient, or
otherwise use -1
for all next
elements - or a combination, e.g. -1
for all but
the last entry. If any next
value is given as
-1
, at least one of the
find_next()
and
find_next_object()
callbacks must be
registered.
To indicate the end of the list we can either pass a NULL
pointer for the tobj
array, or pass an array
where the last struct confd_tag_next_object element has the
tv
element set to NULL. The latter is preferable,
since we can then combine the final list entries with the end-of-list
indication in the reply to a single callback invocation.
When next
values other than
-1
are used, these must remain valid even after
the end of the list has been reached, since ConfD may still need to
issue a new callback request based on an "intermediate"
next
value as described above. They can be
discarded (e.g. allocated memory released) when a new
get_next_object()
or
find_next_object()
callback request for the same
list in the same transaction has been received, or at the end of the
transaction.
In the case of list traversal by means of a secondary
index, the secondary index values must be unique for entry-by-entry
traversal with
find_next_object()
/find_next()
to be possible. Thus we can not use -1
for the
next
element in this case if the
secondary index values are not unique.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADTYPE
int confd_data_reply_attrs( | struct confd_trans_ctx *tctx, |
const confd_attr_value_t *attrs, | |
int num_attrs) ; |
This function is used by the get_attrs()
callback to return the requested attribute values. The
attrs
array should be populated with
num_attrs
elements of type
confd_attr_value_t, which is defined as:
If multiple attributes were requested in the callback
invocation, they should be given in the same order in the reply as in
the request. Requested attributes that are not set should be omitted
from the array. If none of the requested attributes are set, or no
attributes at all are set when all attributes are requested,
num_attrs
should be given as 0, and the value
of attrs
is ignored.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADTYPE
This function must be used to return the equivalent of CONFD_OK when the actual callback returned CONFD_DELAYED_RESPONSE. I.e. it is appropriate for a transaction callback, a data callback for a write operation, or a validation callback, when the result is successful.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS
This function must be used to return an error when the actual
callback returned CONFD_DELAYED_RESPONSE. There are two cases where
the value of errstr
has a special
significance:
trans_lock()
This is equivalent to returning CONFD_ALREADY_LOCKED from the callback.
write_start()
or
prepare()
This is equivalent to returning CONFD_IN_USE from the callback.
In all other cases, calling
confd_delayed_reply_error()
is equivalent to
calling confd_trans_seterr()
with the
errstr
value and returning CONFD_ERR from the
callback. It is also possible to first call
confd_trans_seterr()
(for the varargs format) or
confd_trans_seterr_extended()
etc (for EXTENDED
ERROR REPORTING as described in confd_lib_lib(3)), and then
call confd_delayed_reply_error()
with NULL for
errstr
.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS
A data callback should normally complete "quickly", since
e.g. the execution of a 'show' command in the CLI may require many
data callback invocations. Thus it should be possible to set the
/confdConfig/capi/queryTimeout
in
confd.conf
(see above) such that it covers the
longest possible execution time for any data callback. In some rare
cases it may still be necessary for a data callback to have a longer
execution time, and then this function can be used to extend (or
shorten) the timeout for the current callback invocation. The timeout
is given in seconds from the point in time when the function is
called.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
This function is used by the application to set an error
string. The next transaction or data callback which returns CONFD_ERR will
have this error description attached to it. This error may
propagate to the CLI, the NETCONF manager, the Web UI or the log
files depending on the situation. We also use this function to
propagate warning messages from the validate()
callback if we are doing semantic validation in C. The
fmt
argument is a printf style format string.
void confd_trans_seterr_extended( | struct confd_trans_ctx *tctx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
const char *fmt, | |
...) ; |
This function can be used to provide more structured error information from a transaction or data callback, see the section EXTENDED ERROR REPORTING in confd_lib_lib(3).
int confd_trans_seterr_extended_info( | struct confd_trans_ctx *tctx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
confd_tag_value_t *error_info, | |
int n, | |
const char *fmt, | |
...) ; |
This function can be used to provide structured error
information in the same way as
confd_trans_seterr_extended()
, and additionally
provide contents for the NETCONF <error-info> element. See the
section EXTENDED
ERROR REPORTING in confd_lib_lib(3).
This function is used by the application to set an error
string. The next db callback function which returns CONFD_ERR will
have this error description attached to it. This error may
propagate to the CLI, the NETCONF manager, the Web UI or the log
files depending on the situation. The fmt
argument is a printf style format string.
void confd_db_seterr_extended( | struct confd_db_ctx *dbx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
const char *fmt, | |
...) ; |
This function can be used to provide more structured error information from a db callback, see the section EXTENDED ERROR REPORTING in confd_lib_lib(3).
int confd_db_seterr_extended_info( | struct confd_db_ctx *dbx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
confd_tag_value_t *error_info, | |
int n, | |
const char *fmt, | |
...) ; |
This function can be used to provide structured error
information in the same way as
confd_db_seterr_extended()
, and additionally
provide contents for the NETCONF <error-info> element. See the
section EXTENDED
ERROR REPORTING in confd_lib_lib(3).
Some of the DB callbacks registered via
confd_register_db_cb()
, e.g.
copy_running_to_startup()
, may require a longer
execution time than others, and in these cases the timeout specified
for /confdConfig/capi/newSessionTimeout
may be
insufficient. This function can then be used to extend the timeout for
the current callback invocation. The timeout is given in seconds from
the point in time when the function is called.
When the ConfD AAA tree is populated by an external data
provider (see the AAA
chapter in the User Guide), this function can be used by the
data provider to notify ConfD when there is a change to the AAA data.
I.e. it is an alternative to executing the command confd
--clear-aaa-cache. See also
maapi_aaa_reload()
in confd_lib_maapi(3).
It is possible to define DES3 and AES keys inside confd.conf. These keys are used by ConfD to encrypt data which is entered into the system which has either of the two builtin types tailf:des3-cbc-encrypted-string or tailf:aes-cfb-128-encrypted-string. See confd_types(3).
This function will copy those keys from ConfD (which reads
confd.conf) into memory in the library. The parameter
dtx
is a daemon context which is connected
through a call to confd_connect()
.
The function must be called before
confd_register_done()
is called. If this is
impractical, or if the application doesn't otherwise use a daemon
context, the equivalent function
maapi_install_crypto_keys()
may be more
convenient to use, see confd_lib_maapi(3).
NCS service callbacks are invoked in a manner similar to the
data callbacks described above, but require a registration for a
service point, specified as ncs:servicepoint
in the data
model. The init()
transaction callback must also
be registered, and must use the
confd_trans_set_fd()
function to assign a worker
socket for the transaction.
This function registers the service callbacks. The struct ncs_service_cbs is defined as:
struct ncs_name_value { char *name; char *value; };
enum ncs_service_operation { NCS_SERVICE_CREATE = 0, NCS_SERVICE_UPDATE = 1, NCS_SERVICE_DELETE = 2 };
struct ncs_service_cbs { char servicepoint[MAX_CALLPOINT_LEN]; int (*pre_modification)(struct confd_trans_ctx *tctx, enum ncs_service_operation op, confd_hkeypath_t *kp, struct ncs_name_value *proplist, int num_props); int (*pre_lock_create)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, struct ncs_name_value *proplist, int num_props, int fastmap_thandle); int (*create)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, struct ncs_name_value *proplist, int num_props, int fastmap_thandle); int (*post_modification)(struct confd_trans_ctx *tctx, enum ncs_service_operation op, confd_hkeypath_t *kp, struct ncs_name_value *proplist, int num_props); void *cb_opaque; /* private user data */ };
The create()
callback is invoked inside NCS
FASTMAP when creation or update of a service instance is committed.
It should attach to the FASTMAP transaction by means of
maapi_attach2()
(see confd_lib_maapi(3)),
passing the fastmap_thandle
transaction handle
as the thandle
parameter to
maapi_attach2()
. The usid
parameter for maapi_attach2()
should be given as
0. To modify data in the FASTMAP transaction, the NCS-specific
maapi_shared_xxx()
functions must be used, see
the section NCS SPECIFIC
FUNCTIONS in the confd_lib_maapi(3) manual
page.
The pre_lock_create()
callback is invoked
in the same way as the create()
callback. The
difference is that this callback is invoked outside the transaction
lock of the current transaction, and may thus run in parallel with
pre_lock_create()
invocations in other
transactions.
A service can only register one of the two
functions create()
and
pre_lock_create()
The pre_modification()
and
post_modification()
callbacks are optional, and
are invoked outside FASTMAP. pre_modification()
is invoked before create, update, or delete of the service, as
indicated by the enum ncs_service_operation op
parameter. Conversely post_modification()
is
invoked after create, update, or delete of the service. These
functions can be useful e.g. for allocations that should be stored and
existing also when the service instance is removed.
All the callbacks receive a property list via the
proplist
and num_props
parameters. This list is initially empty
(proplist
== NULL and
num_props
== 0), but it can be used to store
and later modify persistent data outside the service model that might
be needed.
We must call the
confd_register_done()
function when we are done
with all registrations for a daemon, see above.
int ncs_service_reply_proplist( | struct confd_trans_ctx *tctx, |
const struct ncs_name_value *proplist, | |
int num_props) ; |
This function must be called with the new property list,
immediately prior to returning from the callback, if the stored
property list should be updated. If a callback returns without calling
ncs_service_reply_proplist()
, the previous
property list is retained. To completely delete the property list,
call this function with the num_props
parameter
given as 0.
This library also supports the registration of callback functions on validation points in the data model. A validation point is a point in the data model where ConfD will invoke an external function to validate the associated data. The validation occurs before a transaction is committed. Similar to the state machine described for "external data bases" above where we install callback functions in the struct confd_trans_cbs, we have to install callback functions for each validation point. It does not matter if the database is CDB or an external database, the validation callbacks described here work equally well for both cases.
void confd_register_trans_validate_cb( | struct confd_daemon_ctx *dx, |
const struct confd_trans_validate_cbs *vcbs) ; |
This function installs two callback functions for the
struct confd_daemon_ctx. One function that
gets called when the validation phase starts in a transaction and
one when the validation phase stops in a transaction. In the
init()
callback we can use the MAAPI api to
attach to the running transaction, this way we can later on, freely
traverse the configuration and read data. The data we will be
reading through MAAPI (see confd_lib_maapi(3))
will be read from the shadow storage containing the
not-yet-committed data.
The struct confd_trans_validate_cbs is defined as:
struct confd_trans_validate_cbs { int (*init)(struct confd_trans_ctx *tctx); int (*stop)(struct confd_trans_ctx *tctx); };
It must thus be populated with two function pointers when we call this function.
The init()
callback is conceptually
invoked at the start of the validation phase, but just as for
transaction callbacks, ConfD will as far as possible delay the
actual invocation of the validation init()
callback for a given daemon until it is required. This means that
if none of the daemon's validate()
callbacks
need to be invoked (see below), init()
and
stop()
will not be invoked either.
If we need to allocate memory or other resources for the
validation this can also be done in the init()
callback, with the resources being freed in the
stop()
callback. We can use the
t_opaque
element in the struct
confd_trans_ctx to manage this, but in a daemon that
implements both data and validation callbacks it is better to use
the v_opaque
element for validation, to be able
to manage the allocations independently.
Similar to the init()
callback for
external data bases, we must in the init()
callback associate a file descriptor with the transaction. This
file descriptor will be used for the actual validation. Thus in a
multi threaded application, we can have one thread performing
validation for a transaction in parallel with other threads
executing e.g. data callbacks. Thus a typical implementation of an
init()
callback for validation looks as:
static int init_validation(struct confd_trans_ctx *tctx) { maapi_attach(maapi_socket, mtest__ns, tctx); confd_trans_set_fd(tctx, workersock); return CONFD_OK; }
We must also install an actual validation function for each
validation point, i.e. for each tailf:validate
statement
in the YANG data model.
A validation point has a name and an associated function pointer. The struct which must be populated for each validation point looks like:
struct confd_valpoint_cb { char valpoint[MAX_CALLPOINT_LEN]; int (*validate)(struct confd_trans_ctx *tctx, confd_hkeypath_t *kp, confd_value_t *newval); void *cb_opaque; /* private user data */ };
We must call the
confd_register_done()
function when we are done
with all registrations for a daemon, see above.
See the user guide chapter "Semantic validation" for code
examples. The validate()
callback can return
CONFD_OK if all is well, or CONFD_ERROR if the validation fails. If we
wish a message to accompany the error we must prior to returning
from the callback, call confd_trans_seterr()
or
confd_trans_seterr_extended()
.
The cb_opaque
element can be used to pass
arbitrary data to the callback, e.g. when the same callback is used
for multiple validation points. It is made available to the callback
via the element vcb_opaque
in the transaction
context (tctx
argument), see the structure
definition above.
If the tailf:opaque
substatement has been used with
the tailf:validate
statement in the data model, the
argument string is made available to the callback via the
validate_opaque
element in the transaction
context.
We also have yet another special return value which can be
used (only) from the validate()
callback which
is CONFD_VALIDATION_WARN. Prior to return of this value we must call
confd_trans_seterr()
which provides a string
describing the warning. The warnings will get propagated to the
transaction engine, and depending on where the transaction
originates, ConfD may or may not act on the warnings. If the
transaction originates from the CLI or the Web UI, ConfD will
interactively present the user with a choice - whereby the
transaction can be aborted.
If the transaction originates from NETCONF - which does not have any interactive capabilities - the warnings are ignored. The warnings are primarily intended to alert inexperienced users that attempt to make - dangerous - configuration changes. There can be multiple warnings from multiple validation points in the same transaction.
It is also possible to let the validate()
callback return CONFD_DELAYED_RESPONSE in which case the application
at a later stage must invoke either
confd_delayed_reply_ok()
,
confd_delayed_reply_error()
or
confd_delayed_reply_validation_warn()
.
In some cases it may be necessary for the validation callbacks
to verify the availability of resources that will be needed if the
new configuration is committed. To support this kind of
verification, the validation_info
element in
the struct confd_trans_ctx can carry one of
these flags:
When this flag is set, the current validation phase is a
"test" validation, as in e.g. the CLI 'validate' command, and
the transaction will return to the READ state regardless of
the validation result. This flag is available in all of the
init()
, validate()
,
and stop()
callbacks.
When this flag is set, all requirements for a commit
have been met, i.e. all validation as well as the write_start
and prepare transitions have been successful, and the actual
commit will follow. This flag is only available in the
stop()
callback.
int confd_register_range_valpoint_cb( | struct confd_daemon_ctx *dx, |
struct confd_valpoint_cb *vcb, | |
const confd_value_t *lower, | |
const confd_value_t *upper, | |
int numkeys, | |
const char *fmt, | |
...) ; |
A variant of confd_register_valpoint_cb()
which registers a validation function for a range of key values. The
lower
, upper
,
numkeys
, fmt
, and
remaining parameters are the same as for
confd_register_range_data_cb()
, see above.
This function must be used to return the equivalent of
CONFD_VALIDATION_WARN when the validate()
callback returned CONFD_DELAYED_RESPONSE. Before calling this
function, we must call
confd_trans_seterr()
to provide a string
describing the warning.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_MALLOC, CONFD_ERR_OS
The application can generate notifications that are sent via the northbound protocols. Currently NETCONF notification streams are supported. The application generates the content for each notification and sends it via a socket to ConfD, which in turn manages the stream subscriptions and distributes the notifications accordingly.
A stream always has a "live feed", which is the sequence of new notifications, sent in real time as they are generated. Subscribers may also request "replay" of older, logged notifications if the stream supports this, perhaps transitioning to the live feed when the end of the log is reached. There may be one or more replays active simultaneously with the live feed. ConfD forwards replay requests from subscribers to the application via callbacks if the stream supports replay.
Each notification has an associated time stamp, the "event time". This is the time when the event that generated the notification occurred, rather than the time the notification is logged or sent, in case these times differ. The application must pass the event time to ConfD when sending a notification, and it is also needed when replaying logged events, see below.
int confd_register_notification_stream( | struct confd_daemon_ctx *dx, |
const struct confd_notification_stream_cbs *ncbs, | |
struct confd_notification_ctx **nctx) ; |
This function registers the notification stream and optionally
two callback functions used for the replay functionality. If the
stream does not support replay, the callback elements in the
struct confd_notification_stream_cbs are set to
NULL. A context pointer is returned via the
**nctx
argument - this must be used by the
application for the sending of live notifications via
confd_notification_send()
(see below).
The confd_notification_stream_cbs structure is defined as:
struct confd_notification_stream_cbs { char streamname[MAX_STREAMNAME_LEN]; int fd; int (*get_log_times)( struct confd_notification_ctx *nctx); int (*replay)(struct confd_notification_ctx *nctx, struct confd_datetime *start, struct confd_datetime *stop); void *cb_opaque; /* private user data */ };
The fd
element must be set to a
previously connected worker socket. This socket may be used for
multiple notification streams, but not for any of the callback
processing described above. Since it is only used for sending data
to ConfD, there is no need for the application to poll the
socket. Note that the control socket must be connected before
registration even if the callbacks are not registered.
We must call the
confd_register_done()
function when we are done
with all registrations for a daemon, see above.
The get_log_times()
callback is called by
ConfD to find out a) the creation time of the current log and b) the
event time of the last notification aged out of the log, if any.
The application provides the times via the
confd_notification_reply_log_times()
function
(see below) and returns CONFD_OK.
The replay()
callback is called by ConfD
to request replay. The nctx
context pointer
must be saved by the application and used when sending the replay
notifications via confd_notification_send()
, as
well as for the
confd_notification_replay_complete()
(or
confd_notification_replay_failed()
) call (see
below) - the callback should return without waiting for the replay
to complete. The pointer references allocated memory, which is
freed by the
confd_notification_replay_complete()
(or
confd_notification_replay_failed()
)
call.
The times given by *start
and
*stop
specify the extent of the replay. The
start time will always be given and specify a time in the past,
however the stop time may be either in the past or in the future or
even omitted, i.e. the stop
argument is
NULL. This means that the subscriber has requested that the
subscription continues indefinitely with the live feed when the
logged notifications have been sent.
If the stop time is given:
The application sends all logged notifications
that have an event time later than the start time but not later
than the stop time, and then calls
confd_notification_replay_complete()
. Note
that if the stop time is in the future when the replay request
arrives, this includes notifications logged while the replay is in
progress (if any), as long as their event time is not later than
the stop time.
If the stop time is not given:
The application sends all logged notifications
that have an event time later than the start time, and then calls
confd_notification_replay_complete()
. Note
that this includes notifications logged after the request was
received (if any).
ConfD will if needed switch the subscriber over to the live
feed and then end the subscription when the stop time is
reached. The callback may analyze the start
and
stop
arguments to determine start and stop
positions in the log, but if the analysis is postponed until after
the callback has returned, the confd_datetime
structure(s) must be copied by the callback.
The replay()
callback may optionally
select a separate worker socket to be used for the replay
notifications. In this case it must call
confd_notification_set_fd()
to indicate which
socket should be used.
Note that unlike the callbacks for external data bases and
validation, these callbacks do not use a worker socket for the
callback processing, and consequently there is no
init()
callback to request one. The callbacks
are invoked, and the reply is sent, via the daemon control
socket.
The cb_opaque
element in the
confd_notification_stream_cbs structure can be
used to pass arbitrary data to the callbacks in much the same way as
for callpoint and validation point registrations, see the
description of the struct confd_data_cbs
structure above. However since the callbacks are not associated with
a transaction, this element is instead made available in the
confd_notification_ctx structure.
int confd_notification_send( | struct confd_notification_ctx *nctx, |
struct confd_datetime *time, | |
confd_tag_value_t *values, | |
int nvalues) ; |
This function is called by the application to send a
notification, whether "live" or replay. The
nctx
pointer is provided by ConfD as described
above. The time
argument specifies the event
time for the notification. The values
argument
is an array of length nvalues
, populated with
the content of the notification as described for the Tagged Value
Array format in the XML STRUCTURES
section of the confd_types(3) manual
page.
For example, a NETCONF notification of the form
<ncn:notification xmlns:ncn="urn:ietf:params:xml:ns:netconf:notification:1.0"> <linkUp xmlns="http://example.com/ns/test/1.0"> <ncn:eventTime>2007-08-17T08:56:05Z</ncn:eventTime> <ifIndex>3</ifIndex> </linkUp> </ncn:notification>
could be sent with the following code:
struct confd_notification_ctx *nctx; struct confd_datetime event_time = {2007, 8, 17, 8, 56, 5, 0, 0, 0}; confd_tag_value_t notif[3]; int n = 0; CONFD_SET_TAG_XMLBEGIN(¬if[n], test_linkUp, test__ns); n++; CONFD_SET_TAG_UINT32(¬if[n], test_ifIndex, 3); n++; CONFD_SET_TAG_XMLEND(¬if[n], test_linkUp, test__ns); n++; confd_notification_send(nctx, &event_time, notif, n);
While it is possible to use separate threads to send live and replay notifications for a given stream, or to send different streams on a given worker socket, this is not recommended. This is because it involves rather complex synchronization problems that can only be fully solved by the application, in particular in the case where a replay switches over to the live feed.
The application calls this function to notify ConfD that the
replay is complete, using the nctx
pointer
received in the corresponding replay()
callback
invocation.
In case the application fails to complete the replay as
requested (e.g. the log gets overwritten while the replay is in
progress), the application should call this function
instead of
confd_notification_replay_complete()
. An error
message describing the reason for the failure can be supplied by
first calling confd_notification_seterr()
or
confd_notification_seterr_extended()
, see
below. The nctx
pointer received in the
corresponding replay()
callback invocation is
used for both calls.
This function may optionally be called by the
replay()
callback to request that the worker
socket given by fd
should be used for the
replay. Otherwise the socket specified in the
confd_notification_stream_cbs at registration
will be used.
int confd_notification_reply_log_times( | struct confd_notification_ctx *nctx, |
struct confd_datetime *creation, | |
struct confd_datetime *aged) ; |
Reply function for use in the
get_log_times()
callback invocation. If no
notifications have been aged out of the log, give NULL for the
aged
argument.
In some cases the callbacks may be unable to carry out the
requested actions, e.g. the capacity for simultaneous replays might
be exceeded, and they can then return CONFD_ERR. This function
allows the callback to associate an error message with the
failure. It can also be used to supply an error message before
calling confd_notification_replay_failed()
.
void confd_notification_seterr_extended( | struct confd_notification_ctx *nctx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
const char *fmt, | |
...) ; |
This function can be used to provide more structured error information from a notification callback, see the section EXTENDED ERROR REPORTING in confd_lib_lib(3).
int confd_notification_seterr_extended_info( | struct confd_notification_ctx *nctx, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
confd_tag_value_t *error_info, | |
int n, | |
const char *fmt, | |
...) ; |
This function can be used to provide structured error
information in the same way as
confd_notification_seterr_extended()
, and additionally
provide contents for the NETCONF <error-info> element. See the
section EXTENDED
ERROR REPORTING in confd_lib_lib(3).
int confd_register_snmp_notification( | struct confd_daemon_ctx *dx, |
int fd, | |
const char *notify_name, | |
const char *ctx_name, | |
struct confd_notification_ctx **nctx) ; |
SNMP notifications can also be sent via the notification
framework, however most aspects of the stream concept described
above do not apply for SNMP. This function is used to register a
worker socket, the snmpNotifyName
(notify_name
), and SNMP context
(ctx_name
) to be used for the
notifications.
The fd
parameter must give a previously
connected worker socket. This socket may be used for different
notifications, but not for any of the callback processing described
above. Since it is only used for sending data to ConfD, there is no
need for the application to poll the socket. Note that the control
socket must be connected before registration, even if none of the
callbacks described below are registered.
The context pointer returned via the
**nctx
argument must be used by the application
for the subsequent sending of the notifications via
confd_notification_send_snmp()
or
confd_notification_send_snmp_inform()
(see
below).
When a notification is sent using one of these functions, it
is delivered to the management targets defined for the
snmpNotifyName
in the
snmpNotifyTable
in SNMP-NOTIFICATION-MIB for
the specified SNMP context. If notify_name
is
NULL or the empty string (""), the notification is sent to all
management targets. If ctx_name
is NULL or the
empty string (""), the default context ("") is used.
We must call the
confd_register_done()
function when we are done
with all registrations for a daemon, see above.
int confd_notification_send_snmp( | struct confd_notification_ctx *nctx, |
const char *notification, | |
struct confd_snmp_varbind *varbinds, | |
int num_vars) ; |
Sends the SNMP notification specified by
notification
, without requesting inform-request
delivery information. This is equivalent to calling
confd_notification_send_snmp_inform()
(see
below) with NULL as the cb_id
argument. I.e. if
the common arguments are the same, the two functions will send the
exact same set of traps and inform-requests.
int confd_register_notification_snmp_inform_cb( | struct confd_daemon_ctx *dx, |
const struct confd_notification_snmp_inform_cbs *cb) ; |
If we want to receive information about the delivery of SNMP inform-requests, we must register two callbacks for this. The struct confd_notification_snmp_inform_cbs is defined as:
struct confd_notification_snmp_inform_cbs { char cb_id[MAX_CALLPOINT_LEN]; void (*targets)(struct confd_notification_ctx *nctx, int ref, struct confd_snmp_target *targets, int num_targets); void (*result)(struct confd_notification_ctx *nctx, int ref, struct confd_snmp_target *target, int got_response); void *cb_opaque; /* private user data */ };
The callback identifier cb_id
can be
chosen arbitrarily, it is only used when sending SNMP notifications
with confd_notification_send_snmp_inform()
-
however each inform callback registration must use a unique
cb_id
. The callbacks are invoked via the
control socket, i.e. the application must poll it and invoke
confd_fd_ready()
when data is available.
When a notification is sent, the target()
callback will be invoked once with num_targets
(possibly 0) inform-request targets in the
targets
array, followed by
num_targets
invocations of the
result()
callback, one for each target. The
ref
argument (passed from the
confd_notification_send_snmp_inform()
call)
allows for tracking the result of multiple notifications with
delivery overlap.
We must call the
confd_register_done()
function when we are done
with all registrations for a daemon, see above.
int confd_notification_send_snmp_inform( | struct confd_notification_ctx *nctx, |
const char *notification, | |
struct confd_snmp_varbind *varbinds, | |
int num_vars, | |
const char *cb_id, | |
int ref) ; |
Sends the SNMP notification specified by
notification
. If cb_id
is
not NULL, the callbacks registered for cb_id
will be invoked with the ref
argument as
described above, otherwise no inform-request delivery information
will be provided. The varbinds
array should be
populated with num_vars
elements as described
in the Notifications section of the SNMP Agent chapter in the User
Guide.
If notification
is the empty string, no
notification is looked up; instead varbinds
defines the notification, including the notification id (variable
name "snmpTrapOID"). This is especially useful for forwarding a
notification which has been received from the SNMP gateway (see
confd_register_notification_sub_snmp_cb()
below).
If varbinds
does not contain a timestamp
(variable name "sysUpTime"), one will be supplied by the
agent.
void confd_notification_set_snmp_src_addr( | struct confd_notification_ctx *nctx, |
const struct confd_ip *src_addr) ; |
By default, the source address for the SNMP notifications that
are sent by the above functions is chosen by the IP stack of the OS.
This function may be used to select a specific source address, given
by src_addr
, for the SNMP notifications
subsequently sent using the nctx
context. The
default can be restored by calling the function with a
src_addr
where the af
element is set to AF_UNSPEC
.
int confd_notification_set_snmp_notify_name( | struct confd_notification_ctx *nctx, |
const char *notify_name) ; |
This function can be used to change the snmpNotifyName
(notify_name
) for the
nctx
context. The new snmpNotifyName is used
for notifications sent by subsequent calls to
confd_notification_send_snmp()
and
confd_notification_send_snmp_inform()
that use
the nctx
context.
int confd_register_notification_sub_snmp_cb( | struct confd_daemon_ctx *dx, |
const struct confd_notification_sub_snmp_cb *cb) ; |
Registers a callback function to be called when an SNMP notification is received by the SNMP gateway.
The struct confd_notification_sub_snmp_cb is defined as:
struct confd_notification_sub_snmp_cb { char sub_id[MAX_CALLPOINT_LEN]; int (*recv)(struct confd_notification_ctx *nctx, char *notification, struct confd_snmp_varbind *varbinds, int num_vars, confd_value_t *src_addr, u_int16_t src_port); void *cb_opaque; /* private user data */ };
The sub_id
element is the subscription id
for the notifications. The recv()
callback
will be called when a notification is received. See the section
"Receiving and Forwarding Traps" in the chapter "The SNMP gateway"
in the Users Guide.
We must call the
confd_register_done()
function when we are done
with all registrations for a daemon, see above.
Notifications are sent asynchronously, i.e. normally without
blocking the caller of the send functions described above. This means
that in some cases, ConfD's sending of the notifications on the
northbound interfaces may lag behind the send calls. If we want to
make sure that the notifications have actually been sent out, e.g. in
some shutdown procedure, we can call
confd_notification_flush()
. This function will
block until all notifications sent using the given
nctx
context have been fully processed by
ConfD. It can be used both for notification streams and for SNMP
notifications (however it will not wait for replies to SNMP
inform-requests to arrive).
The use of action callbacks can be specified either via a
rpc
statement or via a tailf:action
statement in the YANG data model, see the YANG specification and tailf_yang_extensions(5).
In both cases the use of a tailf:actionpoint
statement
specifies that the action is implemented as a callback function.
This section describes
how such callback functions should be implemented and registered
with ConfD.
Unlike the callbacks for data and validation, there is not always a transaction associated with an action callback. However an action is always associated with a user session (NETCONF, CLI, etc), and only one action at a time can be invoked from a given user session. Hence a pointer to the associated struct confd_user_info is passed to the callbacks.
The action callback mechanism is also used for command and completion callbacks configured for the CLI, either in a YANG module using tailf extension statements, or in a clispec(5). As the parameter structure is significantly different, special callbacks are used for these functions.
This function registers up to five callback functions, two of which will be called in sequence when an action is invoked. The struct confd_action_cbs is defined as:
struct confd_action_cbs { char actionpoint[MAX_CALLPOINT_LEN]; int (*init)(struct confd_user_info *uinfo); int (*abort)(struct confd_user_info *uinfo); int (*action)(struct confd_user_info *uinfo, struct xml_tag *name, confd_hkeypath_t *kp, confd_tag_value_t *params, int nparams); int (*command)(struct confd_user_info *uinfo, char *path, int argc, char **argv); int (*completion)(struct confd_user_info *uinfo, int cli_style, char *token, int completion_char, confd_hkeypath_t *kp, char *cmdpath, char *cmdparam_id, struct confd_qname *simpleType, char *extra); void *cb_opaque; /* private user data */ };
The init()
callback, and at least one of
the action()
, command()
,
and completion()
callbacks, must be specified.
It is in principle possible to use a single "point name" for more
than one of these callback types, and have the corresponding
callback invoked in each case, but in typical usage we would only
register one of the callbacks action()
,
command()
, and
completion()
. Below, the term "action
callback" is used to refer to any of these three.
Similar to the init()
callback for
external data bases, we must in the init()
callback associate a worker socket with the action. This socket
will be used for the invocation of the action callback, which
actually carries out the action. Thus in a multi threaded
application, actions can be dispatched to different threads.
However note that unlike the callbacks for external data bases
and validation, both init()
and action
callbacks are registered for each action point (i.e. different
action points can have different init()
callbacks), and there is no finish()
callback -
the action is completed when the action callback returns.
The struct confd_action_ctx actx element inside the
struct confd_user_info holds action-specific data, in
particular the t_opaque
element could be used to
pass data from the init()
callback to the action
callback, if needed. If the action is associated with a transaction,
the thandle
element is set to the transaction
handle, and can be used with a call to
maapi_attach2()
(see confd_lib_maapi(3)). This
is the case for all types of action callbacks invoked from the CLI and
Web UI, and for the action() callback when invoked via
maapi_request_action_th()
(see confd_lib_maapi(3)) - in
other cases, this element is -1.
The cb_opaque
element in the
confd_action_cbs structure can be used to pass
arbitrary data to the callbacks in much the same way as for
callpoint and validation point registrations, see the description of
the struct confd_data_cbs structure above.
This element is made available in the
confd_action_ctx structure.
If the tailf:opaque
substatement has been used with
the tailf:actionpoint
statement in the data model, the
argument string is made available to the callbacks via the
actionpoint_opaque
element in the
confd_action_ctx structure.
We must call the
confd_register_done()
function when we are done
with all registrations for a daemon, see above.
The action()
callback receives all the
parameters pertaining to the action: The name
argument is a pointer to the action name as defined in the data model,
the kp
argument gives the path
through the data model for an action defined via
tailf:action
(it is a NULL pointer for an action defined
via rpc
), and finally the
params
argument is a representation of the
inout parameters provided when the action is invoked.
The params
argument is an array of length nparams
,
populated as described for the Tagged Value Array format in the
XML
STRUCTURES section of the confd_types(3) manual
page.
The command()
callback is invoked for CLI
callback commands. It must always result in a call of
confd_action_reply_command()
. As the
parameters in this case are all in string form, they are passed in
the traditional Unix argc
,
argv
manner -
i.e. argv
is an array of
argc
pointers to NUL-terminated strings plus
a final NULL pointer element, and argv[0]
is
the name of the command. Additionally the full path of the command
is available via the path
argument.
The completion()
callback is invoked for
CLI completion and information. It must result in a call of
confd_action_reply_completion()
, except for the
case when the callback is invoked via a
tailf:cli-custom-range-enumerator
statement in the data
model (see below). The
cli_style
argument gives the style of the CLI
session as a character: 'J', 'C', or 'I'. The
token
argument is a NUL-terminated string
giving the parameter of the CLI command line that the callback
invocation pertains to, and completion_char
is the character that the user typed, i.e. TAB ('\t'), SPACE (' '),
or '?'. If the callback pertains to a data model element,
kp
identifies that element, otherwise it is
NULL. The cmdpath
is a NUL-terminated string
giving the full path of the command. If a
cli-completion-id
is specified in the YANG module, or a
completionId
is specified in the clispec, it is given
as a NUL-terminated string via cmdparam_id
,
otherwise this argument is NULL. If the invocation pertains to an
element that has a type definition, the
simpleType
argument identifies the type with
namespace and type name, otherwise it is NULL. The
extra
argument is currently unused (always
NULL).
When completion()
is invoked via a
tailf:cli-custom-range-enumerator
statement in the data
model, it is a request to provide possible key values for creation of
an entry in a list with a custom range specification. The callback
must in this case result in a call of
confd_action_reply_range_enum()
. Refer to the
cli/range_create
example in the bundled examples
collection to see an implementation of such a callback.
The action callbacks must return CONFD_OK, CONFD_ERR, or CONFD_DELAYED_RESPONSE. CONFD_DELAYED_RESPONSE implies that the application must later reply asynchronously.
The optional abort()
callback is called
whenever an action is aborted, e.g. when a user invokes an action
from one of the northbound agents and aborts it before it has
completed. The abort()
callback will be
invoked on the control socket. It is the responsibility of the
abort()
callback to make sure that the pending
reply from the action callback is sent. This is required to allow
the worker socket to be used for further queries. There are several
possible ways for an application to support aborting. E.g. the
application can return CONFD_DELAYED_RESPONSE from the action
callback. Then, when the abort()
callback is
called, it can terminate the executing action and use
e.g. confd_action_delayed_reply_error()
.
Alternatively an application can use threads where the action
callback is executed in a separate thread. In this case the
abort()
callback could inform the thread
executing the action that it should be terminated, and that thread
can just return from the action callback.
int confd_register_range_action_cbs( | struct confd_daemon_ctx *dx, |
const struct confd_action_cbs *acb, | |
const confd_value_t *lower, | |
const confd_value_t *upper, | |
int numkeys, | |
const char *fmt, | |
...) ; |
A variant of confd_register_action_cbs()
which registers action callbacks for a range of key values. The
lower
, upper
,
numkeys
, fmt
, and
remaining parameters are the same as for
confd_register_range_data_cb()
, see above.
This function can not be used for registration of the
command()
or completion()
callbacks - only actions specified in the data model are invoked via a
keypath that can be used for selection of the corresponding
callbacks.
Associate a worker socket with the action. This function must
be called in the init()
callback - a typical
implementation of an init()
callback looks
as:
static int init_action(struct confd_user_info *uinfo) { confd_action_set_fd(uinfo, workersock); return CONFD_OK; }
int confd_action_reply_values( | struct confd_user_info *uinfo, |
confd_tag_value_t *values, | |
int nvalues) ; |
If the action definition specifies that the action should
return data, it must invoke this function in response to the
action()
callback. The
values
argument points to an array of length
nvalues
, populated with the output parameters
in the same way as the
params
array above.
This function must only be called for an
action()
callback.
If a CLI callback command should return data, it must invoke
this function in response to the command()
callback. The values
argument points to an
array of length nvalues
, populated with
pointers to NUL-terminated strings.
This function must only be called for a
command()
callback.
int confd_action_reply_rewrite( | struct confd_user_info *uinfo, |
char **values, | |
int nvalues, | |
char **unhides, | |
int nunhides) ; |
This function can be called instead of
confd_action_reply_command()
as a response to a
show path rewrite callback invocation. The values
argument points to an
array of length nvalues
, populated with
pointers to NUL-terminated strings representing the tokens of the
new path. The unhides
argument points to an
array of length nunhides
, populated with
pointers to NUL-terminated strings representing hide groups to
temporarily unhide during evaluation of the show command.
This function must only be called for a
command()
callback.
int confd_action_reply_rewrite2( | struct confd_user_info *uinfo, |
char **values, | |
int nvalues, | |
char **unhides, | |
int nunhides, | |
struct confd_rewrite_select **selects, | |
int nselects) ; |
This function can be called instead of
confd_action_reply_command()
as a response to a
show path rewrite callback invocation. The
values
argument points to an array of length
nvalues
, populated with pointers to
NUL-terminated strings representing the tokens of the new path.
The unhides
argument points to an array of
length nunhides
, populated with pointers to
NUL-terminated strings representing hide groups to temporarily
unhide during evaluation of the show command.
The selects
argument points to an array of
length nselects
, populated with pointers
to confd_rewrite_select structs representing additional select
targets.
This function must only be called for a
command()
callback.
int confd_action_reply_completion( | struct confd_user_info *uinfo, |
struct confd_completion_value *values, | |
int nvalues) ; |
This function must normally be called in response to the
completion()
callback. The
values
argument points to an
nvalues
long array of
confd_completion_value elements:
enum confd_completion_type { CONFD_COMPLETION, CONFD_COMPLETION_INFO, CONFD_COMPLETION_DESC, CONFD_COMPLETION_DEFAULT };
struct confd_completion_value { enum confd_completion_type type; char *value; char *extra; };
For a completion alternative, type
is set
to CONFD_COMPLETION, value
gives the
alternative as a NUL-terminated string, and
extra
gives explanatory text as a
NUL-terminated string - if there is no such text,
extra
is set to NULL. For "info" or "desc"
elements, type
is set to CONFD_COMPLETION_INFO
or CONFD_COMPLETION_DESC, respectively, and
value
gives the text as a NUL-terminated string
(the extra
element is ignored).
In order to fallback to the normal completion behavior,
type
should be set to
CONFD_COMPLETION_DEFAULT. CONFD_COMPLETION_DEFAULT cannot be
combined with the other completion types, implying the
values
array always must have length
1
which is indicated by
nvalues
setting.
This function must only be called for a
completion()
callback.
int confd_action_reply_range_enum( | struct confd_user_info *uinfo, |
char **values, | |
int keysize, | |
int nkeys) ; |
This function must be called in response to the
completion()
callback when it is invoked via a
tailf:cli-custom-range-enumerator
statement in the data
model. The values
argument points to a
keysize
*
nkeys
long array of strings giving the possible
key values, where keysize
is the number of keys
for the list in the data model and nkeys
is the
number of list entries for which keys are provided. I.e. the array
gives entry1-key1, entry1-key2, ..., entry2-key1, entry2-key2, ... and
so on. See the cli/range_create
example in the
bundled examples collection for details.
This function must only be called for a
completion()
callback.
If action callback encounters fatal problems that can not be expressed via the reply function, it may call this function with an appropriate message and return CONFD_ERR instead of CONFD_OK.
void confd_action_seterr_extended( | struct confd_user_info *uinfo, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
const char *fmt, | |
...) ; |
This function can be used to provide more structured error information from an action callback, see the section EXTENDED ERROR REPORTING in confd_lib_lib(3).
int confd_action_seterr_extended_info( | struct confd_user_info *uinfo, |
enum confd_errcode code, | |
u_int32_t apptag_ns, | |
u_int32_t apptag_tag, | |
confd_tag_value_t *error_info, | |
int n, | |
const char *fmt, | |
...) ; |
This function can be used to provide structured error
information in the same way as
confd_action_seterr_extended()
, and additionally
provide contents for the NETCONF <error-info> element. See the
section EXTENDED
ERROR REPORTING in confd_lib_lib(3).
If we use the CONFD_DELAYED_RESPONSE as a return value from
the action callback, we must later asynchronously reply. If we use
one of the confd_action_reply_xxx()
functions,
this is a complete reply. Otherwise we must use the
confd_action_delayed_reply_ok()
function to
signal success, or the
confd_action_delayed_reply_error()
function
to signal an error.
Some action callbacks may require a significantly longer
execution time than others, and this time may not even be possible to
determine statically (e.g. a file download). In such cases the
/confdConfig/capi/queryTimeout
setting in
confd.conf
(see above) may be insufficient, and
this function can be used to extend (or shorten) the timeout for the
current callback invocation. The timeout is given in seconds from the
point in time when the function is called.
Examples on how to work with actions are available in the User Guide and in the bundled examples collection.
We can register a callback with ConfD's AAA subsystem, to
be invoked whenever AAA has completed processing of an
authentication attempt. In the case where the authentication was
otherwise successful, the callback can still cause it to be rejected.
This can be used to implement specific access policies, as an
alternative to using PAM or "External" authentication for this
purpose. The callback will only be invoked if it is both enabled via
/confdConfig/aaa/authenticationCallback/enabled
in
confd.conf
(see confd.conf(5)) and registered as
described here.
If the callback is enabled in
confd.conf
but not registered, or invocation
keeps failing for some reason, all authentication
attempts will fail.
This callback can not be used to actually perform the authentication. If we want to implement the authentication outside of ConfD, we need to use PAM or "External" authentication, see the AAA chapter in the User Guide.
Registers the authentication callback. The struct confd_auth_cb is defined as:
The auth()
callback is invoked with a
pointer to an authentication context that provides information about
the result of the authentication so far. The callback must return
CONFD_OK or CONFD_ERR, see below. The struct
confd_auth_ctx is defined as:
struct confd_auth_ctx { struct confd_user_info *uinfo; char *method; int success; union { struct { /* if success */ int ngroups; char **groups; } succ; struct { /* if !success */ int logno; /* number from confd_logsyms.h */ char *reason; } fail; } ainfo; /* ConfD internal fields */ char *errstr; };
The uinfo
element points to a struct
confd_user_info with details about the user logging in,
specifically user name, password (if used), source IP address,
context, and protocol. Note that the user session does not actually
exist at this point, even if the AAA authentication was successful - it
will only be created if the callback accepts the authentication, hence
e.g. the usid
element is always 0.
The method
string gives the authentication
method used, as follows:
Password authentication. This generic term is used if the authentication failed.
Password authentication. On successful authentication, the specific method that succeeded is given. See the AAA chapter in the User Guide for an explanation of these methods.
Public key authentication via the internal SSH server.
Authentication with an unknown or unsupported method with this name was attempted via the internal SSH server.
If success
is non-zero, the AAA
authentication succeeded, and groups
is an array of
length ngroups
that gives the groups that will be
assigned to the user at login. If the callback returns CONFD_OK, the
complete authentication succeeds and the user is logged in. If it
returns CONFD_ERR (or an invalid return value), the authentication
fails.
If success
is zero, the AAA authentication
failed, with the reason given by logno
(one of
CONFD_BAD_LOCAL_PASS
,
CONFD_NO_SUCH_LOCAL_USER
, or
CONFD_SSH_NO_LOGIN
) and the explanatory string
reason
. This invocation is only for informational
purposes - the callback return value has no effect on the
authentication, and should normally be CONFD_OK.
This function can be used to provide a text message when the callback returns CONFD_ERR. If used when rejecting a successful authentication, the message will be logged in ConfD's audit log (otherwise a generic "rejected by application callback" message is logged).
We can register two authorization callbacks with ConfD's AAA subsystem. These will be invoked when the northbound agents check that a command or a data access is allowed by the AAA access rules. The callbacks can partially or completely replace the access checks done within the AAA subsystem, and they may accept or reject the access. Typically many access checks are done during the processing of commands etc, and using these callbacks can thus have a significant performance impact. Unless it is a requirement to query an external authorization mechanism, it is far better to only configure access rules in the AAA data model (see the AAA chapter in the User Guide).
The callbacks will only be invoked if they are both enabled via
/confdConfig/aaa/authorization/callback/enabled
in
confd.conf
(see confd.conf(5)) and registered as
described here.
If the callbacks are enabled in
confd.conf
but no registration has been done, or
if invocation keeps failing for some reason, all
access checks will be rejected.
int confd_register_authorization_cb( | struct confd_daemon_ctx *dx, |
const struct confd_authorization_cbs *acb) ; |
Registers the authorization callbacks. The struct confd_authorization_cbs is defined as:
struct confd_authorization_cbs { int cmd_filter; int data_filter; int (*chk_cmd_access)(struct confd_authorization_ctx *actx, char **cmdtokens, int ntokens, int cmdop); int (*chk_data_access)(struct confd_authorization_ctx *actx, u_int32_t hashed_ns, confd_hkeypath_t *hkp, int dataop, int how); };
Both callbacks are optional, i.e. we can set the function
pointer in struct confd_authorization_cbs to NULL if we
don't want the corresponding callback invocation. In this case the AAA
subsystem will handle the access check as if the callback was
registered, but always replied with
CONFD_ACCESS_RESULT_DEFAULT
(see below).
The cmd_filter
and
data_filter
elements can be used to prevent access
checks from causing invocation of a callback even though it is
registered. If we do not want any filtering, they must be set to
zero. The value is a bitmask obtained by ORing together values: For
cmd_filter
, we can use the possible values for
cmdop
(see below), preventing the corresponding
invocations of chk_cmd_access()
. For
data_filter
, we can use the possible values for
dataop
and how
(see
below), preventing the corresponding invocation of
chk_data_access()
. If the callback invocation is
prevented by filtering, the AAA subsystem will handle the access check
as if the callback had replied with
CONFD_ACCESS_RESULT_CONTINUE
(see below).
Both callbacks are invoked with a pointer to an authorization context that provides information about the user session that the access check pertains to, and the group list for that session. The struct confd_authorization_ctx is defined as:
struct confd_authorization_ctx { struct confd_user_info *uinfo; int ngroups; char **groups; struct confd_daemon_ctx *dx; /* ConfD internal fields */ int result; int query_ref; };
chk_cmd_access()
This callback is invoked for command authorization, i.e.
it corresponds to the rules under
/aaa/authorization/cmdrules
in the AAA data
model. cmdtokens
is an array of
ntokens
NUL-terminated strings
representing the command to be checked, corresponding to the
command
leaf in the cmdrule
list. If
/confdConfig/cli/modeInfoInAAA
is enabled in
confd.conf
(see confd.conf(5)), mode names
will be prepended in the cmdtokens
array.
The cmdop
parameter gives the operation,
corresponding to the ops
leaf in the
cmdrule
list. The possible values for
cmdop
are:
CONFD_ACCESS_OP_READ
Read access. The CLI will use this during command completion, to filter out alternatives that are disallowed by AAA.
CONFD_ACCESS_OP_EXECUTE
Execute access. This is used when a command is about to be executed.
This callback may be invoked with actx->uinfo
== NULL
, meaning that no user session has been
established for the user yet. This will occur e.g. when the CLI
checks whether a user attempting to log in is allowed to
(implicitly) execute the command "request system logout user"
(J-CLI) or "logout" (C/I-CLI) when the maximum number of
sessions has already been reached (if allowed, the CLI will ask
whether the user wants to terminate one of the existing
sessions).
chk_data_access()
This callback is invoked for data authorization, i.e. it
corresponds to the rules under
/aaa/authorization/datarules
in the AAA data
model. hashed_ns
and
hkp
give the namespace and hkeypath of
the data node to to be checked, corresponding to the
namespace
and keypath
leafs in the
datarule
list. The hkp
parameter may be NULL, which means that access to the entire
namespace given by hashed_ns
is
requested. When a hkeypath is provided, some key elements in the
path may be without key values (i.e. hkp->v[n][0].type ==
C_NOEXISTS). This indicates "wildcard" keys, used for CLI tab
completion when keys are not fully specified. The
dataop
parameter gives the operation,
corresponding the ops
leaf in the datarule
list. The possible values for dataop
are:
CONFD_ACCESS_OP_READ
Read access.
CONFD_ACCESS_OP_EXECUTE
Execute access.
CONFD_ACCESS_OP_CREATE
Create access.
CONFD_ACCESS_OP_UPDATE
Update access.
CONFD_ACCESS_OP_DELETE
Delete access.
CONFD_ACCESS_OP_WRITE
Write access. This is used when the specific write operation (create/update/delete) isn't known yet, e.g. in CLI command completion or processing of a NETCONF edit-config.
The how
parameter is one of:
CONFD_ACCESS_CHK_INTERMEDIATE
Access to the given data node or its descendants is requested. This is used e.g. in CLI command completion or processing of a NETCONF edit-config.
CONFD_ACCESS_CHK_FINAL
Access to the specific data node is requested.
The callbacks must call this function to report the result of
the access check to ConfD, and should normally return CONFD_OK. If any
other value is returned, it will cause the access check to be
rejected. The actx
parameter is the pointer to
the authorization context passed in the callback invocation, and
result
must be one of:
CONFD_ACCESS_RESULT_ACCEPT
The access is allowed. This is a "final verdict", analogous to a "full match" when the AAA rules are used.
CONFD_ACCESS_RESULT_REJECT
The access is denied.
CONFD_ACCESS_RESULT_CONTINUE
The access is allowed "so far". I.e. access to
sub-elements is not necessarily allowed. This result is mainly
useful when chk_cmd_access()
is called with
cmdop
==
CONFD_ACCESS_OP_READ
or
chk_data_access()
is called with
how
==
CONFD_ACCESS_CHK_INTERMEDIATE
.
CONFD_ACCESS_RESULT_DEFAULT
The request should be handled according to the rules configured in the AAA data model.
The authorization callbacks are invoked on the daemon control
socket, and as such are expected to complete quickly, within the
timeout specified for
/confdConfig/capi/newSessionTimeout
. However in case they
send requests to a remote server, and such a request needs to be
retried, this function can be used to extend the timeout for the
current callback invocation. The timeout is given in seconds from the
point in time when the function is called.
It is possible to register a callback function to generate
customized error messages for ConfD's internally generated
errors. All the customizable errors are defined with a type and a code
in the XML document
$CONFD_DIR/src/confd/errors/errcode.xml
in the
ConfD release. To use this functionality, the application must
#include
the file confd_errcode.h
,
which defines C constants for the types and codes.
Registers the error formatting callback. The struct confd_error_cb is defined as:
struct confd_error_cb { int error_types; void (*format_error)(struct confd_user_info *uinfo, struct confd_errinfo *errinfo, char *default_msg); };
The error_types
element is the logical OR of
the error types that the callback should handle. An application daemon
can only register one error formatting callback, and only one daemon
can register for each error type. The available types are:
CONFD_ERRTYPE_VALIDATION
Errors detected by ConfD's internal semantic
validation of the data model constraints, e.g. mandatory elements
that are unset, dangling references, etc. The codes for this type
are the confd_errno
values corresponding to the
validation errors, as resulting e.g. from a call to
maapi_apply_trans()
(see confd_lib_maapi(3)).
I.e. CONFD_ERR_NOTSET, CONFD_ERR_BAD_KEYREF, etc - see the 'id'
attribute in errcode.xml
.
CONFD_ERRTYPE_BAD_VALUE
Type errors, i.e. errors generated when an invalid
value is given for a leaf in the data model. The codes for this type
are defined in confd_errcode.h
as
CONFD_BAD_VALUE_XXX, where "XXX" is the all-uppercase form of the code
name given in errcode.xml
.
CONFD_ERRTYPE_CLI
CLI-specific errors. The codes for this type are
defined in confd_errcode.h
as CONFD_CLI_XXX in
the same way as for
CONFD_ERRTYPE_BAD_VALUE
.
CONFD_ERRTYPE_MISC
Miscellaneous errors, which do not fit into the
other categories. The codes for this type are defined in
confd_errcode.h
as CONFD_MISC_XXX in the same
way as for
CONFD_ERRTYPE_BAD_VALUE
.
The format_error()
callback is invoked with
a pointer to a struct confd_errinfo, which gives the error
type and type-specific structured information about the details of the
error. It is defined as:
struct confd_errinfo { int type; /* CONFD_ERRTYPE_XXX */ union { struct confd_errinfo_validation validation; struct confd_errinfo_bad_value bad_value; struct confd_errinfo_cli cli; struct confd_errinfo_misc misc; } info; };
For CONFD_ERRTYPE_VALIDATION
, the
struct confd_errinfo_validation validation
gives the detailed information, using an info
union that has a specific struct member for each code:
struct confd_errinfo_validation { int code; /* CONFD_ERR_NOTSET, CONFD_ERR_TOO_FEW_ELEMS, ... */ union { struct { /* the element given by kp is not set */ confd_hkeypath_t *kp; } notset; struct { /* kp has n instances, must be at least min */ confd_hkeypath_t *kp; int n, min; } too_few_elems; struct { /* kp has n instances, must be at most max */ confd_hkeypath_t *kp; int n, max; } too_many_elems; struct { /* the elements given by kps1 have the same set of values vals as the elements given by kps2 (kps1, kps2, and vals point to n_elems long arrays) */ int n_elems; confd_hkeypath_t *kps1; confd_hkeypath_t *kps2; confd_value_t *vals; } non_unique; struct { /* the element given by kp references the non-existing element given by ref Note: 'ref' may be NULL or have key elements without values (ref->v[n][0].type == C_NOEXISTS) if it cannot be instantiated */ confd_hkeypath_t *kp; confd_hkeypath_t *ref; } bad_keyref; struct { /* the mandatory 'choice' statement choice in the container kp does not have a selected 'case' */ confd_value_t *choice; confd_hkeypath_t *kp; } unset_choice; struct { /* the 'must' expression expr for element kp is not satisfied - error_message and and error_app_tag are NULL if not given in the 'must'; val points to the value of the element if it has one, otherwise it is NULL */ char *expr; confd_hkeypath_t *kp; char *error_message; char *error_app_tag; confd_value_t *val; } must_failed; struct { /* the element kp has the instance-identifier value instance, which doesn't exist, but require-instance is 'true' */ confd_hkeypath_t *kp; confd_hkeypath_t *instance; } missing_instance; struct { /* the element kp has the instance-identifier value instance, which doesn't conform to the specified path filters */ confd_hkeypath_t *kp; confd_hkeypath_t *instance; } invalid_instance; struct { /* the expression for a configuration policy rule evaluated to 'false' - error_message is the associated error message */ char *error_message; } policy_failed; struct { /* the XPath expression expr, for the configuration policy rule with key name, could not be compiled due to msg */ char *name; char *expr; char *msg; } policy_compilation_failed; struct { /* the expression expr, for the configuration policy rule with key name, failed XPath evaluation due to msg */ char *name; char *expr; char *msg; } policy_evaluation_failed; } info; int test; /* 1 if 'validate', 0 if 'commit' */ struct confd_trans_ctx *tctx; /* only valid for duration of callback */ };
The member structs are named as the
confd_errno
values that are used for the
code
elements, i.e. notset
for
CONFD_ERR_NOTSET, etc. For this error type, the callback also has full
information about the transaction that failed validation via the
struct confd_trans_ctx *tctx
element - it is even
possible to use maapi_attach()
(see confd_lib_maapi(3)) to
attach to the transaction and read arbitrary data from it, in case the
data directly related to the error (as given in the code-specific
struct) is not sufficient.
For the other error types, the corresponding
confd_errinfo_xxx
struct gives the code and an
array with the parameters for the default error message, as defined by
the <fmt>
element in
errcode.xml
:
enum confd_errinfo_ptype { CONFD_ERRINFO_KEYPATH, CONFD_ERRINFO_STRING };
struct confd_errinfo_param { enum confd_errinfo_ptype type; union { confd_hkeypath_t *kp; char *str; } val; };
struct confd_errinfo_bad_value { int code; int n_params; struct confd_errinfo_param *params; };
The parameters in the params
array are given
in the order they appear in the <fmt>
specification.
Parameters that are specified as {path}
have
params[n].type
set to
CONFD_ERRINFO_KEYPATH
, and are represented as a
confd_hkeypath_t that can be accessed via
params[n].val.kp
. All other parameters are
represented as strings, i.e. params[n].type
is
CONFD_ERRINFO_STR
and the string value can be
accessed via params[n].val.str
. The struct
confd_errinfo_cli cli
and struct confd_errinfo_misc
misc
union members have the same form as struct
confd_errinfo_bad_value
shown above.
Finally, the default_msg
callback
parameter gives the default error message that will be reported to the
user if the format_error()
function does not
generate a replacement.
This function must be called by
format_error()
to provide a replacement of the
default error message. If format_error()
returns
without calling confd_error_seterr()
, the default
message will be used.
Here is an example that targets a specific validation error for a specific element in the data model. For this case only, it replaces ConfD's internally generated messages of the form:
"too many 'protocol bgp', 2 configured, at
most 1 must be configured"
with
"Only 1 bgp instance is supported, cannot define
2"
#include <confd_lib.h> #include <confd_dp.h> #include <confd_errcode.h> . . int main(int argc, char **argv) { struct confd_error_cb ecb; . . memset(&ecb, 0, sizeof(ecb)); ecb.error_types = CONFD_ERRTYPE_VALIDATION; ecb.format_error = format_error; if (confd_register_error_cb(dctx, &ecb) != CONFD_OK) confd_fatal("Couldn't register error callback\n"); . } static void format_error(struct confd_user_info *uinfo, struct confd_errinfo *errinfo, char *default_msg) { struct confd_errinfo_validation *err; confd_hkeypath_t *kp; err = &errinfo->info.validation; if (err->code == CONFD_ERR_TOO_MANY_ELEMS) { kp = err->info.too_many_elems.kp; if (CONFD_GET_XMLTAG(&kp->v[0][0]) == myns_bgp && CONFD_GET_XMLTAG(&kp->v[1][0]) == myns_protocol) { confd_error_seterr(uinfo, "Only %d bgp instance is supported, " "cannot define %d", err->info.too_many_elems.max, err->info.too_many_elems.n); } } }
The CLI-specific "Aborted: " prefix is not included in the
message for this error type - if we wanted to replace that too, we
could include the CONFD_ERRTYPE_CLI
error type in
the registration and process the
CONFD_CLI_COMMAND_ABORTED
error code for this
type, see errcode.xml
.