Motr  M0
Configuration Client (confc)

Configuration client library – confc – provides user-space and kernel interfaces for accessing Motr configuration information.

Confc obtains configuration data from network-accessible configuration server (confd) and caches this data in memory.


Data Structures

  • m0_confc — an instance of configuration client. This structure contains m0_conf_cache, which represents configuration cache. m0_confc also keeps reference to the state machine group that synchronizes state machines created by this confc.
  • m0_confc_ctx — configuration retrieval context. This structure embodies data needed by a state machine to process configuration request.
  • m0_conf_diter — configuration directory iterator. This structure maintains multiple levels of a configuration directory path and data needed for its traversal. Each directory level is represented by m0_conf_diter_lvl. Each m0_conf_diter_lvl contains 2 objects of m0_confc_ctx corresponding to m0_confc_open() and m0_confc_readdir() operations, which are re-used for subsequent iterations for that directory level. m0_conf_diter traverses the configuration directory tree in depth first order.

Subroutines

Initialization and termination

Prior to accessing configuration, the application (aka configuration consumer) should initialise configuration client by calling m0_confc_init().

A confc instance is associated with a state machine group (m0_sm_group). A user managing this group is responsible for making sure m0_sm_asts_run() is called when the group's channel is signaled; "AST" section of State machine has more details on this topic.

m0_confc_fini() terminates confc, destroying configuration cache.

#include "conf/confc.h"
#include "conf/obj.h"
struct m0_sm_group *grp = ...;
struct m0_rpc_machine *rpcm = ...;
struct m0_confc confc;
startup(const struct m0_fid *profile, ...)
{
rc = m0_confc_init(&confc, grp, profile, "confd-ep-addr", rpcm,
NULL);
...
}
... Access configuration objects, using confc interfaces. ...
shutdown(...)
{
}

Accessing configuration objects

The application gets access to configuration data by opening configuration objects with m0_confc_open() or m0_confc_open_sync(). Directory objects can be iterated over with m0_confc_readdir() or m0_confc_readdir_sync().

m0_confc_open() and m0_confc_readdir() are asynchronous functions. Prior to calling them, the application should initialise a context object (m0_confc_ctx_init()) and register a clink with .sm_chan member of m0_confc_ctx::fc_mach. (See sm_waiter_init() in the recipe #1 below.)

m0_confc_ctx_is_completed() checks whether configuration retrieval has completed, i.e., terminated or failed.

m0_confc_ctx_error() returns the error status of an asynchronous configuration retrieval operation. m0_confc_ctx_result() returns the requested configuration object.

A caller of m0_confc_open_sync() or m0_confc_readdir_sync() will be blocked while confc is processing the request.

All m0_confc_open*()ed configuration objects must be m0_confc_close()ed before m0_confc_fini() is called.

Note
Confc library pins (see Pinned Objects) only those configuration objects that are m0_confc_open*()ed or m0_confc_readdir*()ed by the application.

m0_conf_diter_next() is an asynchronous function. Prior to invoking it, the application must invoke m0_conf_diter_wait_arm() and register a clink to receive the completion event. Result of the iteration must be accessed using m0_conf_diter_result().


Recipes

Configuration objects can be opened asynchronously (m0_confc_open()) or synchronously (m0_confc_open_sync()). Many of the examples below use synchronous calls for the sake of brevity.

Get profile configuration

#include "conf/confc.h"
#include "conf/obj.h"
struct m0_confc *g_confc = ...;
// A sample m0_confc_ctx wrapper.
struct sm_waiter {
struct m0_clink w_clink;
};
static int sm_waiter_init(struct sm_waiter *w, struct m0_confc *confc);
static void sm_waiter_fini(struct sm_waiter *w);
// Uses asynchronous m0_confc_open().
static int profile_open1(struct m0_conf_profile **prof,
const struct m0_fid *id)
{
struct sm_waiter w;
int rc;
rc = sm_waiter_init(&w, g_confc);
if (rc != 0)
return M0_ERR(rc);
m0_confc_open(&w.w_ctx, NULL, M0_CONF_ROOT_PROFILES_FID, id);
while (!m0_confc_ctx_is_completed(&w.w_ctx))
m0_chan_wait(&w.w_clink);
rc = m0_confc_ctx_error(&w.w_ctx);
if (rc == 0)
return rc;
}
// Uses m0_confc_open_sync().
static int profile_open2(struct m0_conf_profile **prof,
const struct m0_fid *id)
{
struct m0_conf_obj *obj;
int rc;
M0_CONF_ROOT_PROFILES_FID, *id);
if (rc == 0)
return rc;
}
// Filters out intermediate state transitions of m0_confc_ctx::fc_mach.
static bool sm_filter(struct m0_clink *link)
{
struct sm_waiter,
w_clink)->w_ctx);
}
static int sm_waiter_init(struct sm_waiter *w, struct m0_confc *confc)
{
if (rc == 0) {
m0_clink_init(&w->w_clink, sm_filter);
}
return rc;
}
static void sm_waiter_fini(struct sm_waiter *w)
{
}

Iterate directory object asynchronously

#include "conf/confc.h"
#include "conf/obj.h"
#include "lib/arith.h" // M0_CNT_INC
struct sm_waiter {
struct m0_clink w_clink;
};
// sm_waiter_*() functions are defined in one of the recipes above.
static void sm_waiter_init(struct sm_waiter *w, struct m0_confc *confc);
static void sm_waiter_fini(struct sm_waiter *w);
// Uses configuration data of every object in given directory.
static int dir_entries_use(struct m0_conf_obj *dir,
void (*use)(const struct m0_conf_obj *),
bool (*stop_at)(const struct m0_conf_obj *))
{
struct sm_waiter w;
int rc;
struct m0_conf_obj *entry = NULL;
while ((rc = m0_confc_readdir(&w.w_ctx, dir, &entry)) > 0) {
if (rc == M0_CONF_DIRNEXT) {
// The entry is available immediately.
M0_ASSERT(entry != NULL);
use(entry);
if (stop_at != NULL && stop_at(entry)) {
rc = 0;
break;
}
continue; // Note, that `entry' will be
// closed by m0_confc_readdir().
}
// Cache miss.
if (rc != 0)
break; // error
if (entry == NULL)
break; // end of directory
use(entry);
if (stop_at != NULL && stop_at(entry))
break;
// Re-initialise m0_confc_ctx.
}
return rc;
}
See also
Detailed Functional Specification