Module econfd_cdb

An Erlang interface equivalent to the CDB C-API (documented in confd_lib_cdb(3)).

Copyright © 2006 Tail-F Systems AB

Version: {$Id$}

Description

An Erlang interface equivalent to the CDB C-API (documented in confd_lib_cdb(3)).

The econfd_cdb library is used to connect to the ConfD built in XML database, CDB. The purpose of this API 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, the CLI, the Web UI, or MAAPI modifies the data so that the application can re-read the configuration data and act accordingly.

Paths

In the C lib a path is a string. Assume the following YANG fragment:

         container hosts {
           list host {
             key name;
             leaf name {
               type string;
             }
             leaf domain {
               type string;
             }
             leaf defgw {
               type inet:ip-address;
             }
             container interfaces {
               list interface {
                 key name;
                 leaf name {
                   type string;
                 }
                 leaf ip {
                   type inet:ip-address;
                 }
                 leaf mask {
                   type inet:ip-address;
                 }
                 leaf enabled {
                   type boolean;
                 }
               }
             }
           }
         }
Furthermore assume the database is populated with the following data
             <hosts xmlns="http://acme.com/ns/hst/1.0">
                <host>
                  <name>buzz</name>
                  <domain>tail-f.com</domain>
                  <defgw>192.168.1.1</defgw>
                  <interfaces>
                    <interface>
                      <name>eth0</name>
                      <ip>192.168.1.61</ip>
                      <mask>255.255.255.0</mask>
                      <enabled>true</enabled>
                    </interface>
                    <interface>
                      <name>eth1</name>
                      <ip>10.77.1.44</ip>
                      <mask>255.255.0.0</mask>
                      <enabled>false</enabled>
                    </interface>
                  </interfaces>
                </host>
              </hosts>

The format path "/hosts/host{buzz}/defgw" refers to the leaf element called defgw of the host whose key (name element) is buzz.

The format path "/hosts/host{buzz}/interfaces/interface{eth0}/ip" refers to the leaf element called "ip" in the "eth0" interface of the host called "buzz".

In the Erlang CDB and MAAPI interfaces we use ikeypath() lists instead to address individual objects in the XML tree. The IkeyPath is backwards, thus the two above paths are expressed as
   [defgw, {<<"buzz">>}, host, [NS|hosts]]
   [ip, {<<"eth0">>}, interface, interfaces, {<<"buzz">>}, host, [NS|hosts]]
It is possible loop through all entries in a list as in:
   N = econfd_cdb:num_instances(CDB, [host,[NS|hosts]]),
   lists:map(fun(I) ->
               econfd_cdb:get_elem(CDB, [defgw,[I],host,[NS|hosts]]), .......
             end, lists:seq(0, N-1))
   
Thus in the list with length N [Index] is an implicit key during the life of a CDB read session.

Data Types

cdb_sess()

cdb_sess() = #cdb_session{}

A datastructure which is used as a handle to all the of the access functions

dbtype()

dbtype() = '?CDB_RUNNING' | '?CDB_STARTUP' | '?CDB_OPERATIONAL' | '?CDB_PRE_COMMIT_RUNNING'

When we open CDB sessions we must choose which database to read or write from/to. These ints are defined in econfd.hrl

err()

err() = {error, {integer(), binary()}} | {error, closed}

Errors can be either

sub_ns()

sub_ns() = econfd:namespace() | ''

A namespace or use '' as wildcard (any namespace)

sub_type()

sub_type() = '?CDB_SUB_RUNNING' | '?CDB_SUB_RUNNING_TWOPHASE' | '?CDB_SUB_OPERATIONAL'

Subscription type

subscription_sync_type()

subscription_sync_type() = '?CDB_DONE_PRIORITY' | '?CDB_DONE_SOCKET' | '?CDB_DONE_TRANSACTION' | '?CDB_DONE_OPERATIONAL'

Return value from the fun passed to wait/3, indicating what to do with further notifications coming from this transaction. These ints are defined in econfd.hrl

