[#]: # (man)
NAME
====
ldmsd-store-dev - LDMSD Store Development Guide
SYNOPSIS
========
`#include "ldmsd_plugin.h"`
`#include "ldmsd_store.h"`
DESCRIPTION
===========
An LDMSD storage plugin is an LDMSD plugin implementing the store interface
(`ldmsd_store_type_s`). A storage plugin instance is an entity created from the
storage plugin to handle the storage of updated LDMS sets of the same schema
(see [ldmsd-aggregator][agg](7) for an example usage). Each storage plugin
instance has a copy of `ldmsd_store_type_s` structure associated with it, which
can be conveniently accessed by the macro `LDMSD_STORE(inst)`. The association
of the store plugin instance and a copy of `ldmsd_store_type_s` structure can be
depicted as follows:
```txt
.-----------------------.
| inst extension |
|-----------------------| .-----------------------.
| .-------------------. | | ldmsd_store_type_s |
| |ldmsd_plugin_inst_s| | |-----------------------|
| |-------------------| | | .-------------------. |
| | base ------------+-+------->| |ldmsd_plugin_type_s| |
| | | | | |-------------------| |
| | INSTANCE APIs | |<-------+-+-inst | |
| '-------------------' | | | | |
| | | | PLUGIN APIs | |
| EXTENSION DATA | | '-------------------' |
'-----------------------' | |
| STORE APIs |
'-----------------------'
```
`ldmsd` uses the STORE APIs in `ldmsd_plugin_type_s` to communicate with the the
plugin instance about storage business (e.g. open, close, store). The PLUGIN
APIs in `ldmsd_plugin_type_s` and INSTANCE APIs in `ldmsd_plugin_ints_s` are for
instance management and configuration. The storage plugin must extend the
`ldmsd_plugin_inst_s` structure to handle its plugin instance. The
`ldmsd_store_type_s` structure is an extended strucutre of `ldmsd_plugin_type_s`
that handlesa storage and common plugin APIs. It is created and managed by
`ldmsd`. The storage plugin does not have an authority over this structure other
than override some APIs.
The interaction between `ldmsd` and the storage plugin / storage plugin instance
can be summarized as the following:
- instance creation
-
`ldmsd` create a plugin instance upon a `load` user request (see [Creating an
Instance](#creating-an-instance)) by calling `new()` function in the plugin
library. The plugin then allocate the memory to hold the extended instance
structure and setup INSTANCE APIs. At this point, the plugin instance does not
have `ldmsd_store_type_s` associated with it yet. `ldmsd` will inspect the
`ldmsd_plugin_inst_s.type` (must be `LDMSD_STORE_TYPENAME` in this case) and
create a copy of `ldmsd_store_type_s` after `new()` has returned.
- store instance initialization
-
After `new()` has returned, and a copy of `ldmsd_store_type_s` has been created
and linked with the instance by `ldmsd`, `ldmsd_plugin_inst_s.init()` is called
to let the plugin further setup the STORE APIs. Additional plugin resources
could be initialized during the init call as well.
- configuration
-
`ldmsd` configure the plugin instance when the user gave `config` command. The
configuration attributes from the command are passed along to the plugin config
function (see [Config](#config) subsection). After a successful config, the
plugin became configured and is ready to be used by `strgp` (storage policy, see
[ldmsd-aggregator][agg](7)).
- store open
-
`strgp` will call `ldmsd_store_type_s.open()` to perform an *open* operation on
the storage. This is analogous to opening a file before writing data into it.
- store
-
After a successful open, when a `strgp`-matching set get updated,
`ldmsd_store_type_s.store()` is called to notify the storage instance to store
the updated set. This is analogous to file write.
- store close
-
After a successful open, when the `strgp` is stopped by the user the API
`ldmsd_store_type_s.close()` is called to notify the plugin instance to clean up
the resources from open. This is analogous to file close.
- instance deletion
-
After `strgp` stopped and deleted, the store instance could also be deleted by
the user. In such case, `ldmsd_plugin_inst_s.delete()` is called to let the
plugin instance clean up its resources.
We will use a `plaintext` storage plugin as an example to guide through how to
implement a storage plugin for LDMS in the following subsections.
[Preparation](#preparation)
[Conventions](#conventions)
[Extending Plugin Instance](#extending-plugin-instance)
[Creating an Instance](#creating-an-instance)
[Help and Description](#help-and-description)
[Initialization](#initialization)
[Store APIs](#store-apis)
[Config](#config)
[Createing a Schema and a Set](#creating-a-schema-and-a-set)
[Sample](#sample)
Preparation
------------
This step is for developing a storage plugin in ovis development tree
(recommended). If you are developing a plugin outside of ovis development tree,
you can skip this step, and have to make sure that the plugin links with
`libstore.so`.
Please follow these steps to prepare a workspace for developing `plaintext`
store:
**1)** Create a sub directory `plaintext` in `ldms/src/ldmsd-stores`.
**2)** Create a `Makefile.am` and a (or several) C source file for the plugin
(`plaintext.c` for this example). The `Makefile.am` should look like the
following:
```make
# file: ldms/src/ldmsd-stores/plaintext/Makefile.am
include ../common.am
pkglib_LTLIBRARIES = libplaintext.la
libplaintext_la_SOURCES = plaintext.c
libplaintext_la_CFLAGS = $(STORE_CFLAGS)
libplaintext_la_LIBADD = $(STORE_LIBADD)
libplaintext_la_LDFLAGS = $(STORE_LDFLAGS)
```
The `../common.am` defines `STORE_CFLAGS`, `STORE_LIBADD` and `STORE_LDFLAGS`
variables containing necessary artifacts to build the storage plugin.
**3)** Edit `ldms/configure.ac`, add an option for the sampler, and add the
`Makefile` in "2)" in the `AC_CONFIG_FILES` list as follows:
```autoconf
dnl Options for sampler
...
OPTION_DEFAULT_DISABLE([plaintext], [ENABLE_PLAINTEXT])
...
AC_CONFIG_FILES([Makefile src/Makefile src/core/Makefile
...
src/plaintext/Makefile
...
])
...
```
Please note that the paths in `ldms/configure.ac` is relative to `ldms/`
directory (where the autoconf script resides).
`OPTION_DEFAULT_DISABLE` macro is used so that the plugin won't be build unless
the user specifies `--enable-plaintext` configure option. In the case that the
plugin does not depend on extra libraries other than those required by `ldmsd`,
we may use `OPTION_DEFAULT_ENABLE` macro to have it build by default (unless the
user specify `--disable-thermal` configure option).
In the case of extra dependencies, please also add a library / header checking
logic in the `ldms/configure.ac`.
**4)** Edit `ldms/src/ldmsd-stores/Makefile.am` and add `plaintext` as a
conditional build subdirectory:
```make
# file: ldms/src/ldmsd-stores/Makefile.am
...
if ENABLE_PLAINTEXT
SUBDIRS += plaintext
endif
...
```
**5)** (Optional) For convenience, the template
`ldms/templates/store_template.c` can be copied to
`ldms/src/ldmsd-stores/plaintext/plaintext.c` as a starting point of
development.
Conventions
-----------
**Functions** of the plugin are `static`, except for `new()` function. The name
of the plugin functions are preferably prefixed with plugin name for
readability (e.g. `static int plaintext_init(ldmsd_plugin_inst_t pi)`).
The name of the **extended instance** structure is preferably `struct
_inst_s` (e.g. `struct plaintext_inst_s`).
Extending Plugin Instance
-------------------------
To extend a sample plugin instance, simply define a new structure with `struct
ldmsd_plugin_inst_s` as the first member. It is conventional to name the element
`base`. The following is an example for `plaintext`:
```c
/* file: ldms/src/ldmsd-samplers/plaintext/plaintext.c */
...
typedef struct plaintext_inst_s *plaintext_inst_t;
struct plaintext_inst_s {
struct ldmsd_plugin_inst_s base;
char *path;
FILE *f; /* file pointer to the output file */
};
...
```
Creating an Instance
--------------------
The storage plugin implementation must implement `ldmsd_plugin_inst_t new()`
function. This function is called by `ldmsd` when it needs to create an instance
of the plugin (when `load` ldmsd config command is processed).
`new()` function must allocate memory for the newly created instance and
setup the instance APIs. The following is an example for `plaintext` sampler:
```c
/* file: ldms/src/ldmsd-stores/plaintext/plaintext.c */
...
/* global structure for setting up instance APIs and some default values */
struct plaintext_inst_s __inst = {
.base = {
/* must get version from this macro */
.version = LDMSD_PLUGIN_VERSION_INITIALIZER,
/* type must be LDMSD_STORE_TYPENAME */
.type_name = LDMSD_STORE_TYPENAME,
.plugin_name = "plaintext",
/* Common Plugin APIs, these functions are to be implemented */
.desc = plaintext_desc,
.help = plaintext_help,
.init = plaintext_init,
.del = plaintext_del,
.config = plaintext_config,
},
};
ldmsd_plugin_inst_t new()
{
plaintext_inst_t inst = malloc(sizeof(*inst));
if (inst)
*inst = __inst; /* copy __inst value above */
return &inst->base;
}
...
```
At this point, the corresponding `ldmsd_store_type_s` has not been created and
paired with this instance yet. `ldmsd` will handle the creation and pairing
after `new()` returned.
Help and Description
--------------------
The `ldmsd_plugin_inst_s.desc()` interface is for a short description of the
plugin, and `ldmsd_plugin_inst_s.help()` interface is for a long help text
describing how to use/configure the plugin instance. The `plaintext` description
and help implemntation is as the following:
```c
/* file: ldms/src/ldmsd-stores/plaintext/plaintext.c */
...
static const char *plaintext_desc(ldmsd_plugin_inst_t pi)
{
return "plaintext - write data to plain text file";
}
static const char *plaintext_help(ldmsd_plugin_inst_t pi)
{
return "plaintext config synopsis:\n"
" config name=INST [COMMON OPTIONS] path=PATH_TO_FILE\n"
"\n"
"Descriptions:\n"
" plaintext writes plain text data to the file specified\n"
" by `path` parameter. If the file does not exist,\n"
" it will be created. If the file existed, it will be\n"
" appended to.\n";
}
...
```
Initialization
--------------
After the plugin instance is created by `new()`, and `ldmsd` successfully link
it to a copy of `ldmsd_store_type_s`, `ldmsd_plugin_inst_s.init()` is called to
initialize the plugin instance and setup (or overries) STORE APIs in the linked
`ldmsd_store_type_s`. The plugin is expected to initialize its resources during
this call. Some plugin that relies on static files that requires no
configuration call may decide to open those files during init.
The storage plugin must implement `ldmsd_store_type_s.open()`,
`ldmsd_store_type_s.close()`, `ldmsd_store_type_s.flush()`, and
`ldmsd_store_type_s.store()` store operations. These interface must be setup
during `ldmsd_plugin_inst_s.init()`. The following is an example of init
function of `plaintext` sampler:
```c
static int plaintext_init(ldmsd_plugin_inst_t pi)
{
ldmsd_store_type_t store = LDMSD_STORE(pi);
store->open = plaintext_open;
store->close = plaintext_close;
store->store = plaintext_store;
store->flush = plaintext_flush;
return 0;
}
```
Config
------
After `new()` and `ldmsd_plugin_inst_s.init()` (corresponding to the user's
`load` command), `ldmsd_plugin_inst_s.config()` is called when the user gave
`config` command. For example:
```conf
load name=x plugin=plaintext
config name=x path=/tmp/plain
```
The configuration attributes are passed along to `ldmsd_plugin_inst_s.config()`
API by `ldmsd` via `json` parameter (see [json_util.h][json_util.h]). If
`ldmsd_plugin_inst_s.config()` is not implemented (`NULL`), the base
`ldmsd_plugin_type_s.config()` is called instead.
`ldmsd_plugin_inst_s.config()` implementation should also call
`ldmsd_plugin_type_s.config()` to process the common configuration attributes.
On configuration error, the plugin instance can `snprintf()` to `ebuf` to
explain the error and return the error number.
The following is an implementation of `config()` for `plaintext`.
```c
...
static int
plaintext_config(ldmsd_plugin_inst_t pi, json_entity_t json,
char *ebuf, int ebufsz)
{
ldmsd_store_type_t store = LDMSD_STORE(pi);
plaintext_inst_t inst = (void*)pi;
int rc;
const char *val;
rc = store->base.config(pi, json, ebuf, ebufsz);
if (rc)
return rc;
val = json_attr_find_str(json, "path");
if (!val) {
snprintf(ebuf, ebufsz, "missing `path` attribute.\n");
return EINVAL;
}
inst->path = strdup(val);
if (!inst->path) {
snprintf(ebuf, ebufsz, "Out of memory.\n");
return errno;
}
return 0;
}
...
```
Store APIs
----------
`ldmsd` handle data storage business with a storage plugin instance through the
storage APIs as the following:
- ldmsd_store_type_s.open()
-
After a successful config,
ldmsd
assures to call
ldmsd_store_type_s.open()
before storing the data to let the
storage instance prepare the underlying resources. This is analogous to
open()
of a file.
ldmsd_store_type_s.open()
also receive strgp
(storage
policy) which contain schema information needed for storage initialization.
strgp.schema
is a string containing schema name of the sets being
fed to the storage instance. strgp.metric_count
is the number of
metrics to be stored. strgp.metric_list
is a list of
ldmsd_strgp_metric
containing information for each metric to be
stored. The plugin instance can iterate through the list using
ldmsd_strgp_metric_first()
and
ldmsd_strgp_metric_next()
functions. For each
ldmsd_strgp_metric
, the name, type, and metric index are
.name
, .type
, and .idx
respectively.
- ldmsd_store_type_s.store()
-
ldmsd
calls ldmsd_store_type_s.store()
, supplying
set
and strgp
to the plugin instance, to notify the
storage plugin instance about the set that has just been updated. The plugin
instance then access set
data using LDMS API (e.g.
ldms_metric_get_u64()
-- see more in ldms.h
) and store
the data accordingly. strgp.metric_list
tells the storage plugin
instance which data to store. The plugin instance can iterate through the list
using ldmsd_strgp_metric_first()
and
ldmsd_strgp_metric_next()
functions. For each
ldmsd_strgp_metric
, the name, type, and metric index are
.name
, .type
, and .idx
respectively.
- ldmsd_store_type_s.flush()
-
ldmsd
occastionally calls .flush()
to notify the store
to flush out cached data. If the store does not support flush operation, it
still need to implement a flush function that does nothing and returns 0.
- ldmsd_store_type_s.close()
-
When the user issued
strgp_stop
command to ldmsd
, the
ldmsd_store_type_s.close()
is called to close down the (opened)
store. The storage plugin instance shall close the underlying store and clean up
resources allocated in ldmsd_store_type_s.open()
.
The following is the `plaintext` implementation of store APIs:
```c
...
static int
plaintext_open(ldmsd_plugin_inst_t pi, ldmsd_strgp_t strgp)
{
plaintext_inst_t inst = (void*)pi;
inst->f = fopen(inst->path, "a");
if (!inst->f)
return errno;
return 0;
}
static int
plaintext_close(ldmsd_plugin_inst_t pi)
{
plaintext_inst_t inst = (void*)pi;
fclose(inst->f);
inst->f = NULL;
}
static int
plaintext_flush(ldmsd_plugin_inst_t pi)
{
plaintext_inst_t inst = (void*)pi;
fflush(inst->f);
}
static int
plaintext_store(ldmsd_plugin_inst_t pi, ldms_set_t set, ldmsd_strgp_t strgp)
{
/* `store` data from `set` into the store */
plaintext_inst_t inst = (void*)pi;
ldmsd_strgp_metric_t m;
const char *setname = ldms_set_instance_name_get(set);
for (m = ldmsd_strgp_metric_first(strgp);
m;
m = ldmsd_strgp_metric_next(m)) {
fprintf(inst->f, "%s:%s:%s:", setname, m->name,
ldms_metric_type_to_str(m->type));
fprint_metric_val(inst->f, set, m->idx);
/*
* fprint_metric_val() implementation is in the EXAMPLE
* section below
*/
fprintf(inst->f, "\n");
}
return 0;
}
...
```
Deletion of Sampler Plugin Instance
-----------------------------------
A plugin instance can be deleted upon user request (`term` command, after the
associated `strgp` has been stopped and removed). The
`ldmsd_plugin_inst_s.del()` is called to let the plugin implementation clear
instnace-specific resources. The plugin implementation must **NOT** free the
instance itself. `ldmsd` will free the instance afterward.
The following is `del()` implementation of `plaintext` storage plugin:
```c
/* part of `plaintext.c` */
static void
plaintext_del(ldmsd_plugin_inst_t pi)
{
plaintext_inst_t inst = (void*)pi;
if (inst->path)
free(inst->path);
if (inst->f)
fclose(inst->f);
}
```
EXAMPLE
=======
This section contain the complete code that we have been building through the
guide.
Source files
------------
This is the full content of `plaintext.c`:
```c
/* file: ldms/src/ldmsd-stores/plaintext/plaintext.c */
#include "ldmsd.h"
#include "ldmsd_store.h"
#define INST(x) ((ldmsd_plugin_inst_t)(x))
#define INST_LOG(inst, lvl, fmt, ...) \
ldmsd_log((lvl), "%s: " fmt, INST(inst)->inst_name, \
##__VA_ARGS__)
typedef struct plaintext_inst_s *plaintext_inst_t;
struct plaintext_inst_s {
struct ldmsd_plugin_inst_s base;
char *path;
FILE *f;
};
/* ============== Store Plugin APIs ================= */
static int
plaintext_open(ldmsd_plugin_inst_t pi, ldmsd_strgp_t strgp)
{
/* Perform `open` operation */
plaintext_inst_t inst = (void*)pi;
inst->f = fopen(inst->path, "a");
if (!inst->f)
return errno;
return 0;
}
static int
plaintext_close(ldmsd_plugin_inst_t pi)
{
/* Perform `close` operation */
plaintext_inst_t inst = (void*)pi;
fclose(inst->f);
inst->f = NULL;
return 0;
}
static int
plaintext_flush(ldmsd_plugin_inst_t pi)
{
/* Perform `flush` operation */
plaintext_inst_t inst = (void*)pi;
fflush(inst->f);
return 0;
}
static void
fprint_metric_array_val(FILE *f, ldms_set_t set, int i)
{
enum ldms_value_type type = ldms_metric_type_get(set, i);
int j, n;
n = ldms_metric_array_get_len(set, i);
for (j = 0; j < n; j++) {
if (j)
fprintf(f, ",");
switch (type) {
case LDMS_V_U8_ARRAY:
fprintf(f, "%hhu", ldms_metric_array_get_u8(set, i, j));
break;
case LDMS_V_S8_ARRAY:
fprintf(f, "%hhd", ldms_metric_array_get_s8(set, i, j));
break;
case LDMS_V_U16_ARRAY:
fprintf(f, "%hu", ldms_metric_array_get_u16(set, i, j));
break;
case LDMS_V_S16_ARRAY:
fprintf(f, "%hd", ldms_metric_array_get_s16(set, i, j));
break;
case LDMS_V_U32_ARRAY:
fprintf(f, "%u", ldms_metric_array_get_u32(set, i, j));
break;
case LDMS_V_S32_ARRAY:
fprintf(f, "%d", ldms_metric_array_get_s32(set, i, j));
break;
case LDMS_V_U64_ARRAY:
fprintf(f, "%lu", ldms_metric_array_get_u64(set, i, j));
break;
case LDMS_V_S64_ARRAY:
fprintf(f, "%ld", ldms_metric_array_get_s64(set, i, j));
break;
case LDMS_V_F32_ARRAY:
fprintf(f, "%f", ldms_metric_array_get_float(set, i, j));
break;
case LDMS_V_D64_ARRAY:
fprintf(f, "%lf", ldms_metric_array_get_double(set, i, j));
break;
}
}
}
static void
fprint_metric_val(FILE *f, ldms_set_t set, int i)
{
enum ldms_value_type type = ldms_metric_type_get(set, i);
if (type == LDMS_V_CHAR_ARRAY) {
fprintf(f, "%s", ldms_metric_array_get_str(set, i));
return;
}
if (ldms_type_is_array(type)) {
fprint_metric_val(f, set, i);
return ;
}
switch (type) {
case LDMS_V_CHAR:
fprintf(f, "%c", ldms_metric_get_char(set, i));
break;
case LDMS_V_U8:
fprintf(f, "%hhu", ldms_metric_get_u8(set, i));
break;
case LDMS_V_S8:
fprintf(f, "%hhd", ldms_metric_get_s8(set, i));
break;
case LDMS_V_U16:
fprintf(f, "%hu", ldms_metric_get_u16(set, i));
break;
case LDMS_V_S16:
fprintf(f, "%hd", ldms_metric_get_s16(set, i));
break;
case LDMS_V_U32:
fprintf(f, "%u", ldms_metric_get_u32(set, i));
break;
case LDMS_V_S32:
fprintf(f, "%d", ldms_metric_get_s32(set, i));
break;
case LDMS_V_U64:
fprintf(f, "%lu", ldms_metric_get_u64(set, i));
break;
case LDMS_V_S64:
fprintf(f, "%ld", ldms_metric_get_s64(set, i));
break;
case LDMS_V_F32:
fprintf(f, "%f", ldms_metric_get_float(set, i));
break;
case LDMS_V_D64:
fprintf(f, "%lf", ldms_metric_get_double(set, i));
break;
}
}
static int
plaintext_store(ldmsd_plugin_inst_t pi, ldms_set_t set, ldmsd_strgp_t strgp)
{
/* `store` data from `set` into the store */
plaintext_inst_t inst = (void*)pi;
ldmsd_strgp_metric_t m;
const char *setname = ldms_set_instance_name_get(set);
for (m = ldmsd_strgp_metric_first(strgp);
m;
m = ldmsd_strgp_metric_next(m)) {
fprintf(inst->f, "%s:%s:%s:", setname, m->name,
ldms_metric_type_to_str(m->type));
fprint_metric_val(inst->f, set, m->idx);
fprintf(inst->f, "\n");
}
return 0;
}
/* ============== Common Plugin APIs ================= */
static const char *
plaintext_desc(ldmsd_plugin_inst_t pi)
{
return "plaintext - write data to plain text file";
}
static const char *
plaintext_help(ldmsd_plugin_inst_t pi)
{
return "plaintext config synopsis:\n"
" config name=INST [COMMON OPTIONS] path=PATH_TO_FILE\n"
"\n"
"Descriptions:\n"
" plaintext writes plain text data to the file specified\n"
" by `path` parameter. If the file does not exist,\n"
" it will be created. If the file existed, it will be\n"
" appended to.\n";
}
static int
plaintext_config(ldmsd_plugin_inst_t pi, json_entity_t json,
char *ebuf, int ebufsz)
{
ldmsd_store_type_t store = LDMSD_STORE(pi);
plaintext_inst_t inst = (void*)pi;
int rc;
const char *val;
rc = store->base.config(pi, json, ebuf, ebufsz);
if (rc)
return rc;
val = json_attr_find_str(json, "path");
if (!val) {
snprintf(ebuf, ebufsz, "missing `path` attribute.\n");
return EINVAL;
}
inst->path = strdup(val);
if (!inst->path) {
snprintf(ebuf, ebufsz, "Out of memory.\n");
return errno;
}
return 0;
}
static void
plaintext_del(ldmsd_plugin_inst_t pi)
{
plaintext_inst_t inst = (void*)pi;
if (inst->path)
free(inst->path);
if (inst->f)
fclose(inst->f);
}
static int
plaintext_init(ldmsd_plugin_inst_t pi)
{
plaintext_inst_t inst = (void*)pi;
ldmsd_store_type_t store = (void*)inst->base.base;
/* override store operations */
store->open = plaintext_open;
store->close = plaintext_close;
store->flush = plaintext_flush;
store->store = plaintext_store;
return 0;
}
static struct plaintext_inst_s __inst = {
.base = {
.version = LDMSD_PLUGIN_VERSION_INITIALIZER,
.type_name = LDMSD_STORE_TYPENAME,
.plugin_name = "plaintext",
/* Common Plugin APIs */
.desc = plaintext_desc,
.help = plaintext_help,
.init = plaintext_init,
.del = plaintext_del,
.config = plaintext_config,
},
/* plugin-specific data initialization (for new()) here */
};
ldmsd_plugin_inst_t
new()
{
plaintext_inst_t inst = malloc(sizeof(*inst));
if (inst)
*inst = __inst;
return &inst->base;
}
/* ----------------- EOF --------------------- */
```
This is the Makefile for building `plaintext` store:
```make
# file: ldms/src/ldmsd-stores/plaintext/Makefile.am
include ../common.am
libplaintext_la_SOURCES = plaintext.c
libplaintext_la_CFLAGS = $(STORE_CFLAGS)
libplaintext_la_LIBADD = $(STORE_LIBADD)
libplaintext_la_LDFLAGS = $(STORE_LDFLAGS)
pkglib_LTLIBRARIES = libplaintext.la
```
Configure and Makefile Modification
-----------------------------------
The following diff shows the modification to configure and Makefile relating to
the build of `plaintext` store:
```diff
--- a/ldms/configure.ac
+++ b/ldms/configure.ac
@@ -68,6 +68,7 @@ OPTION_DEFAULT_ENABLE([flatfile], [ENABLE_FLATFILE])
OPTION_DEFAULT_ENABLE([csv], [ENABLE_CSV])
OPTION_DEFAULT_DISABLE([rabbitkw], [ENABLE_RABBITKW])
OPTION_DEFAULT_DISABLE([rabbitv3], [ENABLE_RABBITV3])
+OPTION_DEFAULT_DISABLE([plaintext], [ENABLE_PLAINTEXT])
dnl AMQP
OPTION_DEFAULT_DISABLE([amqp], [ENABLE_AMQP])
@@ -524,6 +525,7 @@ AC_CONFIG_FILES([Makefile src/Makefile src/core/Makefile
src/ldmsd-stores/store_sos/Makefile
src/ldmsd-stores/store_csv/Makefile
src/ldmsd-stores/store_amqp/Makefile
+ src/ldmsd-stores/plaintext/Makefile
scripts/Makefile
src/test/Makefile
etc/Makefile
--- a/ldms/src/ldmsd-stores/Makefile.am
+++ b/ldms/src/ldmsd-stores/Makefile.am
@@ -15,3 +15,7 @@ endif
if ENABLE_AMQP
SUBDIRS += store_amqp
endif
+
+if ENABLE_PLAINTEXT
+SUBDIRS += plaintext
+endif
```
Building
--------
The following steps will build ldms with `plaintext` store.
```sh
$ cd ldms/
$ ./autogen.sh
$ mkdir build
$ cd build/
$ ../configure --prefix=/opt/ldms --with-ovis-lib=/opt/ovis-lib \
--enable-plaintext
$ make && make install
```
Running ldmsds to test the store
--------------------------------
We need a sampler `ldmsd` (see [ldmsd-sampler][samp](7)) as a data source to
feed to an aggregator `ldmsd` (see [ldmsd-aggregator][agg](7)) that use
`plaintext` to store data. We will setup a sampler `ldmsd` on localhost:10001
with `meminfo` plugin, and an aggregator `ldmsd` on localhost:9001 with
`plaintext` store. Running them in foreground mode `-F` also helps in debugging.
```sh
# on one terminal
$ ldmsd -F -x sock:10001 -c samp.conf
# on another terminal
$ ldmsd -F -x sock:9001 -c agg.conf
```
The following is the content of `samp.conf`:
```
# samp.conf
load name=mem plugin=meminfo
config name=mem
smplr_add name=smplr instance=mem component_id=20
smplr_start name=smplr interval=1000000 offset=0
```
And, the following is the content of `agg.conf`:
```
# agg.conf
#### prdcr ####
prdcr_add name=prdcr host=localhost port=10001 xprt=sock \
interval=1000000 type=active
prdcr_start name=prdcr interval=1000000
#### store ####
load name=pt plugin=plaintext
config name=pt path=meminfo.txt buffer=0
strgp_add name=sp container=pt schema=meminfo
strgp_prdcr_add name=sp regex=.*
strgp_start name=sp
#### updtr ####
updtr_add name=updtr interval=1000000 offset=500000
updtr_prdcr_add name=updtr regex=.*
updtr_start name=updtr
```
Verifying
---------
Tailing the output file `meminfo.txt` to see the data written out:
```sh
$ tail -f meminfo.txt
```
The output might look paused sometimes due to file buffering.
SEE ALSO
========
[ldmsd-sampler-dev][samp-dev](7),
[ldmsd-aggregator][agg](7)
[ldmsd-sampler][samp](7)
[agg]: ldmsd-aggregator.md
[samp]: ldmsd-sampler.md
[samp-dev]: ldmsd-sampler-dev.md
[json_util.h]: ../../../lib/src/json/json_util.h