Table of Contents
When we want to change the data model used by ConfD, the simplest method is to stop and restart ConfD with the new .fxs files in place. CDB will then detect the change and perform an upgrade, automatically or assisted by external programs, as described in Chapter 5, CDB - The ConfD XML Database.
If it is necessary that ConfD keeps running throughout the data model change, we can instead control the upgrade from an external program using a set of MAAPI functions, as described in this chapter. The CDB upgrade will be performed in this case too of course, and all the techniques described in the CDB chapter are applicable here too. But in addition, this procedure requires careful synchronization between different ConfD components, e.g. transactions may not span the data model change, and all components must update any related data, while still being able to revert to the original data model in case problems are detected.
The following four sections describe the phases and
    corresponding MAAPI function calls that comprise this upgrade
    procedure, and the steps that must be taken by the program
    controlling the procedure. A complete example showing the procedure
    can be found in
    examples.confd/in_service_upgrade/simple in the
    bundled examples collection. The code excerpts and description
    refer to this example. See also the confd_lib_maapi(3) manual page for the
    definitions of the MAAPI functions.
All the new .fxs files, clispecs, MIBs, etc, as well as the
    docroot tree for the Web UI (if used), that are
    to be used after the upgrade, must be installed separately from the
    current ones, and the current ones may not be removed until the
    upgrade has completed successfully. A good way to organize this is
    to have the references to the installation directories in
    confd.conf use a symbolic link. This way we can
    switch the on-disk data to the new version by simply changing the
    link, without the need for complex modification of
    confd.conf. Files that are unchanged between
    the two versions should be duplicated, or possibly (hard-)linked if
    disk space is limited.
In the example, we use two directories
    pkg/v1 and pkg/v2 for the
    old and new versions, respectively, and a symlink
    pkg/current that points to the currently used
    version. In confd.conf both
    /confdConfig/loadPath/dir and
    /confdConfig/webui/docroot are then given with the use of
    the symlink. Multiple loadPath directories are of course also
    possible, by having subdirectories below v1
    and/or v2.
For MIB (.bin) files other than the ones built-in to ConfD,
    confd.conf offers two possibilities: we can
    either specify the actual file names with
    /confdConfig/snmpAgent/mibs/file elements, or use
    /confdConfig/snmpAgent/mibs/fromLoadPath to tell ConfD to
    load these files from the directories given via
    /confdConfig/loadPath. To have the symlink scheme work
    for the MIB files on upgrade, we need to use the latter alternative,
    and only specify built-in MIBs via
    /confdConfig/snmpAgent/mibs/file.
The upgrade.c program in the example
    controls the upgrade procedure. It can be run either standalone or
    via a osCommand specification in the clispec. In both
    cases it must connect a MAAPI socket and associate it with a user
    session. When running from the CLI, it must use the user session id
    provided by the environment variable
    CONFD_MAAPI_USID for this, otherwise it can
    start a new user session:
static int set_usess(int sock)
{
    char *user = "admin";
    const char *groups[] = {"admin"};
    char *context = "system";
    struct confd_ip ip;
    /* must use usid from CLI to be allowed to run across upgrade */
    if ((usid_env = getenv("CONFD_MAAPI_USID")) != NULL) {
        return maapi_set_user_session(sock, atoi(usid_env));
    } else {
        ip.af = AF_INET;
        inet_pton(AF_INET, "127.0.0.1", &ip.ip.v4);
        return maapi_start_user_session(sock, user, context, groups, 1,
                                        &ip, CONFD_PROTO_TCP);
    }
}
Applications connected to ConfD, e.g. data providers and CDB
    subscribers, are not directly affected by the upgrade procedure. If
    they need to take some action due to the upgrade, they should be
    subscribed to CONFD_NOTIF_UPGRADE_EVENT event
    notifications (see Chapter 12, Notifications), and will
    then be notified of the different phases of the upgrade. If nothing
    else, most applications should call
    confd_load_schemas() (see confd_lib_lib(3)) when an upgrade has completed, in
    order to update the in-memory representation of the data model. The
    cdb_subscriber.c program in the example shows
    how this can be done.
After having set up the MAAPI socket, the first step in the
    actual upgrade procedure is to call the
    maapi_init_upgrade() function. Its purpose is
    to bring ConfD into "upgrade mode", where no transactions are
    running, and the northbound agents have entered a state that does
    not allow new transactions to be started.
    progress("Initializing upgrade...\n");
    phase = "Init";
    /* run notifier in separate process
       - maapi_init_upgrade() blocks */
    notifier = run_notifier(timeout, force);
    OK(maapi_init_upgrade(maapisock, timeout,
                          force ? MAAPI_UPGRADE_KILL_ON_TIMEOUT : 0));
    if (notifier != -1)
        kill(notifier, SIGTERM);
    notifier = -1;
    progress("Init OK\n");
    maapi_prio_message(maapisock, "all",
                       "\n>>> System upgrade in progress...\n");
If users have sessions in configure mode when this function is called, they are given the opportunity to exit from configure mode voluntarily. The function call will block until all transactions have been terminated (although not longer than specified by the timeout). For this reason, we fork() a process that periodically sends out messages to all users, informing them of the imminent upgrade.
If any transactions remain when the timeout expires,
    maapi_init_upgrade() will fail with
    confd_errno
    CONFD_ERR_TIMEOUT, unless the
    MAAPI_UPGRADE_KILL_ON_TIMEOUT flag was used.
    If upgrade.c was given the -f
    (force) option, it will pass this flag to
    maapi_init_upgrade(), and any remaining
    transactions will be forcibly terminated instead.