Function Index

choice_path/1
cli_diff_iterate/5Iterate over changes in CDB after a subscription triggers.
close/1End the session and close the socket.
connect/0Equivalent to connect({127, 0, 0, 1}).
connect/1Connect to CDB on the host with address Address.
connect/2Connect to CDB on the host with address Address:Port.
connect/3Connect to CDB on the host with address Address:Port.
create/2Only for CDB operational data: Create the element denoted by IKP.
delete/2Only for CDB operational data: Delete the element denoted by IKP.
diff_iterate/5Iterate over changes in CDB after a subscription triggers.
end_session/1Terminate the session.
exists/2Checks existense of an object.
get_case/3Returns the current case for a choice.
get_elem/2Read an element.
get_modifications_cli/2
get_modifications_cli/3Return Return a string with the CLI commands that corresponds to the changes that triggered subscription.
get_object/2Returns all the values in a container or list entry.
get_objects/4Returns all the values for NumEntries list entries, starting at index StartIndex.
get_phase/1Get CDB start-phase.
get_txid/1Get CDB transaction id.
get_values/3Returns the values for the leafs that have the "value" 'not_found' in the Values list.
index/2Returns the position (starting at 0) of the list entry in path.
mop_2_str/1
new_session/2Initiate a new session using the socket returned by connect/1 or connect/2.
new_session/3Initiate a new session using the socket returned by connect/1 or connect/2, with detailed control via the Flags argument.
next_index/2Returns the position (starting at 0) of the list entry after the given path (which can be non-existing, and if multiple keys the last keys can be '*').
num_instances/2Returns the number of entries in a list.
set_case/4Only for CDB operational data: Set the case for a choice.
set_elem/3Only for CDB operational data: Write Value into CDB.
set_elem2/3Only for CDB operational data: Write ValueBin into CDB.
set_object/3Only for CDB operational data: Write an entire object, i.e.
set_values/3Only for CDB operational data: Write a list of tagged values.
subscribe/3Equivalent to subscribe(CDB, Prio, 0, MatchKeyString).
subscribe/4Set up a CDB configuration subscription.
subscribe/5Equivalent to subscribe(CDB, Type, 0, Prio, Ns, MatchKeyString).
subscribe/6Generalized subscription, where Type is one of
  • ?CDB_SUB_RUNNING - traditional commit subscription, same as subscribe/4.
  • ?CDB_SUB_RUNNING_TWOPHASE - two phase subscription, i.e. notification will be received for prepare, commit, and possibly abort.
  • ?CDB_SUB_OPERATIONAL - subscription for changes to CDB operational data.
Flags is either 0 or:
  • ?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 originated the abort.
subscribe_done/1After a subscriber is done with all subscriptions and ready to receive updates this subscribe_done/1 must be called.
subscribe_session/1Initialize a subscription socket.
trigger_subscriptions/1Equivalent to trigger_subscriptions(Socket, all).
trigger_subscriptions/2Trigger CDB subscribers as if a update in the configuration had been done.
wait/3Wait for subscription events.
wait_start/1Wait for CDB to become available (reach start-phase one).

Function Details

choice_path/1

choice_path(Tag) -> any()

cli_diff_iterate/5

cli_diff_iterate(CDB::cdb_sess(), Point::integer(), Fun::fun((IKP::econfd:ikeypath(), Op::integer(), Oval::econfd:value() | undefined, Eval::econfd:value() | undefined | econfd:key() | {}, CliStr::binary(), CliTokens::[{econfd:value(), binary()}], State::term()) -> {ok, Ret::integer(), State2::term()} | {error, term()}), Flags::integer(), InitState::term()) -> {ok, State::term()} | {error, term()}

Iterate over changes in CDB after a subscription triggers. This function works just like diff_iterate/5, except the fun will be invoked with two additional arguments, CliStr and CliTokens. CliStr is a string containing the (C-style) rendering of the CLI commands equivalent to the current keypath/operation. CliTokens is a list representing the CLI string broken down by token.

