Saving running configuration as XML

At the prepare phase, the saved running and candidate configurations do not reflect the status seen on client.

The file examples.confd/cdb_subscription/twophase/twophase.c is modified for the following:

----
switch (type) {
case CDB_SUB_PREPARE:
printf("\nTYPE=CDB_SUB_PREPARE\n");
{

            save_config(CONFD_CANDIDATE);
            save_config(CONFD_RUNNING);
            if (go_check_hardware() == 0) {
              printf("send abort\n");
              cdb_sub_abort_trans(ss, CONFD_ERRCODE_RESOURCE_DENIED,
                                  0, 0, "HARDWARE IS NOT CONFIGURABLE");
            }else {
              printf("send CDB_DONE_PRIORITY\n");
              cdb_sync_subscription_socket(ss, CDB_DONE_PRIORITY);

            }
            break;
          }

----

void save_config(int db_mode)
{ /* save */

    int id, ssock, r;
    FILE *f;
    char buf[BUFSIZ];

    if ((tid = maapi_start_trans(sock, db_mode, CONFD_READ)) >= 0)
        printf("GOT tid for config \n");
    else
        ERR("maapi_start_trans()", 1);

    if (db_mode == CONFD_CANDIDATE){
        if ((f = fopen("candidate.xml", "w")) == NULL)
            fatal(errno, 1, "failed to open file %s", "defnet_c.xml");
    }
    if (db_mode == CONFD_RUNNING){
        if ((f = fopen("running.xml", "w")) == NULL)
            fatal(errno, 1, "failed to open file %s", "defnet_r.xml");
    }

    id = maapi_save_config(sock, tid, MAAPI_CONFIG_XML_PRETTY, "/system");
    if (id <= 0)
        ERR("maapi_save_config()", 1);
    if ((ssock = socket(PF_INET, SOCK_STREAM, 0)) < 0 )
        fatal(errno, 1, "failed to open socket");

    OK(confd_stream_connect(ssock, (struct sockaddr*)&addr, addrlen, id, 0));

    while ((r = read(ssock, buf, sizeof(buf))) > 0) {
        if (fwrite(buf, 1, r, f) != r)
            fatal(errno, 1, "failed to write output");
    }
    if (r < 0)
        fatal(errno, 1, "failed to read from stream socket");
    close(ssock);

    OK(maapi_save_config_result(sock, id));
    fprintf(f, "\n");
    if (f != stdout) {
        if (fclose(f) != 0)
            fatal(errno, 1, "failed to write output");
    }

    OK(maapi_finish_trans(sock, tid));

}

----

I tried the abort scenario:

mnair-server# show running-config system
% No entries found.
mnair-server# config
Entering configuration mode terminal
admin(config)# system nodes node1 nodeA 1 nodeB 1 nodeC 2 nodeD 3
admin(config-nodes-node1)# commit
Aborted: resource denied: HARDWARE IS NOT CONFIGURABLE

admin(config)# show configuration
system nodes node1
nodeA 1
nodeB 1
nodeC 2
nodeD 3
!

admin(config)# show configuration running system
% No entries found.

----

Running and candidate Configs are saved in XML.

candidate.xml
::::::::::::::

running.xml
::::::::::::::



node1
1
1
2
3


----

This does not seem to be correct. At the prepare stage, the candidate config should be the same as the running.xml shown above. The running config should be empty, at this point since ‘commit’ has not occurred yet.

Hi,

It seems like you have writable-running enabled, running configured to read-write mode, while the candidate is enabled (see ConfD UG why that is not recommended).

Since you entered config mode with the default “terminal” mode, you will edit only a private copy of the running configuration, not the candidate. Your commit will then commit directly to the running datastore, but since the commit/transaction was aborted in the prepare phase nothing was written to the running datastore.

If you enter config mode using “exclusive” (recommended) or “shared”, the configuration will be written to the candidate and upon commit written to running too (unless aborted before the commit phase).

See ConfD 6.2 UG “16.20.1. Operational mode commands” and “27.1. Datastores” for more details.

Thanks Conny for the prompt response.

As you suggested, I disabled writable-running, and without any code changes, I get the following output in the exact same use case with config in shared mode.

candidate.xml
::::::::::::::



node1
1
1
2
3


running.xml
::::::::::::::



node1
1
1
2
3


Here running.xml looks wrong, candidate.xml is correct.

Next, with config in exclusive mode, the output is:

candidate.xml
::::::::::::::



node1
1
1
2
3


node1
1
1
2
3


running.xml
::::::::::::::

Here running.xml is correct, candidate.xml looks wrong.

Also, in this case the application took about 15-20 seconds and exited. I see the following in the file _tmp_twophase.

TRACE MAAPI_START_TRANS → CONFD_OK
TRACE MAAPI_SAVE_CONFIG → CONFD_OK
TRACE Connected (stream) to ConfD
TRACE MAAPI_SAVE_CONFIG_RESULT → CONFD_OK
TRACE MAAPI_STOP_TRANS → CONFD_OK
TRACE CDB_SYNC_SUB CDB_ABORTDEBUG EOF on socket to ConfD
→ CONFD_EOF
DEBUG EOF on socket to ConfD

Is this (in shared mode, “running.xml” is wrong) a bug in confd or is this a usage/configuration issue?
Also, why does confd exit with logs shown in “_tmp_twophase” above?

Thanks,
Manoj

You get that behaviour both in shared and exclusive/private (global lock on candidate) config mode.
There is a read lock on the candidate when you commit the candidate, so you enter a deadlock when you do a maapi read (here maapi_save_config) of the candidate from your prepare phase config change subscriber.

The running config is not empty since the prepare phase comes after write phase. See ConfD UG 6.2 Chapter 7.5. User sessions and ConfD Transactions for an overview.
As a subscriber, you can start a session towards both CDB_RUNNING and CDB_PRE_COMIT_RUNNING. See “cdb_start_session()” description in the confd_lib_cdb man page.

If you can describe why read need to log the candidate configuration as XML at the prepare phase we may be able to suggest alternative solutions.
If you want to log the candidate for audit reasons a solution could be to just log the diff from your subscriber:

See also ConfD UG chapter 12.2. Audit Messages and 12.4. Commit Events and examples.confd/misc/notifications for other audit log alternatives.

The running config is not empty since the prepare phase comes after write phase

Looking at Chapter 7.5, for write_start() - It is mentioned that “The application should not modify the real running data here. If, later, the abort() callback is called, all write operations performed in this state must be undone.”
I assumed that at the prepare phase, candidate config is not written to running. Perhaps I have misunderstood it.

I am looking for a point in the various phases in commit transaction where:
a. The candidate config is completely validated wrt its YANG semantics
b. Call an external API to check resource availability. Continue to next step if OK. Else abort the commit
c. Generate candidate config XML and push (via message broker) to network functions. If push fails, abort the commit, else continue with the commit transaction

What is the recommended way to do this?

Thanks

a. You want to rely on the transaction validation phase of the transaction that commit the candidate to running as the pic and text in chapter 7.5 describes (but for an external DB, same thing for CDB though).
b. The prepare phase as described earlier. See examples.confd/cdb_subcription/twophase
c. Generate the the “running config to be committed” in XML using maapi_save_config in the prepare phase where you can abort the transaction. Note: It is not recommended to push the config to network functions already in the prepare phase, other than checking if resources is available. Even though you can’t abort in the commit phase, best practice is to raise an alarm, e.g. through a netconf notification, if the push config to network functions fail and let the manager decide what to do next. You typically don’t want to go live with a configuration before the commit phase, and you don’t want to have to rollback the configuration yourself in the event where the manager abort the commit in the prepare phase.

The text refers to an external database. If you are interested in those details, e.g. how new running config is prepared and then copied to an external DB running datastore, see ConfD 6.2 UG chapter 7.7. External configuration data with transactions.

As mentioned previously, see picture and text in ConfD 6.2 UG chapter 7.5

Thanks Conny for all the suggestions and references. I guess more study of UG and examples are needed for a better understanding.