Section 3
confd_lib
confd_libconfd_lib - C library for connecting to NSO
Library
NSO Library, (libconfd, -lconfd)
Description
The libconfd shared library is used to connect to NSO. The documentation for the library is divided into several manual pages:
Common Library Functions
The Data Provider API
The Event Notification API
The High Availability API
The CDB API
The Management Agent API
There is also a C header file associated with each of these manual pages:
#include <confd_lib.h>
Common type definitions and prototypes for the functions in the confd_lib_lib(3) manual page. Always needed.
#include <confd_dp.h>
Needed when functions in the confd_lib_dp(3) manual page are used.
#include <confd_events.h>
Needed when functions in the confd_lib_events(3) manual page are used.
#include <confd_ha.h>
Needed when functions in the confd_lib_ha(3) manual page are used.
#include <confd_cdb.h>
Needed when functions in the confd_lib_cdb(3) manual page are used.
#include <confd_maapi.h>
Needed when functions in the confd_lib_maapi(3) manual page are used.
For backwards compatibility, #include <confd.h> can also be used, and is equivalent to:
See Also
The NSO User Guide
confd_lib_cdb
confd_lib_cdbconfd_lib_cdb - library for connecting to NSO built-in XML database (CDB)
Synopsis
Library
NSO Library, (libconfd, -lconfd)
Description
The libconfd shared library is used to connect to the NSO built-in XML database, CDB. The purpose of this API is to provide a read and subscription API to CDB.
CDB owns and stores the configuration data and the user of the API wants to read that configuration data and also get notified when someone through either NETCONF, SNMP, the CLI, the Web UI or the MAAPI modifies the data so that the application can re-read the configuration data and act accordingly.
CDB can also store operational data, i.e. data which is designated with a "config false" statement in the YANG data model. Operational data can be both read and written by the applications, but NETCONF and the other northbound agents can only read the operational data.
Paths
The majority of the functions described here take as their two last arguments a format string and a variable number of extra arguments as in: char *``fmt, ...``);
The fmt is a printf style format string which is used to format a path into the XML data tree. Assume the following YANG fragment:
Furthermore, assuming our database is populated with the following data.
The format path /hosts/host{buzz}/defgw refers to the leaf called defgw of the host whose key (name leaf) is buzz.
The format path /hosts/host{buzz}/interfaces/interface{eth0}/ip refers to the leaf called ip in the eth0 interface of the host called buzz.
It is possible loop through all entries in a list as in:
Thus instead of an actually instantiated key inside a pair of curly braces {key}, we can use a temporary integer key inside a pair of brackets [n].
We can use the following modifiers:
%d
requiring an integer parameter (type
int) to be substituted.
%u
requiring an unsigned integer parameter (type
unsigned int) to be substituted.
%s
requiring a
char*string parameter to be substituted.
%ip4
requiring a
struct in_addr*to be substituted.
%ip6
requiring a
struct in6_addr*to be substituted.
%x
requiring a
confd_value_t*to be substituted.
%*x
requiring an array length and a
confd_value_t*pointing to an array of values to be substituted.
%h
requiring a
confd_hkeypath_t*to be substituted.
%*h
requiring a length and a
confd_hkeypath_t*to be substituted.
Thus,
would change the current position to the path: "/hosts/host{earth}/bar{127.0.0.1}"
It is also possible to use the different '%' modifiers outside the curly braces, thus the above example could have been written as:
If an element has multiple keys, the keys must be space separated as in cdb_cd("/bars/bar{%s %d}/item", str, i);. However the '%*x' modifier is an exception to this rule, and it is especially useful when we have a number of key values that are unknown at compile time. If we have a list foo which is known to have two keys, and we have those keys in an array key[], we can use cdb_cd("/foo{%x %x}", &key[0], &key[1]);. But if the number of keys is unknown at compile time (or if we just want a more compact code), we can instead use cdb_cd("/foo{%*x}", n, key); where n is the number of keys.
The '%h' and '%*h' modifiers can only be used at the beginning of a format path, as they expand to the absolute path corresponding to the confd_hkeypath_t. These modifiers are particularly useful with cdb_diff_iterate() (see below), or for MAAPI access in data provider callbacks (see confd_lib_maapi(3) and confd_lib_dp(3)). The '%*h' variant allows for using only the initial part of a confd_hkeypath_t, as specified by the preceding length argument (similar to '%.*s' for printf(3)).
For example, if the iter() function passed to cdb_diff_iterate() has been invoked with a confd_hkeypath_t *kp that corresponds to /hosts/host{buzz}, we can read the defgw child element with
or the entire list entry with
or the defgw child element for host mars with
All the functions that take a path on this form also have a va_list variant, of the same form as cdb_vget() and cdb_vset_elem(), which are the only ones explicitly documented below. I.e. they have a prefix "cdb_v" instead of "cdb_", and take a single va_list argument instead of a variable number of arguments.
Functions
All functions return CONFD_OK (0), CONFD_ERR (-1) or CONFD_EOF (-2) unless otherwise stated. CONFD_EOF means that the socket to NSO has been closed.
Whenever CONFD_ERR is returned from any API function described here, it is possible to obtain additional information on the error through the symbol confd_errno, see the ERRORS section in the confd_lib_lib(3) manual page.
The application has to connect to NSO before it can interact. There are two different types of connections identified by cdb_sock_type:
CDB_DATA_SOCKET
This is a socket which is used to read configuration data, or to read and write operational data.
CDB_SUBSCRIPTION_SOCKET
This is a socket which is used to receive notifications about updates to the database. A subscription socket needs to be part of the application poll set.
Additionally the type CDB_READ_SOCKET is accepted for backwards compatibility - it is equivalent to CDB_DATA_SOCKET.
A call to cdb_connect() is typically followed by a call to either cdb_start_session() for a reading session or a call to cdb_subscribe() for a subscription socket.
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
When we use cdb_connect() to create a connection to NSO/CDB, the name parameter passed to the library initialization function confd_init() (see confd_lib_lib(3)) is used to identify the connection in status reports and logs. If we want different names to be used for different connections from the same application process, we can use cdb_connect_name() with the wanted name instead of cdb_connect().
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
Attaches a mandatory attribute and a mandatory name to the subscriber identified by sock. The name parameter is distinct from the name parameter in cdb_connect_name.
CDB keeps a list of mandatory subscribers for infinite extent, i.e. until confd is restarted. The function is idempotent.
Absence of one or more mandatory subscribers will result in abort of all transactions. A mandatory subscriber must be present during the entire PREPARE delivery phase.
If a mandatory subscriber crashes during a PREPARE delivery phase, the subscriber should be restarted and the commit operation should be retried.
A mandatory subscriber is present if the subscriber has issued at least one cdb_subscribe2() call followed by a cdb_subscribe_done() call.
A call to cdb_mandatory_subscriber() is only allowed before the first call of cdb_subscribe2().
Only applicable for two-phase subscribers.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
If we want to access data in CDB where the toplevel element name is not unique, we need to set the namespace. We are reading data related to a specific .fxs file. confdc can be used to generate a .h file with a #define for the namespace, by the flag --emit-h to confdc (see confdc(1)).
It is also possible to indicate which namespace to use through the namespace prefix when we read and write data. Thus the path /foo:bar/baz will get us /bar/baz in the namespace with prefix "foo" regardless of what the "set" namespace is. And if there is only one toplevel element called "bar" across all namespaces, we can use /bar/baz without the prefix and without calling cdb_set_namespace().
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_NOEXISTS
We use cdb_connect() to establish a read socket to CDB. When the socket is closed, the read session is ended. We can reuse the same socket for another read session, but we must then end the session and create another session using cdb_start_session().
While we have a live CDB read session for configuration data, CDB is normally locked for writing. Thus all external entities trying to modify CDB are blocked as long as we have an open CDB read session. It is very important that we remember to either cdb_end_session() or cdb_close() once we have read what we wish to read.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_NOEXISTS
Starts a new session on an already established socket to CDB. The db parameter should be one of:
CDB_RUNNING
Creates a read session towards the running database.
CDB_PRE_COMMIT_RUNNING
Creates a read session towards the running database as it was before the current transaction was committed. This is only possible between a subscription notification and the final
cdb_sync_subscription_socket(). At any other time trying to callcdb_start_session()will fail with confd_errno set to CONFD_ERR_NOEXISTS.In the case of a
CDB_SUB_PREPAREsubscription notification a session towardsCDB_PRE_COMMIT_RUNNINGwill (in spite of the name) will return values as they were before the transaction which is about to be committed took place. This means that if you want to read the new values during aCDB_SUB_PREPAREsubscription notification you need to create a session towardsCDB_RUNNING. However, since it is locked the session needs to be started in lockless mode usingcdb_start_session2(). So for example:
CDB_STARTUP
Creates a read session towards the startup database.
CDB_OPERATIONAL
Creates a read/write session towards the operational database. For further details about working with operational data in CDB, see the
OPERATIONAL DATAsection below.Subscriptions on operational data will not be triggered from a session created with this function - to trigger operational data subscriptions, we need to use
cdb_start_session2(), see below.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_LOCKED, CONFD_ERR_NOEXISTS
If the error is CONFD_ERR_LOCKED it means that we are trying to create a new CDB read session precisely when the write phase of some transaction is occurring. Thus correct usage of cdb_start_session() is:
Alternatively we can use cdb_start_session2() with flags = CDB_LOCK_SESSION|CDB_LOCK_WAIT. This means that the call will block until the lock has been acquired, and thus we do not need the retry loop.
This function may be used instead of cdb_start_session() if it is considered necessary to have more detailed control over some aspects of the CDB session - if in doubt, use cdb_start_session() instead. The sock and db arguments are the same as for cdb_start_session(), and these values can be used for flags (ORed together if more than one):
The flags affect sessions for the different database types as follows:
CDB_RUNNING
CDB_LOCK_SESSION obtains a read lock for the complete session, i.e. using this flag alone is equivalent to calling
cdb_start_session(). CDB_LOCK_REQUEST obtains a read lock only for the duration of each read request. This means that values of elements read in different requests may be inconsistent with each other, and the consequences of this must be carefully considered. In particular, the use ofcdb_num_instances()and the[n]"integer index" notation in keypaths is inherently unsafe in this mode. Note: The implementation will not actually obtain a lock for a single-value request, since that is an atomic operation anyway. The CDB_LOCK_PARTIAL flag is not allowed.
CDB_STARTUP
Same as CDB_RUNNING.
CDB_PRE_COMMIT_RUNNING
This database type does not have any locks, which means that it is an error to call
cdb_start_session2()with any CDB_LOCK_XXX flag included inflags. Using aflagsvalue of 0 is equivalent to callingcdb_start_session().
CDB_OPERATIONAL
CDB_LOCK_REQUEST obtains a "subscription lock" for the duration of each write request. This can be described as an "advisory exclusive" lock, i.e. only one client at a time can hold the lock (unless CDB_LOCK_PARTIAL is used), but the lock does not affect clients that do not attempt to obtain it. It also does not affect the reading of operational data. The purpose of this lock is to indicate that the client wants the write operation to generate subscription notifications. The lock remains in effect until any/all subscription notifications generated as a result of the write has been delivered.
If the CDB_LOCK_PARTIAL flag is used together with CDB_LOCK_REQUEST, the "subscription lock" only applies to the smallest data subtree that includes all the data in the write request. This means that multiple writes that generates subscription notifications, and delivery of the corresponding notifications, can proceed in parallel as long as they affect disjunct parts of the data tree.
The CDB_LOCK_SESSION flag is not allowed. Using a
flagsvalue of 0 is equivalent to callingcdb_start_session().
In all cases of using CDB_LOCK_SESSION or CDB_LOCK_REQUEST described above, adding the CDB_LOCK_WAIT flag means that instead of failing with CONFD_ERR_LOCKED if the lock can not be obtained immediately, requests will wait for the lock to become available. When used with CDB_LOCK_SESSION it pertains to cdb_start_session2() itself, with CDB_LOCK_REQUEST it pertains to the individual requests.
While it is possible to use this function to start a session towards a configuration database type with no locking at all (flags = 0), this is strongly discouraged in general, since it means that even the values read in a single multi-value request (e.g. cdb_get_object(), see below) may be inconsistent with each other. However it is necessary to do this if we want to have a session open during semantic validation, see the "Semantic Validation" chapter in the User Guide - and in this particular case it is safe, since the transaction lock prevents changes to CDB during validation.
Reading operational data from CDB while there is an ongoing transaction, CDB will by default read through the transaction, returning the value from the transaction if it is being modified. By giving the CDB_READ_COMMITTED flag this behaviour can be overridden in the operational datastore, such that the value already committed to the datastore is read.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_LOCKED, CONFD_ERR_NOEXISTS, CONFD_ERR_PROTOUSAGE
Closes the socket. cdb_end_session() should be called before calling this function.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_NOEXISTS
Even if the call returns an error, the socket will be closed.
This call waits until CDB has completed start-phase 1 and is available, when it is CONFD_OK is returned. If CDB already is available (i.e. start-phase >= 1) the call returns immediately. This can be used by a CDB client who is not synchronously started and only wants to wait until it can read its configuration. The call can be used after cdb_connect().
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
Returns the start-phase CDB is currently in, in the struct cdb_phase pointed to by the second argument. Also if CDB is in phase 0 and has initiated an init transaction (to load any init files) the flag CDB_FLAG_INIT is set in the flags field of struct cdb_phase and correspondingly if an upgrade session is started the CDB_FLAG_UPGRADE is set. The call can be used after cdb_connect() and returns CONFD_OK.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
Normally CDB handles journal compaction of the config datastore automatically. If this has been turned off (in the configuration file) then the .cdb files will grow indefinitely unless this API function is called periodically to initiate compaction. This function initiates a compaction and returns immediately (if the datastore is unavailable, the compaction will be delayed, but eventually compaction will take place). This will also initiate compaction of the operational datastore O.cdb and snapshot datastore S.cdb but without delay.
Errors: -
Similar to cdb_initiate_journal_compaction() but initiates the compaction on the specified CDB file instead of all CDB files. The dbfile argument is identified by enum cdb_dbfile_type. The valid values for NSO are
CDB_A_CDB
This is the configuration datastore A.cdb
CDB_O_CDB
This is the operational datastore O.cdb
CDB_S_CDB
This is the snapshot datastore S.cdb
Errors: CONFD_ERR_PROTOUSAGE
Returns the compaction information for the specified CDB file pointed to by the dbfile argument, see cdb_initiate_journal_dbfile_compaction() for further information. The result is stored in the info argument of struct cdb_compaction_info, containing the current file size, file size of the dbfile after the last compaction, the number of transactions since last compaction, as well as the timestamp of the last compaction.
Errors: CONFD_ERR_PROTOUSAGE, CONFD_ERR_UNAVAILABLE
Read the last transaction id from CDB. This function can be used if we are forced to reconnect to CDB, If the transaction id we read is identical to the last id we had prior to loosing the CDB sockets we don't have to reload our managed object data. See the User Guide for full explanation. Returns CONFD_OK on success and CONFD_ERR or CONFD_EOF on failure.
When the subscriptionReplay functionality is enabled in confd.conf this function returns the list of available transactions that CDB can replay. The current transaction id will be the first in the list, the second at txid[1] and so on. The number of transactions is returned in resultlen. In case there are no replay transactions available (the feature isn't enabled or there hasn't been any transactions yet) only one (the current) transaction id is returned. It is up to the caller to free() txid when it is no longer needed.
A timeout for client actions can be specified via /confdConfig/cdb/clientTimeout in confd.conf, see the confd.conf(5) manual page. This function can be used to dynamically extend (or shorten) the timeout for the current action. Thus it is possible to configure a restrictive timeout in confd.conf, but still allow specific actions to have a longer execution time.
The function can be called either with a subscription socket during subscription delivery on that socket (including from the iter() function passed to cdb_diff_iterate()), or with a data socket that has an active session. The timeout is given in seconds from the point in time when the function is called.
The timeout for subscription delivery is common for all the subscribers receiving notifications at a given priority. Thus calling the function during subscription delivery changes the timeout for all the subscribers that are currently processing notifications.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_PROTOUSAGE, CONFD_ERR_BADSTATE
Leafs in the data model may be optional, and presence containers and list entries may or may not exist. This function checks whether a node exists in CDB. Returns 0 for false, 1 for true and CONFD_ERR or CONFD_EOF for errors.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH
Changes the working directory according to the format path. Note that this function can not be used as an existence test.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH
Similar to cdb_cd() but pushes the previous current directory on a stack.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_NOSTACK, CONFD_ERR_BADPATH
Pops the top element from the directory stack and changes directory to previous directory.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_NOSTACK
Returns the current position as previously set by cdb_cd(), cdb_pushd(), or cdb_popd() as a string path. Note that what is returned is a pretty-printed version of the internal representation of the current position, it will be the shortest unique way to print the path but it might not exactly match the string given to cdb_cd(). The buffer in *curdir will be NULL terminated, and no more characters than strsz-1 will be written to it.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
Returns the current position like cdb_getcwd(), but as a pointer to a hashed keypath instead of as a string. The hkeypath is dynamically allocated, and may further contain dynamically allocated elements. The caller must free the allocated memory, easiest done by calling confd_free_hkeypath().
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
Returns the number of entries in a list or leaf-list. On error CONFD_ERR or CONFD_EOF is returned.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_UNAVAILABLE
Given a path to a list entry cdb_next_index() returns the position (starting from 0) of the next entry (regardless of whether the path exists or not). When the list has multiple keys a * may be used for the last keys to make the path partially instantiated. For example if /foo/bar has three integer keys, the following pseudo code could be used to iterate over all entries with 42 as the first key:
If there is no next entry -1 is returned. It is not possible to use this function on an ordered-by user list. On error CONFD_ERR or CONFD_EOF is returned.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_UNAVAILABLE
Given a path to a list entry cdb_index() returns its position (starting from 0). On error CONFD_ERR or CONFD_EOF is returned.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH
This function returns 1 for a leaf which has a default value defined in the data model when no value has been set, i.e. when the default value is in effect. It returns 0 for other existing leafs, and CONFD_ERR or CONFD_EOF for errors. There is normally no need to call this function, since CDB automatically provides the default value as needed when cdb_get() etc is called.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOEXISTS, CONFD_ERR_UNAVAILABLE
Sets up a CDB subscription so that we are notified when CDB configuration data changes. There can be multiple subscription points from different sources, that is a single client daemon can have many subscriptions and there can be many client daemons.
Each subscription point is defined through a path similar to the paths we use for read operations. We can subscribe either to specific leafs or entire subtrees. Subscribing to list entries can be done using fully qualified paths, or tagpaths to match multiple entries. A path which isn't a leaf element automatically matches the subtree below that path. When specifying keys to a list entry it is possible to use the wildcard character * which will match any key value.
When subscribing to a leaf with a tailf:default-ref statement, or to a subtree with elements that have tailf:default-ref, implicit subscriptions to the referred leafs are added. This means that a change in a referred leaf will generate a notification for the subscription that has referring leaf(s) - but currently such a change will not be reported by cdb_diff_iterate(). Thus to get the new "effective" value of a referring leaf in this case, it is necessary to either read the value of the leaf with e.g. cdb_get() - or to use a subscription that includes the referred leafs, and use cdb_diff_iterate() when a notification for that subscription is received.
Some examples
/hosts
Means that we subscribe to any changes in the subtree - rooted at /hosts. This includes additions or removals of host entries as well as changes to already existing host entries.
/hosts/host{www}/interfaces/interface{eth0}/ip
Means we are notified when host www changes its IP address on eth0.
/hosts/host/interfaces/interface/ip
Means we are notified when any host changes any of its IP addresses.
/hosts/host/interfaces
Means we are notified when either an interface is added/removed or when an individual leaf element in an existing interface is changed.
The priority value is an integer. When CDB is changed, the change is performed inside a transaction. Either a commit operation from the CLI or a candidate-commit operation in NETCONF means that the running database is changed. These changes occur inside a ConfD transaction. CDB will handle the subscriptions in lock-step priority order. First all subscribers at the lowest priority are handled, once they all have replied and synchronized through calls to cdb_sync_subscription_socket() the next set - at the next priority level is handled by CDB. Priority numbers are global, i.e. if there are multiple client daemons notifications will still be delivered in priority order per all subscriptions, not per daemon.
See cdb_diff_iterate() and cdb_diff_match() for ways of filtering subscription notifications and finding out what changed. The easiest way is though to not use either of the two above mentioned diff function but to solely rely on the positioning of the subscription points in the tree to figure out what changed.
cdb_subscribe() returns a subscription point in the return parameter spoint. This integer value is used to identify this particular subscription.
Because there can be many subscriptions on the same socket the client must notify ConfD when it is done subscribing and ready to receive notifications. This is done using cdb_subscribe_done().
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOEXISTS
Sets up a CDB subscription for changes in the operational data base. Similar to the subscriptions for configuration data, we can be notified of changes to the operational data stored in CDB. Note that there are several differences from the subscriptions for configuration data:
Notifications are only generated if the writer has taken a subscription lock, see
cdb_start_session2()above.Priorities are not used for these notifications.
It is not possible to receive the previous value for modified leafs in
cdb_diff_iterate().A special synchronization reply must be used when the notifications have been read (see
cdb_sync_subscription_socket()below).
Operational and configuration subscriptions can be done on the same socket, but in that case the notifications may be arbitrarily interleaved, including operational notifications arriving between different configuration notifications for the same transaction. If this is a problem, use separate sockets for operational and configuration subscriptions.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOEXISTS
This function supersedes the current cdb_subscribe() and cdb_oper_subscribe() as well as makes it possible to use the new two phase subscription method. The cdb_sub_type is defined as:
The CDB subscription type CDB_SUB_RUNNING is the same as cdb_subscribe(), CDB_SUB_OPERATIONAL is the same as cdb_oper_subscribe(), and CDB_SUB_RUNNING_TWOPHASE does a two phase subscription.
The flags argument should be set to 0, or a combination of:
CDB_SUB_WANT_ABORT_ON_ABORT
Normally if a subscriber is the one to abort a transaction it will not receive an abort notification. This flags means that this subscriber wants an abort notification even if it was the one that called cdb_sub_abort_trans(). This flag is only valid when the subscription type is
CDB_SUB_RUNNING_TWOPHASE.
The two phase subscriptions work like this: A subscriber uses cdb_subscribe2() with the type set to CDB_SUB_RUNNING_TWOPHASE to register as many subscription points as required. The cdb_subscribe_done() function is used to indicate that no more subscription points will be registered on that particular socket. Only after cdb_subscribe_done() is called will subscription notifications be delivered.
Once a transaction enters prepare state all CDB two phase subscribers will be notified in priority order (lowest priority first, subscribers with the same priority is delivered in parallel). The cdb_read_subscription_socket2() function will set type to CDB_SUB_PREPARE. Once all subscribers have acknowledged the notification by using the function cdb_sync_subscription_socket(CDB_DONE_PRIORITY) they will subsequently be notified when the transaction is committed. The CDB_SUB_COMMIT notification is the same as the current subscription mechanism, so when a transaction is committed all subscribers will be notified (again in priority order).
When a transaction is aborted, delivery of any remaining CDB_SUB_PREPARE notifications is cancelled. The subscribers that had already been notified with CDB_SUB_PREPARE will be notified with CDB_SUB_ABORT (This notification will be done in reverse order of the CDB_SUB_PREPARE notification). The transaction could be aborted because one of the subscribers that received CDB_SUB_PREPARE called cdb_sub_abort_trans(), but it could also be caused for other reasons, for example another data provider (than CDB) can abort the transaction.
Two phase subscriptions are not supported for NCS.
Operational and configuration subscriptions can be done on the same socket, but in that case the notifications may be arbitrarily interleaved, including operational notifications arriving between different configuration notifications for the same transaction. If this is a problem, use separate sockets for operational and configuration subscriptions.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOEXISTS
When a client is done registering all its subscriptions on a particular subscription socket it must call cdb_subscribe_done(). No notifications will be delivered until then.
This function makes it possible to trigger CDB subscriptions for configuration data even though the configuration has not been modified. The caller will trigger all subscription points passed in the sub_points array (or all subscribers if the array is of zero length) in priority order, and the call will not return until the last subscriber has called cdb_sync_subscription_socket().
The call is blocking and doesn't return until all subscribers have acknowledged the notification. That means that it is not possible to use cdb_trigger_subscriptions() in a cdb subscriber process (without forking a process or spawning a thread) since it would cause a deadlock.
The subscription notification generated by this "synthetic" trigger will seem like a regular subscription notification to a subscription client. As such, it is possible to use cdb_diff_iterate() to traverse the changeset. CDB will make up this changeset in which all leafs in the configuration will appear to be set, and all list entries and presence containers will appear as if they are created.
If the client is a two-phase subscriber, a prepare notification will first be delivered and if any client aborts this synthetic transaction further delivery of subscription notification is suspended and an error is returned to the caller of cdb_trigger_subscriptions(). The error is the result of mapping the CONFD_ERRCODE as set by the aborting client as described for MAAPI in the EXTENDED ERROR REPORTING section in the confd_lib_lib(3) manpage. Note however that the configuration is still the way it is - so it is up to the caller of cdb_trigger_subscriptions() to take appropriate action (for example: raising an alarm, restarting a subsystem, or even rebooting the system).
If one or more subscription ids is passed in the subids array that are not valid, an error (CONFD_ERR_PROTOUSAGE) will be returned and no subscriptions will be triggered. If no subscription ids are passed this error can not occur (even if there aren't any subscribers).
This function works like cdb_trigger_subscriptions(), but for CDB subscriptions to operational data. The caller will trigger all subscription points passed in the sub_points array (or all operational data subscribers if the array is of zero length), and the call will not return until the last subscriber has called cdb_sync_subscription_socket().
Since the generation of subscription notifications for operational data requires that the subscription lock is taken (see cdb_start_session2()), this function implicitly attempts to take a "global" subscription lock. If the subscription lock is already taken, the function will by default return CONFD_ERR with confd_errno set to CONFD_ERR_LOCKED. To instead have it wait until the lock becomes available, CDB_LOCK_WAIT can be passed for the flags parameter.
This function makes it possible to replay the subscription events for the last configuration change to some or all CDB subscribers. This call is useful in a number of recovery scenarios, where some CDB subscribers lost connection to ConfD before having received all the changes in a transaction. The replay functionality is only available if it has been enabled in confd.conf
The caller specifies the transaction id of the last transaction that the application has completely seen and acted on. This verifies that the application has only missed (part of) the last transaction. If a different (older) transaction ID is specified, an error is returned and no subscriptions will be triggered. If the transaction id is the latest transaction ID (i.e. the caller is already up to date) nothing is triggered and CONFD_OK is returned.
By calling this function, the caller will potentially trigger all subscription points passed in the sub_points array (or all subscribers if the array is of zero length). The subscriptions will be triggered in priority order, and the call will not return until the last subscriber has called cdb_sync_subscription_socket().
The call is blocking and doesn't return until all subscribers have acknowledged the notification. That means that it is not possible to use cdb_replay_subscriptions() in a cdb subscriber process (without forking a process or spawning a thread) since it would cause a deadlock.
The subscription notification generated by this "synthetic" trigger will seem like a regular subscription notification to a subscription client. It is possible to use cdb_diff_iterate() to traverse the changeset.
If the client is a two-phase subscriber, a prepare notification will first be delivered and if any client aborts this synthetic transaction further delivery of subscription notification is suspended and an error is returned to the caller of cdb_replay_subscriptions(). The error is the result of mapping the CONFD_ERRCODE as set by the aborting client as described for MAAPI in the EXTENDED ERROR REPORTING section in the confd_lib_lib(3) manpage.
The subscription socket - which is acquired through a call to cdb_connect() - must be part of the application poll set. Once the subscription socket has I/O ready to read, we must call cdb_read_subscription_socket() on the subscription socket.
The call will fill in the result in the array sub_points with a list of integer values containing subscription points earlier acquired through calls to cdb_subscribe(). The global variable cdb_active_subscriptions can be read to find how many active subscriptions the application has. Make sure the sub_points[] array is at least this big, otherwise the confd library will write in unallocated memory.
The subscription points may be either for configuration data or operational data (if cdb_oper_subscribe() has been used on the same socket), but they will all be of the same "type" - i.e. a single call of the function will never deliver a mix of configuration and operational data subscription points.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
This is another version of the cdb_read_subscription_socket() with two important differences:
In this version subpoints is allocated by the library, and it is up to the caller of this function to
free()it when it is done.It is possible to retrieve the type of the subscription notification via the
typereturn parameter.
All parameters except sock are return parameters. It is legal to pass in flags and type as NULL pointers (in which case type and flags cannot be retrieved). subpoints is an array of integers, the length is indicated in resultlen, it is allocated by the library, and must be freed by the caller. The type parameter is what the subscriber uses to distinguish the different types of subscription notifications.
The flags return parameter can have the following bits set:
CDB_SUB_FLAG_IS_LAST
This bit is set when this notification is the last of its type for this subscription socket.
CDB_SUB_FLAG_HA_IS_SECONDARY
This bit is set when NCS runs in HA mode, and the current node is an HA secondary. It is a convenient way for the subscriber to know when invoked on a secondary and adjust, or possibly skip, processing.
CDB_SUB_FLAG_TRIGGER
This bit is set when the cause of the subscription notification is that someone called
cdb_trigger_subscriptions().
CDB_SUB_FLAG_REVERT
If a confirming commit is aborted it will look to the CDB subscriber as if a transaction happened that is the reverse of what the original transaction was. This bit will be set when such a transaction is the cause of the notification. Note that for a two-phase subscriber both a prepare and a commit notification is delivered. However it is not possible to reply by calling
cdb_sub_abort_trans()for the prepare notification in this case, instead the subscriber will have to take appropriate backup action if it needs to abort (for example: raise an alarm, restart, or even reboot the system).
CDB_SUB_FLAG_HA_SYNC
This bit is set when the cause of the subscription notification is initial synchronization of a HA secondary from CDB on the primary.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
After reading the subscription socket the cdb_diff_iterate() function can be used to iterate over the changes made in CDB data that matched the particular subscription point given by subid.
The user defined function iter() will be called for each element that has been modified and matches the subscription. The iter() callback receives the confd_hkeypath_t kp which uniquely identifies which node in the data tree that is affected, the operation, and optionally the values it has before and after the transaction. The op parameter gives the modification as:
MOP_CREATED
The list entry,
presencecontainer, or leaf of typeemptygiven bykphas been created.
MOP_DELETED
The list entry,
presencecontainer, or optional leaf given bykphas been deleted.If the subscription was triggered because an ancestor was deleted, the
iter()function will not called at all if the delete was above the subscription point. However if the flag ITER_WANT_ANCESTOR_DELETE is passed tocdb_diff_iterate()then deletes that trigger a descendant subscription will also generate a call toiter(), and in this casekpwill be the path that was actually deleted.
MOP_MODIFIED
A descendant of the list entry given by
kphas been modified.
MOP_VALUE_SET
The value of the leaf given by
kphas been set tonewv.
MOP_MOVED_AFTER
The list entry given by
kp, in anordered-by userlist, has been moved. Ifnewvis NULL, the entry has been moved first in the list, otherwise it has been moved after the entry given bynewv. In this casenewvis 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.
By setting the flags parameter ITER_WANT_REVERSE two-phase subscribers may use this function to traverse the reverse changeset in case of CDB_SUB_ABORT notification. In this scenario a two-phase subscriber traverses the changes in the prepare phase (CDB_SUB_PREPARE notification) and if the transaction is aborted the subscriber may iterate the inverse to the changes during the abort phase (CDB_SUB_ABORT notification).
For configuration subscriptions, the previous value of the node can also be passed to iter() if the flags parameter contains ITER_WANT_PREV, in which case oldv will be pointing to it (otherwise NULL). For operational data subscriptions, the ITER_WANT_PREV flag is ignored, and oldv is always NULL - there is no equivalent to CDB_PRE_COMMIT_RUNNING that holds "old" operational data.
If iter() returns ITER_STOP, no more iteration is done, and CONFD_OK is returned. If iter() returns ITER_RECURSE iteration continues with all children to the node. If iter() returns ITER_CONTINUE iteration ignores the children to the node (if any), and continues with the node's sibling, and if iter() returns ITER_UP the iteration is continued with the node's parents sibling. If, for some reason, the iter() function wants to return control to the caller of cdb_diff_iterate() before all the changes has been iterated over it can return ITER_SUSPEND. The caller then has to call cdb_diff_iterate_resume() to continue/finish the iteration.
The state parameter can be used for any user supplied state (i.e. whatever is supplied as initstate is passed as state to iter() in each invocation).
By default the traverse order is undefined but guaranteed to be the most efficient one. The traverse order may be changed by setting setting a bit in the flags parameter:
ITER_WANT_SCHEMA_ORDER
The
iter()function will be invoked in schema order (i.e. in the order in which the elements are defined in the YANG file).
ITER_WANT_LEAF_FIRST_ORDER
The
iter()function will be invoked for leafs first, then non-leafs.
ITER_WANT_LEAF_LAST_ORDER
The
iter()function will be invoked for non-leafs first, then leafs.
If the flags parameter ITER_WANT_SUPPRESS_OPER_DEFAULTS is given, operational default values will be skipped during iteration.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_NOEXISTS, CONFD_ERR_BADSTATE, CONFD_ERR_PROTOUSAGE.
The application must call this function whenever an iterator function has returned ITER_SUSPEND to finish up the iteration. If the application does not wish to continue iteration it must at least call cdb_diff_iterate_resume(s, ITER_STOP, NULL, NULL); to clean up the state. The reply parameter is what the iterator function would have returned (i.e. normally ITER_RECURSE or ITER_CONTINUE) if it hadn't returned ITER_SUSPEND. Note that it is up to the iterator function to somehow communicate that it has returned ITER_SUSPEND to the caller of cdb_diff_iterate(), this can for example be a field in a struct for which a pointer to can passed back and forth in the state/resumestate variable.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_NOEXISTS, CONFD_ERR_BADSTATE.
This function can be invoked when a subscription point has fired. Similar to the confd_hkp_tagmatch() function it takes an argument which is an array of XML tags. The function will invoke cdb_diff_iterate() on a subscription socket. Using combinations of ITER_STOP, ITER_CONTINUE and ITER_RECURSE return values, the function checks a tagpath and decides whether any changes (under the subscription point) has occurred that also match the provided path tags. It is slightly easier to use this function than cdb_diff_iterate() but can also be slower since it is a general purpose matcher.
If we have a subscription point at /root, we could invoke this function as:
The function returns 1 if there were any changes under subpoint that matched tags, 0 if no match was found and CONFD_ERR on error.
The cdb_get_modifications() function can be called after reception of a subscription notification to retrieve all the changes that caused the subscription notification. The socket s is the subscription socket, the subscription id must also be provided. Optionally a path can be used to limit what is returned further (only changes below the supplied path will be returned), if this isn't needed fmt can be set to NULL.
When cdb_get_modifications() returns CONFD_OK, the results are in values, which is a tag value array with length nvalues. The library allocates memory for the results, which must be free:d by the caller. This can in all cases be done with code like this:
The tag value array differs somewhat between how it is described in the confd_types(3) manual page, most notably only the values that were modified in this transaction are included. In addition to that these are the different values of the tags depending on what happened in the transaction:
A leaf of type empty that has been deleted has the value of
C_NOEXISTS, and when it is created it has the valueC_XMLTAG.A leaf or a leaf-list that has been set to a new value (or its default value) is included with that new value. If the leaf or leaf-list is optional, then when it is deleted the value is
C_NOEXISTS.Presence containers are included when they are created or when they have modifications below them (by the usual
C_XMLBEGIN,C_XMLENDpair). If a presence container has been deleted its tag is included, but has the valueC_NOEXISTS.
By default cdb_get_modifications() does not include list instances (created, deleted, or modified) - but if the CDB_GET_MODS_INCLUDE_LISTS flag is included in the flags parameter, list instances will be included. To receive information about where a list instance in an ordered-by user list is moved, the CDB_GET_MODS_INCLUDE_MOVES flag must also be included in the flags parameter. To receive information about ancestor list entry or presence container deletion the CDB_GET_MODS_WANT_ANCESTOR_DELETE flag must also be included in the flags parameter. Created, modified and moved instances are included wrapped in the C_XMLBEGIN / C_XMLEND pair, with the keys first. A list instance moved to the beginning of the list is indicated by C_XMLMOVEFIRST after the keys. A list instance moved elsewhere is indicated by C_XMLMOVEAFTER after the keys, with the after-keys following directly after. Deleted list instances instead begin with C_XMLBEGINDEL, then follows the keys, immediately followed by a C_XMLEND.
If the CDB_GET_MODS_SUPPRESS_DEFAULTS flag is included in the flags parameter, a default value that comes into effect for a leaf due to an ancestor list entry or presence container being created will not be included, and a default value that comes into effect for a leaf due to a set value being deleted will be included as a deletion (i.e. with value C_NOEXISTS).
When processing a CDB_SUB_ABORT notification for a two phase subscription, it is also possible to request a list of "reverse" modifications instead of the normal "forward" list. This is done by including the CDB_GET_MODS_REVERSE flag in the flags parameter.
The cdb_get_modifications_iter() is basically a convenient short-hand of the cdb_get_modifications() function intended to be used from within a iteration function started by cdb_diff_iterate(). In this case no subscription id is needed, and the path is implicitly the current position in the iteration.
Combining this call with cdb_diff_iterate() makes it for example possible to iterate over a list, and for each list instance fetch the changes using cdb_get_modifications_iter(), and then return ITER_CONTINUE to process next instance.
Note: The CDB_GET_MODS_REVERSE flag is ignored by cdb_get_modifications_iter(). It will instead return a "forward" or "reverse" list of modifications for a CDB_SUB_ABORT notification according to whether the ITER_WANT_REVERSE flag was included in the flags parameter of the cdb_diff_iterate() call.
The cdb_get_modifications_cli() function can be called after reception of a subscription notification to retrieve all the changes that caused the subscription notification as a string in Cisco CLI format. The socket s is the subscription socket, the subscription id must also be provided. The flags parameter is a bitmask with the following bits:
ITER_WANT_CLI_ORDER
When subscription is triggered by
cdb_trigger_subscriptions()this flag ensures that modifications are in the same order as they would be if triggered by a real commit. Use of this flag negatively impacts performance and memory consumption during the cdb_get_modifications_cli call.
The CLI string is malloc(3)ed by the library, and the caller must free the memory using free(3) when it is not needed any longer.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
Once we have read the subscription notification through a call to cdb_read_subscription_socket() and optionally used the cdb_diff_iterate() to iterate through the changes as well as acted on the changes to CDB, we must synchronize with CDB so that CDB can continue and deliver further subscription messages to subscribers with higher priority numbers.
There are four different types of synchronization replies the application can use in the enum cdb_subscription_sync_type parameter:
CDB_DONE_PRIORITY
This means that the application has acted on the subscription notification and CDB can continue to deliver further notifications.
CDB_DONE_SOCKET
This means that we are done. But regardless of priority, CDB shall not send any further notifications to us on our socket that are related to the currently executing transaction.
CDB_DONE_TRANSACTION
This means that CDB should not send any further notifications to any subscribers - including ourselves - related to the currently executing transaction.
CDB_DONE_OPERATIONAL
This should be used when a subscription notification for operational data has been read. It is the only type that should be used in this case, since the operational data does not have transactions and the notifications do not have priorities.
When using two phase subscriptions and cdb_read_subscription_socket2() has returned the type as CDB_SUB_PREPARE or CDB_SUB_ABORT the only valid response is CDB_DONE_PRIORITY.
For configuration data, the transaction that generated the subscription notifications is pending until all notifications have been acknowledged. A read lock on CDB is in effect while notifications are being delivered, preventing writes until delivery is complete.
For operational data, the writer that generated the subscription notifications is not directly affected, but the "subscription lock" remains in effect until all notifications have been acknowledged - thus subsequent attempts to obtain a "global" subscription lock, or a subscription lock using CDB_LOCK_PARTIAL for a non-disjuct subtree, will fail or block while notifications are being delivered (see cdb_start_session2() above). Write operations that don't attempt to obtain the subscription lock will proceed independent of the delivery of subscription notifications.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
After receiving a subscription notification (using cdb_read_subscription_socket()) but before acknowledging it (or aborting, in the case of prepare subscriptions), it is possible to send progress reports back to ConfD using the cdb_sub_progress() function. The socket sock must be the subscription socket, and it is allowed to call the function more than once to display more than one message. It is also possible to use this function in the diff-iterate callback function. A newline at the end of the string isn't necessary.
Depending on which north-bound interface that triggered the transaction, the string passed may be reported by that interface. Currently this is only presented in the CLI when the operator requests detailed reporting using the commit | details command.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
This function is to be called instead of cdb_sync_subscription_socket() when the subscriber wishes to abort the current transaction. It is only valid to call after cdb_read_subscription_socket2() has returned with type set to CDB_SUB_PREPARE. The arguments after sock are the same as to confd_X_seterr_extended() and give the caller a way of indicating the reason for the failure. Details can be found in the EXTENDED ERROR REPORTING section in the confd_lib_lib(3) manpage.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
This function does the same as cdb_sub_abort_trans(), and additionally gives the possibility to provide contents for the NETCONF <error-info> element. See the EXTENDED ERROR REPORTING section in the confd_lib_lib(3) manpage.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS
Returns the user session id for the transaction that triggered the current subscription notification. This function uses a subscription socket, and can only be called when a subscription notification for configuration data has been received on that socket, before cdb_sync_subscription_socket() has been called. Additionally, it is not possible to call this function from the iter() function passed to cdb_diff_iterate(). To retrieve full information about the user session, use maapi_get_user_session() (see confd_lib_maapi(3)).
Note: When the ConfD High Availability functionality is used, the user session information is not available on secondary nodes.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADSTATE, CONFD_ERR_NOEXISTS
Returns the transaction handle for the transaction that triggered the current subscription notification. This function uses a subscription socket, and can only be called when a subscription notification for configuration data has been received on that socket, before cdb_sync_subscription_socket() has been called. Additionally, it is not possible to call this function from the iter() function passed to cdb_diff_iterate().
A CDB client is not expected to access the ConfD transaction store directly - this function should only be used for logging or debugging purposes.
When the ConfD High Availability functionality is used, the transaction information is not available on secondary nodes.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADSTATE, CONFD_ERR_NOEXISTS
This function reads a value from the path in fmt and writes the result into the result parameter confd_value_t. The path must lead to a leaf element in the XML data tree. Note that for the C_BUF, C_BINARY, C_LIST, C_OBJECTREF, C_OID, C_QNAME, C_HEXSTR, and C_BITBIG confd_value_t types, the buffer(s) pointed to are allocated using malloc(3) - it is up to the user of this interface to free them using confd_free_value().
Errors: CONFD_ERR_NOEXISTS, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_BADTYPE
All the type safe versions of cdb_get() described below, as well as cdb_vget(), also have the same possible Errors. When the type of the read value is wrong, confd_errno is set to CONFD_ERR_BADTYPE and the function returns CONFD_ERR. The YANG type is given in the descriptions below.
Type safe variant of cdb_get() which is used to read int8 values.
Type safe variant of cdb_get() which is used to read int16 values.
Type safe variant of cdb_get() which is used to read int32 values.
Type safe variant of cdb_get() which is used to read int64 values.
Type safe variant of cdb_get() which is used to read uint8 values.
Type safe variant of cdb_get() which is used to read uint16 values.
Type safe variant of cdb_get() which is used to read uint32 values.
Type safe variant of cdb_get() which is used to read uint64 values.
Type safe variant of cdb_get() which is used to read bits values where the highest assigned bit position for the type is 31.
Type safe variant of cdb_get() which is used to read bits values where the highest assigned bit position for the type is above 31 and below 64.
Type safe variant of cdb_get() which is used to read bits values where the highest assigned bit position for the type is above 63. Upon successful return rval is pointing to a buffer of size bufsiz. It is up to the user of this function to free the buffer using free(3) when it is not needed any longer.
Type safe variant of cdb_get() which is used to read inet:ipv4-address values.
Type safe variant of cdb_get() which is used to read inet:ipv6-address values.
Type safe variant of cdb_get() which is used to read xs:float and xs:double values.
Type safe variant of cdb_get() which is used to read boolean values.
Type safe variant of cdb_get() which is used to read date-and-time values.
Type safe variant of cdb_get() which is used to read xs:date values.
Type safe variant of cdb_get() which is used to read xs:time values.
Type safe variant of cdb_get() which is used to read xs:duration values.
Type safe variant of cdb_get() which is used to read enumeration values. If we have:
The two enumeration values unbounded and infinity will occur as two #define integers in the .h file which is generated from the YANG module. Thus this function cdb_get_enum_value() populates an unsigned integer pointer.
Type safe variant of cdb_get() which is used to read instance-identifier values. Upon successful return rval is pointing to an allocated confd_hkeypath_t. It is up to the user of this function to free the hkeypath using confd_free_hkeypath() when it is not needed any longer.
Type safe variant of cdb_get() which is used to read object-identifier values. Upon successful return rval is pointing to an allocated struct confd_snmp_oid. It is up to the user of this function to free the struct using free(3) when it is not needed any longer.
Type safe variant of cdb_get() which is used to read string values. Upon successful return rval is pointing to a buffer of size bufsiz. It is up to the user of this function to free the buffer using free(3) when it is not needed any longer.
Type safe variant of cdb_get() which is used to read string values. If the buffer returned by cdb_get() fits into *n bytes CONFD_OK is returned and the buffer is copied into *rval. Upon successful return *n is set to the number of bytes copied into *rval.
Type safe variant of cdb_get() which is used to read string values. If the buffer returned by cdb_get() plus a terminating NUL fits into n bytes CONFD_OK is returned and the buffer is copied into *rval (as well as a terminating NUL character).
Type safe variant of cdb_get(), as cdb_get_buf() but for binary values. Upon successful return rval is pointing to a buffer of size bufsiz. It is up to the user of this function to free the buffer using free(3) when it is not needed any longer.
Type safe variant of cdb_get(), as cdb_get_buf() but for yang:hex-string values. Upon successful return rval is pointing to a buffer of size bufsiz. It is up to the user of this function to free the buffer using free(3) when it is not needed any longer.
Type safe variant of cdb_get() which is used to read xs:QName values. Note that prefixsz can be zero (in which case *prefix will be set to NULL). The space for prefix and name is allocated using malloc(), it is up to the user of this function to free them when no longer in use.
Type safe variant of cdb_get() which is used to read values of a YANG leaf-list. The function will malloc() an array of confd_value_t elements for the list, and return a pointer to the array via the **values parameter and the length of the array via the *n parameter. The caller must free the memory for the values (see cdb_get()) and the array itself. An example that reads and prints the elements of a list of strings:
Type safe variant of cdb_get() which is used to read inet:ipv4-prefix values.
Type safe variant of cdb_get() which is used to read inet:ipv6-prefix values.
Type safe variant of cdb_get() which is used to read decimal64 values.
Type safe variant of cdb_get() which is used to read identityref values.
Type safe variant of cdb_get() which is used to read tailf:ipv4-address-and-prefix-length values.
Type safe variant of cdb_get() which is used to read tailf:ipv6-address-and-prefix-length values.
Type safe variant of cdb_get() which is used to read yang:dotted-quad values.
This function does the same as cdb_get(), but takes a single va_list argument instead of a variable number of arguments - i.e. similar to vprintf(). Corresponding va_list variants exist for all the functions that take a path as a variable number of arguments.
In some cases it can be motivated to read multiple values in one request - this will be more efficient since it only incurs a single round trip to ConfD, but usage is a bit more complex. This function reads at most n values from the container or list entry specified by the path, and places them in the values array, which is provided by the caller. The array is populated according to the specification of the Value Array format in the XML STRUCTURES section of the confd_types(3) manual page.
When reading from a container or list entry with mixed configuration and operational data (i.e. a config container or list entry that has some number of operational elements), some elements will have the "wrong" type - i.e. operational data in a session for CDB_RUNNING/CDB_STARTUP, or config data in a session for CDB_OPERATIONAL. Leaf elements of the "wrong" type will have a "value" of C_NOEXISTS in the array, while static or (existing) optional sub-container elements will have C_XMLTAG in all cases. Sub-containers or leafs provided by external data providers will always be represented with C_NOEXISTS, whether config or not.
On success, the function returns the actual number of elements in the container or list entry. I.e. if the return value is bigger than n, only the values for the first n elements are in the array, and the remaining values have been discarded. Note that given the specification of the array contents, there is always a fixed upper bound on the number of actual elements, and if there are no presence sub-containers, the number is constant.
As an example, with the YANG fragment in the PATHS section above, this code could be used to read the values for interface "eth0" on host "buzz":
In this simple example, we assumed that the application was aware of the details of the data model, specifically that a confd_value_t array of length 4 would be sufficient for the values we wanted to retrieve, and at which positions in the array those values could be found. If we make use of schema information loaded from the ConfD daemon into the library (see confd_types(3)), we can avoid "hardwiring" these details. The following, more complex, example does the same as the above, but using only the names (in the form of #defines from the header file generated by confdc --emit-h) of the relevant leafs:
See confd_lib_lib(3) for the specification of the confd_max_object_size() and confd_next_object_node() functions. Also worth noting is that the return value from confd_max_object_size() is a constant for a given node in a given data model - thus we could optimize the above by calling confd_max_object_size() only at the first invocation of cdb_get_object() for a given node, making use of the opaque element of struct confd_cs_node to store the value:
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH
Similar to cdb_get_object(), but reads multiple entries of a list based on the "instance integer" otherwise given within square brackets in the path - here the path must specify the list without the instance integer. At most n values from each of nobj entries, starting at entry ix, are read and placed in the values array.
The array must be at least n * nobj elements long, and the values for list entry ix + i start at element array[i * n] (i.e. ix starts at array[0], ix+1 at array[n], and so on). On success, the highest actual number of values in any of the list entries read is returned. An error (CONFD_ERR_NOEXISTS) will be returned if we attempt to read more entries than actually exist (i.e. if ix + nobj - 1 is outside the range of actually existing list entries). Example - read the data for all interfaces on the host "buzz" (assuming that we have memory enough for that):
This simple example can of course be enhanced to use loaded schema information in a similar manner as for cdb_get_object() above.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOEXISTS
Read an arbitrary set of sub-elements of a container or list entry. The values array must be pre-populated with n values based on the specification of the Tagged Value Array format in the XML STRUCTURES section of the confd_types(3) manual page, where the confd_value_t value element is given as follows:
C_NOEXISTS means that the value should be read from CDB and stored in the array.
C_PTR also means that the value should be read from CDB, but instead gives the expected type and a pointer to the type-specific variable where the value should be stored. Thus this gives a functionality similar to the type safe versions of
cdb_get().C_XMLBEGIN and C_XMLEND are used as per the specification.
Key values to select list entries can be given with their values.
As a special case, the "instance integer" can be used to select a list entry by using C_CDBBEGIN instead of C_XMLBEGIN (and no key values).
When we use C_PTR, we need to take special care to free any allocated memory. When we use C_NOEXISTS and the value is stored in the array, we can just use confd_free_value() regardless of the type, since the confd_value_t has the type information. But with C_PTR, only the actual value is stored in the pointed-to variable, just as for cdb_get_buf(), cdb_get_binary(), etc, and we need to free the memory specifically allocated for the types listed in the description of cdb_get() above. See the corresponding cdb_get_xxx() functions for the details of how to do this.
All elements have the same position in the array after the call, in order to simplify extraction of the values - this means that optional elements that were requested but didn't exist will have C_NOEXISTS rather than being omitted from the array. However requesting a list entry that doesn't exist, or requesting non-CDB data, or operational vs config data, is an error. Note that when using C_PTR, the only indication of a non-existing value is that the destination variable has not been modified - it's up to the application to set it to some "impossible" value before the call when optional leafs are read.
In this rather complex example we first read only the "name" and "enabled" values for all interfaces, and then read "ip" and "mask" for those that were enabled - a total of two requests. Note that since the "interface" list begin/end elements are in the array, the path must not include the "interface" component. When reading values from a single container, it is generally simpler to have the container component (and keys or instance integer) in the path instead.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_BADTYPE, CONFD_ERR_NOEXISTS
When we use the YANG choice statement in the data model, this function can be used to find the currently selected case, avoiding useless cdb_get() etc requests for elements that belong to other cases. The fmt, ... arguments give the path to the container or list entry where the choice is defined, and choice is the name of the choice. The case value is returned to the confd_value_t that rcase points to, as type C_XMLTAG - i.e. we can use the CONFD_GET_XMLTAG() macro to retrieve the hashed tag value. If no case is currently selected (i.e. for an optional choice that doesn't have a default case), the function will fail with CONFD_ERR_NOEXISTS.
If we have "nested" choices, i.e. multiple levels of choice statements without intervening container or list statements in the data model, the choice argument must give a '/'-separated path with alternating choice and case names, from the data node given by the fmt, ... arguments to the specific choice that the request pertains to.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOEXISTS
Retrieve attributes for a config node. These attributes are currently supported:
The attrs parameter is an array of attributes of length num_attrs, specifying the wanted attributes - if num_attrs is 0, all attributes are retrieved. If no attributes are found, *num_vals is set to 0, otherwise an array of confd_attr_value_t elements is allocated and populated, its address stored in *attr_vals, and *num_vals is set to the number of elements in the array. The confd_attr_value_t struct is defined as:
If any attribute values are returned (*num_vals > 0), the caller must free the allocated memory by calling confd_free_value() for each of the confd_value_t elements, and free(3) for the *attr_vals array itself.
Errors: CONFD_ERR_NOEXISTS, CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_BADTYPE
This function does the same as cdb_get_attrs(), but takes a single va_list argument instead of a variable number of arguments - i.e. similar to vprintf(). Corresponding va_list variants exist for all the functions that take a path as a variable number of arguments.
Operational Data
It is possible for an application to store operational data (i.e. status and statistical information) in CDB, instead of providing it on demand via the callback interfaces described in the confd_lib_dp(3) manual page. The operational database has no transactions and normally avoids the use of locks in order to provide light-weight access methods, however when the multi-value API functions below are used, all updates requested by a given function call are carried out atomically. Read about how to specify the storage of operational data in CDB via the tailf:cdb-oper extension in the tailf_yang_extensions(5) manual page.
To establish a session for operational data, the application needs to use cdb_connect() with CDB_DATA_SOCKET and cdb_start_session() with CDB_OPERATIONAL. After this, all the read and access functions above are available for use with operational data, and additionally the write functions described below. Configuration data can not be accessed in a session for operational data, nor vice versa - however it is possible to have both types of sessions active simultaneously on two different sockets, or to alternate the use of one socket via cdb_end_session(). The write functions can never be used in a session for configuration data.
In order to trigger subscriptions on operational data, we must obtain a subscription lock via the use of cdb_start_session2() instead of cdb_start_session(), see above.
In YANG it is possible to define a list of operational data without any keys. For this type of list, we use a single "pseudo" key which is always of type C_INT64. This key isn't visible in the northbound agent interfaces, but is used in the functions described here just as if it was a "normal" key.
There are two different functions to set the value of a single leaf. The first takes the value from a confd_value_t struct, the second takes the string representation of the value.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_BADTYPE, CONFD_ERR_NOT_WRITABLE
This function does the same as cdb_set_elem(), but takes a single va_list argument instead of a variable number of arguments - i.e. similar to vprintf(). Corresponding va_list variants exist for all the functions that take a path as a variable number of arguments.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_BADTYPE, CONFD_ERR_NOT_WRITABLE
Create a new list entry, presence container, or leaf of type empty. Note that for list entries and containers, sub-elements will not exist until created or set via some of the other functions, thus doing implicit create via cdb_set_object() or cdb_set_values() may be preferred in this case.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOT_WRITABLE, CONFD_ERR_NOTCREATABLE, CONFD_ERR_ALREADY_EXISTS
Delete a list entry, presence container, or leaf of type empty, and all its child elements (if any).
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOT_WRITABLE, CONFD_ERR_NOTDELETABLE, CONFD_ERR_NOEXISTS
Set all elements corresponding to the complete contents of a container or list entry, except for sub-lists. The values array must be 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.
If the container or list entry itself, or any sub-elements that are specified as existing, do not exist before this call, they will be created, otherwise the existing values will be updated. Non-mandatory leafs and presence containers that are specified as not existing in the array, i.e. with value C_NOEXISTS, will be deleted if they existed before the call.
When writing to a container with mixed configuration and operational data (i.e. a config container or list entry that has some number of operational elements), all config leaf elements must be specified as C_NOEXISTS in the corresponding array elements, while config sub-container elements are specified with C_XMLTAG just as for operational data.
For a list entry, since the key elements must be present in the array, it is not required that the key values are included in the path given by fmt. If the key values are included in the path, the values of the key elements in the array are ignored.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_BADTYPE, CONFD_ERR_NOT_WRITABLE
Set arbitrary sub-elements of a container or list entry. The values array must be 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.
If the container or list entry itself, or any sub-elements that are specified as existing, do not exist before this call, they will be created, otherwise the existing values will be updated. Both mandatory and optional elements may be omitted from the array, and all omitted elements are left unchanged. To actually delete a non-mandatory leaf or presence container as described for cdb_set_object(), it may (as an extension of the format) be specified as C_NOEXISTS instead of being omitted.
For a list entry, the key values can be specified either in the path or via key elements in the array - if the values are in the path, the key elements can be omitted from the array. For sub-lists present in the array, the key elements must of course always also be present though, immediately following the C_XMLBEGIN element and in the order defined by the data model. It is also possible to delete a list entry by using a C_XMLBEGINDEL element, followed by the keys in data model order, followed by a C_XMLEND element.
For a list without keys (see above), the "pseudo" key may (or in some cases must) be present in the array, but of course there is no tag value for it, since it isn't present in the data model. In this case we must use a tag value of 0, i.e. it can be set with code like:
The same method is used when reading data from such a list with the cdb_get_values() function described above.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_BADTYPE, CONFD_ERR_NOT_WRITABLE
When we use the YANG choice statement in the data model, this function can be used to select the current case. When configuration data is modified by northbound agents, the current case is implicitly selected (and elements for other cases potentially deleted) by the setting of elements in a choice. For operational data in CDB however, this is under direct control of the application, which needs to explicitly set the current case. Setting the case will also automatically delete elements belonging to other cases, but it is up to the application to not set any elements in the "wrong" case.
The fmt, ... arguments give the path to the container or list entry where the choice is defined, and choice and scase are the choice and case names. For an optional choice, it is possible to have no case at all selected. To indicate that the previously selected case should be deleted without selecting another case, we can pass NULL for the scase argument.
If we have "nested" choices, i.e. multiple levels of choice statements without intervening container or list statements in the data model, the choice argument must give a '/'-separated path with alternating choice and case names, from the data node given by the fmt, ... arguments to the specific choice that the request pertains to.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_NOTDELETABLE
This function sets an attribute for a path in fmt. The path must lead to an operational config node. See cdb_get_attrs for the supported attributes.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH, CONFD_ERR_BADTYPE, CONFD_ERR_NOT_WRITABLE, CONFD_ERR_NOEXISTS
This function does the same as cdb_set_attr(), but takes a single va_list argument instead of a variable number of arguments - i.e. similar to vprintf(). Corresponding va_list variants exist for all the functions that take a path as a variable number of arguments.
Ncs Specific Functions
Does the same thing as confd_cs_node_cd() (see confd_lib_lib(3)), but can handle paths that are ambiguous due to traversing a mount point, by sending a request to the NSO daemon. To be used when confd_cs_node_cd() returns NULL with confd_errno set to CONFD_ERR_NO_MOUNT_ID.
Errors: CONFD_ERR_MALLOC, CONFD_ERR_OS, CONFD_ERR_BADPATH
See Also
confd_lib(3) - Confd lib
confd_types(3) - ConfD C data types
The ConfD User Guide
confd_lib_dp
confd_lib_dpconfd_lib_dp - callback library for connecting data providers to ConfD
Synopsis
Library
ConfD Library, (libconfd, -lconfd)
Description
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.
Functions
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 alwaysC_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_DISCONNECTflag, 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. Similarlyset_case()will be issued with the default case for choices that have one.When the
CONFD_DAEMON_FLAG_NO_DEFAULTSflag is set, ConfD will only issueset_elem()callbacks when values have been explicitly set, andset_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 aset_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_DEFAULTSflag set must reply toget_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 toget_case()with C_DEFAULT when the default case is in effect.
CONFD_DAEMON_FLAG_PREFER_BULK_GET
This flag requests that the
get_object()callback rather thanget_elem()should be used whenever possible, regardless of whether a "bulk hint" is given by the northbound agent. Ifget_elem()is not registered, the flag is not useful (it has no effect -get_object()is always used anyway), but in cases where the callpoint also covers leafs that cannot be retrieved withget_object(), the daemon must registerget_elem().
CONFD_DAEMON_FLAG_BULK_GET_CONTAINER
This flag tells ConfD that the data provider is prepared to handle a
get_object()callback invocation for the toplevel ancestor container when a leaf is requested by a northbound agent, if there exists no ancestor list node but there exists such a container. If this flag is not set,get_object()is only invoked for list entries, andget_elem()is always used for leafs that do not have an ancestor list node. If bothget_object()andget_elem()are registered, the choice between them is made as for list entries, i.e. based on a "bulk hint" from the northbound agent unless the flagCONFD_DAEMON_FLAG_PREFER_BULK_GETis also set (see above).
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).
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 theinit()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.
The struct confd_trans_cbs is defined as:
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 truein 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.
libconfdwill allocate a transaction context on behalf of the transaction and give this newly allocated structure as an argument to theinit()callback. The structure is defined as: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
libconfdwhich 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 thefdfield 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()orfinish()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 theconfd_delayed_reply_error()function with the special error string "locked". An alternate way to indicate that the database is already locked is to useconfd_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" caseconfd_delayed_reply_error()must be called with a NULL argument afterconfd_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 useconfd_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" caseconfd_delayed_reply_error()must be called with a NULL argument afterconfd_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 useconfd_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" caseconfd_delayed_reply_error()must be called with a NULL argument afterconfd_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 thet_opaquefield in theconfd_trans_ctxto 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()withcodeCONFD_ERRCODE_INTERRUPTshould 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, ConfD has been configured to support confirmed-commit and the revertByCommit isn't enabled, 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. If the revertByCommit feature is enabled the potential rollback to previous running configuration is done using normal reversed commits, hence no checkpointing support is required in this case. 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:
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 acandidate_confirming_commit(). Thetimeoutparameter 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 thecandidate_confirming_commit(), we MUST restart with a rollback. Thus we must remember that we are waiting for acandidate_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
timeoutin thecandidate_commit()function is != 0, we will be either invoked here or in thecandidate_rollback_running()function withintimeoutseconds.candidate_confirming_commit()should make the commit persistent, whereas a call tocandidate_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, confirmed-commit is enabled and revertByCommit is disabled.
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 unless revertByCommit is enabled.
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 unless revertByCommit is enabled.
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. Thepaths[]argument is annpathslong array of hkeypaths that identify the leafs and/or subtrees that are to be locked. Thelockidis a reference that will be used on a subsequent correspondingunlock_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:
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.
The flags field in the struct confd_data_cbs can have the flag CONFD_DATA_WANT_FILTER set. See the function get_next() for details.
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 or the value with list of attributes, of a specific leaf. Assuming we have the following data model:
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 value can optionally be returned with the attributes of the ip leaf throughconfd_data_reply_value_attrs().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()orconfd_data_reply_value_attrs()(orconfd_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 toconfd_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 useconfd_data_reply_value()orconfd_data_reply_value_attrs()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 invokesget_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, or a set of leaf-list elements. The
nextparameter will be-1on the first invocation. This function should reply by means of the functionconfd_data_reply_next_key()or optionallyconfd_data_reply_next_key_attrs()that includes the attributes of list entry in the reply.If the list has a
tailf:secondary-indexstatement (see tailf_yang_extensions(5)), and the entries are supposed to be retrieved according to one of the secondary indexes, the variabletctx->secondary_indexwill be set to a value greater than0, indicating which secondary-index is used. The first secondary-index in the definition is identified with the value1, the second with2, and so on. confdc can be used to generate#defines for the index names. If no secondary indexes are defined, or if the sort order should be according to the key values,tctx->secondary_indexis0.If the flag CONFD_DATA_WANT_FILTER is set in the
flagsfields instruct confd_data_cbs, ConfD may pass a filter to the data provider (e.g., if the list traversal is done due to an XPath evaluation). The filter can be seen as a hint to the data provider to optimize the list retrieval; the data provider can use the filter to ensure that it doesn't return any list entries that don't match the filter. Since it is a hint, it is ok if it returns entries that don't match the filter. However, if the data provider guarantees that all entries returned match the filter, it can set the flag CONFD_TRANS_CB_FLAG_FILTERED intctx->cb_flagsbefore callingconfd_data_reply_next_keyorconfd_data_reply_next_key_attrs(). In this case, ConfD will not re-evaluate the filters. The CONFD_TRANS_CB_FLAG_FILTERED flag should only be set when a list filter is available.The function
confd_data_get_list_filter()can be used by the data provider to get the filter when the first list entry is requested.To signal that no more entries exist, we reply with a NULL pointer as the key value in the
confd_data_reply_next_key()orconfd_data_reply_next_key_attrs()functions.The field
tctx->traversal_idcontains a unique identifier for each list traversal. I.e., it is set to a unique value before the first element is requested, and then this value is kept as the list is being traversed. If a new traversal is started, a new unique value is set.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()orconfd_data_reply_next_key_attrs().For a list that does not specify a non-default sort order by means of an
ordered-by userortailf:sort-orderstatement, ConfD assumes that list entries are ordered strictly by increasing key (or secondary index) values. I.e., CDB's sort order. Thus, for correct operation, we must observe this order when returning list entries in a sequence ofget_next()calls.A special case is the union type key. Entries are ordered by increasing key for their type while types are sorted in the order of appearance in 'enum confd_vtype', see confd_types(3). There are exceptions to this rule, namely these five types, which are always sorted at the end:
C_BUF,C_DURATION,C_INT32,C_UINT8, andC_UINT16. Among these,C_BUFalways comes first, and after that comesC_DURATION. Then follows the three integer types,C_INT32,C_UINT8andC_UINT16, which are sorted together in natural number order regardless of type.If CDB's sort order cannot be provided to ConfD for configuration data, /confdConfig/sortTransactions should be set to 'false'. See confd.conf(5).
set_elem()
This callback writes the value of a leaf. Note that an optional leaf with a type other than
emptyis 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
presencecontainer, a leaf of typeempty, or a leaf-list element. 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 userin the YANG data model, thecreate()callback must add the list entry to the end of the list.
remove()
This callback is used to remove an existing list entry or
presencecontainer and all its sub nodes (if any), an optional leaf, or a leaf-list element. When we use the YANGchoicestatement in the data model, it may also be used to remove nodes that are not optional as such when a differentcase(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
presencecontainers or leafs of typeempty, we cannot use theget_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: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 namelyint32, and thus the existence of that leaf will be determined through a call to theget_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, or for a leaf-list entry. Normally this existence test is done with aget_elem()request for the first key, but since there are no keys, this callback is used instead. Thus if we have such lists, or leaf-lists, we must also implement this callback, and handle a request where the keypath identifies a list entry or a leaf-list element.The callback must reply to ConfD using either the
confd_data_reply_not_found()or theconfd_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 anextparameter to indicate which keys should be returned, this callback instead provides atypeparameter and a set of values to indicate which keys should be returned. Just like forget_next(), the callback should reply by callingconfd_data_reply_next_key()orconfd_data_reply_next_key_attrs()with the keys for the requested list entry.The
keysparameter is a pointer to ankeyselements long array of key values, or secondary index-leaf values (see below). Thetypecan have one of two values:
CONFD_FIND_NEXTThe callback should always reply with the key values for the first list entry after the one indicated by the
keysarray, and anextvalue appropriate for retrieval of subsequent entries. Thekeysarray 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 firstnkeysvalues 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_NEXTIf the values in the
keysarray completely identify an actual existing list entry, the callback should reply with the keys for this list entry and a correspondingnextvalue. Otherwise the same logic as described forCONFD_FIND_NEXTshould be used.The
dp/find_nextexample in the bundled examples collection has an implementation of thefind_next()callback for a list with two integer keys. It shows how thetypevalue 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-indexstatement (see tailf_yang_extensions(5)), the callback must examine the value of thetctx->secondary_indexvariable, as described for theget_next()callback. Iftctx->secondary_indexhas a value greater than0, thekeysandnkeysparameters do not represent key values, but instead values for the index leafs specified by thetailf:index-leafsstatement for the secondary index. The callback should however still reply with the actual key values for the list entry in theconfd_data_reply_next_key()orconfd_data_reply_next_key_attrs()call.Once we have called
confd_data_reply_next_key()orconfd_data_reply_next_key_attrs(), ConfD will useget_next()(orget_next_object()) for any subsequent entry-by-entry list traversal - however we can request that this traversal should be done usingfind_next()(orfind_next_object()) instead, by passing-1for thenextparameter toconfd_data_reply_next_key()orconfd_data_reply_next_key_attrs(). In this case ConfD will always invokefind_next()/find_next_object()withtypeCONFD_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-1for thenextparameter toconfd_data_reply_next_key()orconfd_data_reply_next_key_attrs()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()orconfd_data_reply_next_key_attrs()function.The field
tctx->traversal_idcontains a unique identifier for each list traversal. I.e., it is set to a unique value before the first element is requested, and then this value is kept as the list is being traversed. If a new traversal is started, a new unique value is set.For a list that does not specify a non-default sort order by means of an
ordered-by userortailf:sort-orderstatement, ConfD assumes that list entries are ordered strictly by increasing key (or secondary index) values. I.e., CDB's sort order. Thus, for correct operation, we must observe this order when returning list entries in a sequence ofget_next()calls.A special case is the union type key. Entries are ordered by increasing key for their type while types are sorted in the order of appearance in 'enum confd_vtype', see confd_types(3). There are exceptions to this rule, namely these five types, which are always sorted at the end:
C_BUF,C_DURATION,C_INT32,C_UINT8, andC_UINT16. Among these,C_BUFalways comes first, and after that comesC_DURATION. Then follows the three integer types,C_INT32,C_UINT8andC_UINT16, which are sorted together in natural number order regardless of type.If CDB's sort order cannot be provided to ConfD for configuration data, /confdConfig/sortTransactions should be set to 'false'. See confd.conf(5).
If we have registered
find_next()(orfind_next_object()), it is not strictly necessary to also registerget_next()(orget_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 neitherget_next()norget_next_object()is registered, ConfD will instead invokefind_next()(orfind_next_object()), the same way as if-1had been passed for thenextparameter toconfd_data_reply_next_key()orconfd_data_reply_next_key_attrs()as described above - the actualnextvalue passed is ignored. The very first get_next request for a traversal (i.e. where thenextparameter would be-1) will cause a find_next invocation withtypeCONFD_FIND_NEXTandnkeys== 0, i.e. no keys provided.Similar to the
get_next()callback, a filter may be used to optimize the list retrieval, if the flag CONFD_DATA_WANT_FILTER is set intctx->flagsfield. Otherwise this field should be set to 0.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()orconfd_data_reply_next_key_attrs().
num_instances()
This callback can optionally be implemented. The purpose is to return the number of entries in a list, or the number of elements in a leaf-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_tvalue of type C_INT32. The value is returned through a call toconfd_data_reply_value(), see code example below: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().By default, 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, theget_elem()callback may be omitted whenget_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 forget_elem().However, if the
CONFD_DAEMON_FLAG_BULK_GET_CONTAINERflag is set viaconfd_set_daemon_flags(),get_object()will also be used for the toplevel ancestor container (if any) when no ancestor list node exists. I.e. in this case,get_elem()is only needed for toplevel leafs - if there are any such leafs in the part of the data model covered by a given callpoint.When ConfD invokes the
get_elem()callback, it is the responsibility of the application to issue calls to the reply functionconfd_data_reply_value(). Theget_object()callback cannot use this function since it needs to return a sequence of values. Theget_object()callback must use one of the three functionsconfd_data_reply_value_array(),confd_data_reply_tag_value_array()orconfd_data_reply_tag_value_attrs_array(). See the description of these functions below for the details of the arguments passed. If the entry requested does not exist, the callback must callconfd_data_reply_not_found().