close/1

close(Socket::term() | cdb_sess()) -> ok | {error, Reason::term()}

End the session and close the socket

connect/0

connect() -> {ok, Socket::term()} | {error, Reason::term()}

Equivalent to connect({127, 0, 0, 1}).

connect/1

connect(Address::ip()) -> {ok, Socket::term()} | {error, Reason::term()}

Connect to CDB on the host with address Address

connect/2

connect(Address::ip(), Port::integer()) -> {ok, Socket::term()} | {error, Reason::term()}

Connect to CDB on the host with address Address:Port

If the port is changed it must also be changed in confd.conf

connect/3

connect(Address::ip(), Port::integer(), ClientName::binary()) -> {ok, Socket::term()} | {error, Reason::term()}

Connect to CDB on the host with address Address:Port

If the port is changed it must also be changed in confd.conf A call to cdb_connect() is typically followed by a call to either new_session() for a reading session or a call to subscribe_session() for a subscription socket or calls to any of the write API functions for a data socket. ClientName is a string which confd will use as an identifier when e.g. reporting status.

create/2

create(CDB::cdb_sess(), IKP::econfd:ikeypath()) -> ok | err()

Only for CDB operational data: Create the element denoted by IKP.

delete/2

delete(CDB::cdb_sess(), IKP::econfd:ikeypath()) -> ok | err()

Only for CDB operational data: Delete the element denoted by IKP.

diff_iterate/5

diff_iterate(CDB::cdb_sess(), Point::integer(), Fun::fun((IKP::econfd:ikeypath(), Op::integer(), Oval, Eval, State::term()) -> {ok, Ret::integer(), State2::term()}) | {error, term()}, Flags::integer(), InitState::term()) -> {ok, State::term()} | {error, term()}

Iterate over changes in CDB after a subscription triggers. This function can be called from within the fun passed to wait/3. When called it will invoke Fun for each change that matched the Point. If Flags is ?CDB_ITER_WANT_PREV, Oval will be the previous value (if available). When Oval or Eval is not available (or requested) they will be the atom 'undefined'. When Op == ?MOP_MOVED_AFTER (only for "ordered-by user" list entry), Eval == {} means that the entry was moved first in the list, otherwise Eval is a econfd:key() tuple that identifies the entry it was moved after.

end_session/1

end_session(CDB::cdb_sess()) -> {ok, Socket::term()}

Terminate the session. This releases the lock on CDB which is active during a read session. Returns a socket that can be re-used in new_session/2 We use 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 new_session/2. %% While we have a live CDB read session, CDB is 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 end_session() or close() once we have read what we wish to read.

exists/2

exists(CDB::cdb_sess(), IKP::econfd:ikeypath()) -> {ok, boolean()} | err()

Checks existense of an object. 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, returning Int == 1 if it exists, Int == 0 if not.

get_case/3

get_case(CDB::cdb_sess(), IKP::econfd:ikeypath(), Choice::econfd:qtag() | [econfd:qtag()]) -> {ok, econfd:qtag()} | err()

Returns the current case for a choice.

get_elem/2

get_elem(CDB::cdb_sess(), IKP::econfd:ikeypath()) -> {ok, Value::econfd:value()} | err()

Read an element. Note, the C interface has separate get functions for different types.

get_modifications_cli/2

get_modifications_cli(CDB, Point) -> any()

get_modifications_cli/3

get_modifications_cli(CDB::cdb_sess(), Point::integer(), Flags::integer()) -> {ok, CliString::binary()} | {error, term()}

Return Return a string with the CLI commands that corresponds to the changes that triggered subscription.

get_object/2

get_object(CDB::cdb_sess(), IKP::econfd:ikeypath()) -> {ok, [econfd:value()]} | err()

Returns all the values in a container or list entry.

get_objects/4