When maapi_init_upgrade() is called, a
    CONFD_UPGRADE_INIT_STARTED event notification
    is sent, and when it completes successfully, a
    CONFD_UPGRADE_INIT_SUCCEEDED event notification
    is sent.
If the function fails, i.e. it does not return
    CONFD_OK, ConfD will automatically abort the
    upgrade, reverting to the pre-upgrade state, and send a
    CONFD_UPGRADE_ABORTED event notification. This
    is true also for the functions described in in the next two
    sections.
When maapi_init_upgrade() has completed
    successfully, the next step is to call
    maapi_perform_upgrade(). This tells ConfD to
    load the new .fxs files etc, and we must pass it a list of
    directories to load these files from. These are the directories that
    will become the new loadPath directories once the upgrade is
    complete. These directories will also be searched for CDB "init
    files" (see Section 5.8, “Loading initial data into CDB”), corresponding to the
    /confdConfig/cdb/initPath directories that can be
    specified in confd.conf.
    progress("Performing upgrade...\n");
    phase = "Perform";
    /* set up new loadpath directory */
    snprintf(buf, sizeof(buf), PKG_DIR "/%s", version);
    load_dir[0] = &buf[0];
    OK(maapi_perform_upgrade(maapisock, &load_dir[0], ndirs));
    progress("Perform OK\n");
At this point confd.conf and hence the
    current symlink must still point to the current
    version, and thus we pass the new directory "explicitly" to the
    function as "./pkg/v2". In this example ConfD
    was also started with the --addloadpath option
    specifying an additional loadPath directory. The contents of this
    directory ($CONFD_DIR/etc/confd) does not
    change in the upgrade, but we must pass the same directory to
    maapi_perform_upgrade() too - the files found
    in the given directories will completely replace what ConfD is
    currently using.
A number of different problems may be detected during the
    loading of the new files, e.g. .fxs files may have a version that is
    incompatible with the ConfD version, or they may reference
    namespaces that can not be found. These problems will make
    maapi_perform_upgrade() fail with
    confd_errno
    CONFD_ERR_BAD_CONFIG, and
    confd_lasterr() giving information about the
    details of the problem. If the loading is successful, CDB will start
    its special upgrade transaction, and perform any automatic upgrade
    operations that are needed, before
    maapi_perform_upgrade() returns.
When maapi_perform_upgrade() completes
    successfully, a CONFD_UPGRADE_PERFORMED event
    notification is sent.
When maapi_perform_upgrade() has
    completed successfully, we must call
    maapi_commit_upgrade() to tell ConfD to make
    the upgrade permanent. This will also tell CDB to commit its upgrade
    transaction, and we may need to take some actions for this before
    the call:
If the upgrade requires that an external program
      modifies some CDB data, it must be done at this point, using
      maapi_attach_init() as described in the CDB
      chapter.
If the upgrade includes new validation points, or
      the validation logic for existing validation points has changed,
      the new validators must connect to ConfD and register for their
      validation points before
      maapi_commit_upgrade() is called.
In the example, all the changes can be handled by the automatic CDB upgrade, and we just proceed with the call:
    progress("Committing upgrade...\n");
    phase = "Commit";
    OK(maapi_commit_upgrade(maapisock));
    relink(version);
    progress("Commit OK\n");
    maapi_prio_message(maapisock, "all",
                       ">>> System upgrade has completed successfully.\n");
maapi_commit_upgrade() may fail if the
    upgraded data does not pass validation, and the errors returned in
    this case are the same as for e.g.
    maapi_apply_trans(). Since this will also make
    ConfD automatically revert to the pre-upgrade state, we must not
    change the on-disk data to reflect the upgrade until
    maapi_commit_upgrade() has succeeded.  In the
    code above, the relink() call changes the
    symlink to point to the new version in an atomic manner.
When maapi_commit_upgrade() completes
    successfully, a CONFD_UPGRADE_COMMITED event
    notification is sent.
We can abort the upgrade at any point before the
    maapi_commit_upgrade() call by calling
    maapi_abort_upgrade(). However as noted above,
    this should not be done when one of the other functions fails, since
    ConfD aborts the upgrade automatically in those cases.
When maapi_abort_upgrade() aborts an
    upgrade, a CONFD_UPGRADE_ABORTED event
    notification is sent.
When we use the ConfD High Availability functionality, it is
    critical that all nodes in the HA cluster agree on the data model
    used. For this reason we can not do in-service upgrade on a ConfD
    instance that is part of a HA cluster. A ConfD node in HA state SLAVE
    or MASTER executing
    maapi_init_upgrade() will fail with
    confd_errno
    CONFD_ERR_HA_WITH_UPGRADE. Conversely, when an
    in-service upgrade is in progress, calling
    confd_ha_beslave() will also result in this
    error, and connections from slaves will be rejected.
Thus in-service upgrade is only possible while in HA-state NONE.
To do the in-service upgrade on a HA cluster, we must thus use "rolling upgrade":
Disconnect one of the slaves from the cluster by calling
        confd_ha_benone().
Upgrade the disconnected slave as described above.
Tell the upgraded slave to become master by calling
        confd_ha_bemaster().
Tell the old master to not be master by calling confd_ha_benone()
Upgrade the remaining nodes in the cluster one by one,
        telling each to connect as slave to the upgraded master by
        calling confd_ha_beslave() when the upgrade
        is done.
Alternatively, since the HA configuration should be able to handle that a node is stopped and restarted without service interruption, we may simply use the upgrade method described in the CDB chapter for the "rolling upgrade".