get_objects(CDB::cdb_sess(), IKP::econfd:ikeypath(), StartIndex::integer(), NumEntries::integer()) -> {ok, [[econfd:value()]]} | err()

Returns all the values for NumEntries list entries, starting at index StartIndex. The return value has one Erlang list for each YANG list entry, i.e. it is a list of NumEntries lists.

get_phase/1

get_phase(Socket::term()) -> {ok, {0 | 1 | 2, false | init | upgrade}} | err()

Get CDB start-phase

get_txid/1

get_txid(Socket::term()) -> {ok, {MasterNode::term(), Now::tuple()}} | {ok, Now::tuple()}

Get CDB transaction id. When we are a cdb client, and ConfD restarts, we can use this function to retrieve the last CDB transaction id. If it the same as earlier we don't need re-read the CDB data. This is also useful when we're a CDB client in a HA setup.

get_values/3

get_values(CDB::cdb_sess(), IKP::econfd:ikeypath(), Values::[econfd:tagval()]) -> {ok, [econfd:tagval()]} | err()

Returns the values for the leafs that have the "value" 'not_found' in the Values list. This can be used to read an arbitrary set of sub-elements of a container or list entry. The return value is a list of the same length as Values, i.e. the requested leafs are in the same position in the returned list as in the Values argument. The elements in the returned list are always "canonical" though, i.e. of the form {[Ns|Tag], value() | start | stop | leaf}.

index/2

index(CDB::cdb_sess(), IKP::econfd:ikeypath()) -> {ok, integer()} | err()

Returns the position (starting at 0) of the list entry in path.

mop_2_str/1

mop_2_str(X1) -> any()

new_session/2

new_session(Socket::term(), Db::dbtype()) -> {ok, cdb_sess()} | err()

Initiate a new session using the socket returned by connect/1 or connect/2.

new_session/3

new_session(Socket::term(), Db::dbtype(), Flags::integer()) -> {ok, cdb_sess()} | err()

Initiate a new session using the socket returned by connect/1 or connect/2, with detailed control via the Flags argument.

next_index/2

next_index(CDB::cdb_sess(), IKP::econfd:ikeypath()) -> {ok, integer()} | err()

Returns the position (starting at 0) of the list entry after the given path (which can be non-existing, and if multiple keys the last keys can be '*').

num_instances/2

num_instances(CDB::cdb_sess(), IKP::econfd:ikeypath()) -> {ok, integer()} | err()

Returns the number of entries in a list.

set_case/4

set_case(CDB::cdb_sess(), IKP::econfd:ikeypath(), Choice::econfd:qtag() | [econfd:qtag()], Case::econfd:qtag()) -> ok | err()

Only for CDB operational data: Set the case for a choice.

set_elem/3

set_elem(CDB::cdb_sess(), Value::econfd:value(), IKP::econfd:ikeypath()) -> ok | err()

Only for CDB operational data: Write Value into CDB.

set_elem2/3

set_elem2(CDB::cdb_sess(), ValueBin::binary(), IKP::econfd:ikeypath()) -> ok | err()

Only for CDB operational data: Write ValueBin into CDB. ValueBin is the textual value representation.

set_object/3

set_object(CDB::cdb_sess(), ValueList::[econfd:value()], IKP::econfd:ikeypath()) -> ok | err()

Only for CDB operational data: Write an entire object, i.e. YANG list entry or container.

set_values/3

set_values(CDB::cdb_sess(), ValueList::[econfd:tagval()], IKP::econfd:ikeypath()) -> ok | err()

Only for CDB operational data: Write a list of tagged values. This function is an alternative to set_object/3, and allows for writing more complex structures (e.g. multiple entries in a list).

subscribe/3

subscribe(CDB::cdb_sess(), Prio::integer(), MatchKeyString::string()) -> {ok, PointNumber::integer()} | err()

Equivalent to subscribe(CDB, Prio, 0, MatchKeyString).

subscribe/4

subscribe(CDB::cdb_sess(), Prio::integer(), Ns::sub_ns(), MatchKeyString::string()) -> {ok, PointNumber::integer()} | err()

Set up a CDB configuration subscription.

A CDB subscription means that we are notified when CDB changes. We can have multiple subscription points. Each subscription point is defined through a path corresponding to the paths we use for read operations, however they are in string form and allow formats that aren't possible in a proper ikeypath(). It is possible to indicate namespaces in the path with a prefix notation (see last example) - this is only necessary if there are multiple elements with the same name (in different namespaces) at some level in the path, though.

We can subscribe either to specific leaf elements 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.

Some examples:

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 synchronized via the return value from the fun passed to wait/3, the next set - at the next priority level - is handled by CDB.

The namespace argument specifies the toplevel namespace, i.e. the namespace for the first element in the path. The namespace is optional, 0 can be used as "don't care" value.

subscribe() returns a subscription point which is an integer. This integer value is used later in wait/3 to identify this particular subscription.

subscribe/5

subscribe(CDB::cdb_sess(), Type::sub_type(), Prio::integer(), Ns::sub_ns(), MatchKeyString::string()) -> {ok, PointNumber::integer()} | err()

Equivalent to subscribe(CDB, Type, 0, Prio, Ns, MatchKeyString).

subscribe/6

subscribe(CDB::cdb_sess(), Type::sub_type(), Flags::non_neg_integer(), Prio::integer(), Ns::sub_ns(), MatchKeyString::string()) -> {ok, PointNumber::integer()} | err()

Generalized subscription, where Type is one of

Flags is either 0 or:

subscribe_done/1

subscribe_done(CDB::cdb_sess()) -> ok | err()

After a subscriber is done with all subscriptions and ready to receive updates this subscribe_done/1 must be called. Until it is no notifications will be delivered.

subscribe_session/1

subscribe_session(Socket::term()) -> {ok, cdb_sess()} | err()

Initialize a subscription socket. This is a socket that is used to receive notifications about updates to the database. A subscription socket is used in the subscribe() function.

trigger_subscriptions/1

trigger_subscriptions(Socket::term()) -> ok | err()

Equivalent to trigger_subscriptions(Socket, all).

trigger_subscriptions/2

trigger_subscriptions(Socket::term(), SubPoints::[SubPoint::integer()] | all) -> ok | err()

Trigger CDB subscribers as if a update in the configuration had been done.

wait/3

wait(CDB::cdb_sess(), TimeOut::integer() | infinity, Fun::fun((Points::[integer()]) -> close | subscription_sync_type()) | fun((Type::integer(), Flags::integer(), Points::[integer()]) -> close | subscription_sync_type() | {error, binary()} | {error, #confd_error{}} | {error, tuple()})) -> ok | {error, timeout} | err()

Wait for subscription events.

The fun will be given a list of the subscription points that triggered, and in the arity-3 case also Type and Flags for the notification. There can be several points if we have issued several subscriptions at the same priority.

Type is one of: Flags is the 'bor' of zero or more of: The fun can return the atom 'close' if we wish to close the socket and return from wait/3. Otherwise there are three different types of synchronization replies the application can use as return values from either the arity-1 or the arity-3 fun:

Finally the arity-3 fun can, when Type == ?CDB_SUB_PREPARE, return an error either as {error, binary()} or as {error, #confd_error{}} ({error, tuple()} is only for internal ConfD/NCS use). This will cause the commit of the current transaction to be aborted.

CDB is locked for writing while config subscriptions are delivered.

When wait/3 returns {error, timeout} the connection (and its subscriptions) is still active and the application needs to call wait/3 again. But if wait/3 returns ok or {error, Reason} the connection to ConfD is closed and all subscription points associated with it are cleared.

wait_start/1

wait_start(Socket::term()) -> ok | err()

Wait for CDB to become available (reach start-phase one)


Generated by EDoc, Mar 21 2017, 11:19:33.