diff --git a/ldms/src/sampler/procnetdev2/Plugin_procnetdev2.man b/ldms/src/sampler/procnetdev2/Plugin_procnetdev2.man index 12636ea2a..936fb1754 100644 --- a/ldms/src/sampler/procnetdev2/Plugin_procnetdev2.man +++ b/ldms/src/sampler/procnetdev2/Plugin_procnetdev2.man @@ -6,9 +6,15 @@ Plugin_procnetdev2 - man page for the LDMS procnetdev2 plugin .SH SYNOPSIS -Within ldmsd_controller or a configuration file: +.B load +.RI name= INST_NAME +.RB plugin= procnetdev2 .br -config name=procnetdev2 [ = ] +.B config +.RI name= INST_NAME +.RI [ = ] +.RI [ifaces= CSV_STR ] +.RI [excludes= CSV_STR ] .SH DESCRIPTION With LDMS (Lightweight Distributed Metric Service), plugins for the ldmsd (ldms @@ -18,43 +24,56 @@ from /proc/net/dev. .SH CONFIGURATION ATTRIBUTE SYNTAX The procnetdev2 plugin uses the sampler_base base class. This man page covers -only the configuration attributes, or those with default values, specific to the +only the configuration attributes, or those with default values, specific to this plugin; see ldms_sampler_base.man for the attributes of the base class. .TP .BR config -name= [ifaces=] +.RI name= INST_NAME +.RI [ifaces= CSV_STR ] +.RI [excludes= CSV_STR ] .br configuration line .RS .TP -name= +.RI name= INST_NAME .br -This MUST be procnetdev2. +The name given at \fBload\fR command. .TP -ifaces= +.RI [ifaces= CSV_STR ] .br -(Optional) A CSV list of interfaces to sample. If not specified, all available -interfaces in /proc/net/dev will be reported. It is OK to specify non-existing -interfaces in the ifaces list. +(Optional) A comma-separated list of interface names (e.g. eth0,eth1) to be +collected. If NOT specified, all interfaces are included (unluss the `excludes` +option exclude them). .TP -schema= +.RI [excludes= CSV_STR ] .br -Optional schema name. It is intended that the same sampler on different nodes -with different metrics or ifaces have a different schema. If not specified, will -default to `procnetdev`. +(Optional) A comma-separated list of interface names (e.g. lo,eth0) to be +excluded from the collection. .RE -.SH BUGS -The maximum number of interfaces is limited to 32. + +If \fBifaces\fR and \fBexcludes\fR are NOT specified, the sampler collects data +from all interfaces. If only \fBifaces\fR is specified, the sampler only +collects data from interfaces in the ifaces list. If only \fBexcludes\fR is +specified, the sampler collects data from all interfaces EXCEPT those in the +\fBexcludes\fR option. If both \fBifaces\fR and \fBexcludes\fR are specified, +the sampler collects data from all interfaces that are in \fBifaces\fR option +but are NOT in the \fBexcludes\fR option. .SH EXAMPLES .PP Within ldmsd_controller or a configuration file: .nf -load name=procnetdev -config name=procnetdev producer=vm1_1 instance=vm1_1/procnetdev2 ifaces=eth0,eth1 -start name=procnetdev interval=1000000 offset=0 + + load name=samp plugin=procnetdev + config name=samp producer=vm1_1 instance=vm1_1/procnetdev2 ifaces=eth0,eth1 + start name=samp interval=1000000 offset=0 + + load name=samp2 plugin=procnetdev + config name=samp2 producer=vm1_1 instance=vm1_1/nolo excludes=lo + start name=samp2 interval=1000000 offset=0 + .fi .SH SEE ALSO diff --git a/ldms/src/sampler/procnetdev2/procnetdev2.c b/ldms/src/sampler/procnetdev2/procnetdev2.c index a99c4f2ee..302ad4fcb 100644 --- a/ldms/src/sampler/procnetdev2/procnetdev2.c +++ b/ldms/src/sampler/procnetdev2/procnetdev2.c @@ -1,8 +1,8 @@ /* -*- c-basic-offset: 8 -*- - * Copyright (c) 2010-2016,2018,2022 National Technology & Engineering Solutions + * Copyright (c) 2010-2016,2018,2022,2024 National Technology & Engineering Solutions * of Sandia, LLC (NTESS). Under the terms of Contract DE-NA0003525 with * NTESS, the U.S. Government retains certain rights in this software. - * Copyright (c) 2010-2016,2018,2022 Open Grid Computing, Inc. All rights + * Copyright (c) 2010-2016,2018,2022,2024 Open Grid Computing, Inc. All rights * reserved. * * This software is available to you under a choice of one of two @@ -75,6 +75,28 @@ #define PROC_FILE "/proc/net/dev" static char *procfile = PROC_FILE; +typedef struct procnetdev2_s *procnetdev2_t; +struct procnetdev2_s { + union { + struct ldmsd_sampler sampler; /* sampler interface */ + struct ldmsd_plugin plugin; /* plugin interface */ + }; + FILE *mf; + base_data_t base_data; + + int rec_def_idx; + int netdev_list_mid; + size_t rec_heap_sz; + + int niface; + char **iface; + char *iface_str; + + int nexcludes; + char **excludes; + char *excludes_str; +}; + struct rec_metric_info { int mid; const char *name; @@ -119,75 +141,52 @@ static int rec_metric_ids[REC_METRICS_LEN]; */ -static int rec_def_idx; -static int netdev_list_mid; -static size_t rec_heap_sz; - -static int niface = 0; -//max number of interfaces we can include. -static char iface[MAXIFACE][20]; - #define SAMP "procnetdev2" -static FILE *mf = NULL; -static base_data_t base; static ovis_log_t mylog; -static ldms_set_t get_set(struct ldmsd_sampler *self) +static int create_metric_set(procnetdev2_t p) { - if (base) - return base->set; - return NULL; -} - -static int create_metric_set(base_data_t base) -{ - ldms_schema_t schema; + static ldms_schema_t schema; ldms_record_t rec_def; size_t heap_sz; int rc; - mf = fopen(procfile, "r"); - if (!mf) { - ovis_log(mylog, OVIS_LERROR, "Could not open " SAMP " file " - "'%s'...exiting\n", - procfile); - return ENOENT; - } - /* Create a metric set of the required size */ - schema = base_schema_new(base); + schema = base_schema_new(p->base_data); if (!schema) { ovis_log(mylog, OVIS_LERROR, "%s: The schema '%s' could not be created, errno=%d.\n", - __FILE__, base->schema_name, errno); + __FILE__, p->base_data->schema_name, errno); rc = EINVAL; goto err1; } /* Create netdev record definition */ rec_def = ldms_record_from_template("netdev", rec_metrics, rec_metric_ids); - if (!rec_def) + if (!rec_def) { + rc = errno; goto err2; - rec_heap_sz = ldms_record_heap_size_get(rec_def); + } + p->rec_heap_sz = ldms_record_heap_size_get(rec_def); heap_sz = MAXIFACE * ldms_record_heap_size_get(rec_def); /* Add record definition into the schema */ - rec_def_idx = ldms_schema_record_add(schema, rec_def); - if (rec_def_idx < 0) { - rc = -rec_def_idx; + p->rec_def_idx = ldms_schema_record_add(schema, rec_def); + if (p->rec_def_idx < 0) { + rc = -p->rec_def_idx; goto err3; } /* Add a list (of records) */ - netdev_list_mid = ldms_schema_metric_list_add(schema, "netdev_list", NULL, heap_sz); - if (netdev_list_mid < 0) { - rc = -netdev_list_mid; + p->netdev_list_mid = ldms_schema_metric_list_add(schema, "netdev_list", NULL, heap_sz); + if (p->netdev_list_mid < 0) { + rc = -p->netdev_list_mid; goto err2; } - base_set_new(base); - if (!base->set) { + base_set_new(p->base_data); + if (!p->base_data->set) { rc = errno; goto err2; } @@ -198,12 +197,9 @@ static int create_metric_set(base_data_t base) to the schema */ ldms_record_delete(rec_def); err2: - base_schema_delete(base); - base = NULL; + base_schema_delete(p->base_data); + p->base_data = NULL; err1: - if (mf) - fclose(mf); - mf = NULL; return rc; } @@ -233,18 +229,65 @@ static int config_check(struct attr_value_list *kwl, struct attr_value_list *avl static const char *usage(struct ldmsd_plugin *self) { - return "config name=" SAMP " ifaces=\n" \ + return "config name=" SAMP " [ifaces=] [excludes=]\n" \ BASE_CONFIG_USAGE \ - " A comma-separated list of interface names (e.g. eth0,eth1)\n" - " Order matters. All ifaces will be included\n" - " whether they exist of not up to a total of MAXIFACE\n"; + " ifaces A comma-separated list of interface names (e.g. eth0,eth1)\n" + " to be collected. If NOT specified, all interfaces are\n" + " included (unluss the `excludes` option exclude them).\n" + " excludes A comma-separated list of interface names (e.g. lo,eth0)\n" + " to be excluded from the collection.\n" + "\n" + "If `ifaces` and `excludes` are NOT specified, the sampler collects data from\n" + "all interfaces. If only `ifaces` is specified, the sampler only collects data\n" + "from interfaces in the ifaces list. If only `excludes` is specified,\n" + "the sampler collects data from all interfaces EXCEPT those in the `excludes`\n" + "option. If both `ifaces` and `excludes` are specified, the sampler collects\n" + "data from all interfaces that are in `ifaces` option but are NOT in the\n" + "`excludes` option.\n" + ; +} + +int strpcmp(const void *a, const void *b) +{ + return strcmp(*(char**)a, *(char**)b); +} + +char **strarray(char *s, int *n_out) +{ + char **arr = NULL; + char *pch, *saveptr; + int i, n; + + n = 1; + for (pch = s; *pch; pch++) { + if (*pch == ',') + n++; + } + + arr = malloc(n*sizeof(arr[0])); + if (!arr) + goto out; + /* fill */ + i = 0; + pch = strtok_r(s, ",", &saveptr); + while (pch != NULL){ + arr[i++] = pch; + pch = strtok_r(NULL, ",", &saveptr); + } + + /* sort iface array by strcmp */ + qsort(arr, n, sizeof(arr[0]), strpcmp); + *n_out = n; + + out: + return arr; } static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct attr_value_list *avl) { + procnetdev2_t p = (void*)self; char* ifacelist = NULL; - char* pch = NULL; - char *saveptr = NULL; + char* excludes_str = NULL; char *ivalue = NULL; void *arg = NULL; int rc; @@ -254,7 +297,7 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct return rc; } - if (base) { + if (p->base_data) { ovis_log(mylog, OVIS_LERROR, "Set already created.\n"); return EINVAL; } @@ -262,39 +305,44 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct /* process ifaces */ ivalue = av_value(avl, "ifaces"); if (!ivalue) - goto cfg; + goto excludes; ifacelist = strdup(ivalue); if (!ifacelist) { ovis_log(mylog, OVIS_LCRIT, "out of memory\n"); goto err; } - pch = strtok_r(ifacelist, ",", &saveptr); - while (pch != NULL){ - if (niface >= (MAXIFACE-1)) { - ovis_log(mylog, OVIS_LERROR, "too many ifaces: <%s>\n", - pch); - goto err; - } - snprintf(iface[niface], 20, "%s", pch); - ovis_log(mylog, OVIS_LDEBUG, "added iface <%s>\n", iface[niface]); - niface++; - pch = strtok_r(NULL, ",", &saveptr); + + p->iface = strarray(ifacelist, &p->niface); + if (!p->iface) { + goto err; } - free(ifacelist); - ifacelist = NULL; + p->iface_str = ifacelist; - if (niface == 0) + excludes: + /* process excludes */ + ivalue = av_value(avl, "excludes"); + if (!ivalue) + goto cfg; + excludes_str = strdup(ivalue); + if (!excludes_str) { + ovis_log(mylog, OVIS_LCRIT, "out of memory\n"); + goto err; + } + p->excludes = strarray(excludes_str, &p->nexcludes); + if (!p->excludes) { goto err; + } + p->excludes_str = excludes_str; cfg: - base = base_config(avl, SAMP, SAMP, mylog); - if (!base){ + p->base_data = base_config(avl, SAMP, SAMP, mylog); + if (!p->base_data){ rc = EINVAL; goto err; } - rc = create_metric_set(base); + rc = create_metric_set(p); if (rc) { ovis_log(mylog, OVIS_LERROR, "failed to create a metric set.\n"); goto err; @@ -303,51 +351,61 @@ static int config(struct ldmsd_plugin *self, struct attr_value_list *kwl, struct return 0; err: + p->iface_str = NULL; + p->niface = 0; + if (p->iface) { + free(p->iface); + p->iface = NULL; + } if (ifacelist) free(ifacelist); - base_del(base); + if (excludes_str) + free(excludes_str); + base_del(p->base_data); return rc; } static int sample(struct ldmsd_sampler *self) { + procnetdev2_t p = (void*)self; int rc; char *s; char lbuf[256]; - char curriface[IFNAMSIZ]; + char _curriface[IFNAMSIZ]; + char *curriface = _curriface; union ldms_value v[REC_METRICS_LEN]; int i; ldms_mval_t lh, rec_inst, name_mval; size_t heap_sz; - if (!base) { + if (!p->base_data) { ovis_log(mylog, OVIS_LDEBUG, "plugin not initialized\n"); return EINVAL; } - if (!mf) - mf = fopen(procfile, "r"); - if (!mf) { + if (!p->mf) + p->mf = fopen(procfile, "r"); + if (!p->mf) { ovis_log(mylog, OVIS_LERROR, "Could not open /proc/net/dev file " "'%s'...exiting\n", procfile); return ENOENT; } begin: - base_sample_begin(base); + base_sample_begin(p->base_data); - lh = ldms_metric_get(base->set, netdev_list_mid); + lh = ldms_metric_get(p->base_data->set, p->netdev_list_mid); /* reset device data */ - ldms_list_purge(base->set, lh); + ldms_list_purge(p->base_data->set, lh); - fseek(mf, 0, SEEK_SET); //seek should work if get to EOF - s = fgets(lbuf, sizeof(lbuf), mf); - s = fgets(lbuf, sizeof(lbuf), mf); + fseek(p->mf, 0, SEEK_SET); //seek should work if get to EOF + s = fgets(lbuf, sizeof(lbuf), p->mf); + s = fgets(lbuf, sizeof(lbuf), p->mf); /* data */ do { - s = fgets(lbuf, sizeof(lbuf), mf); + s = fgets(lbuf, sizeof(lbuf), p->mf); if (!s) break; @@ -372,17 +430,23 @@ static int sample(struct ldmsd_sampler *self) continue; } - if (niface) { - /* ifaces list was given in config */ - for (i = 0; i < niface; i++) { - if (strcmp(curriface, iface[i]) == 0) - goto rec; + if (p->niface) { /* ifaces list was given in config */ + if (bsearch(&curriface, p->iface, p->niface, + sizeof(p->iface[0]), strpcmp)) { + goto rec; } /* not in the ifaces list */ continue; } rec: - rec_inst = ldms_record_alloc(base->set, rec_def_idx); + /* must check if `curriface` is excluded */ + if (p->nexcludes) { + if (bsearch(&curriface, p->excludes, p->nexcludes, + sizeof(p->excludes[0]), strpcmp)) { + continue; + } + } + rec_inst = ldms_record_alloc(p->base_data->set, p->rec_def_idx); if (!rec_inst) goto resize; /* iface name */ @@ -392,20 +456,20 @@ static int sample(struct ldmsd_sampler *self) for (i = 1; i < REC_METRICS_LEN; i++) { ldms_record_set_u64(rec_inst, rec_metric_ids[i], v[i].v_u64); } - ldms_list_append_record(base->set, lh, rec_inst); + ldms_list_append_record(p->base_data->set, lh, rec_inst); } while (s); - base_sample_end(base); + base_sample_end(p->base_data); return 0; resize: /* * We intend to leave the set in the inconsistent state so that * the aggregators are aware that some metrics have not been newly sampled. */ - heap_sz = ldms_set_heap_size_get(base->set) + 2*rec_heap_sz; - base_set_delete(base); - base_set_new_heap(base, heap_sz); - if (!base->set) { + heap_sz = ldms_set_heap_size_get(p->base_data->set) + 2*p->rec_heap_sz; + base_set_delete(p->base_data); + base_set_new_heap(p->base_data, heap_sz); + if (!p->base_data->set) { rc = errno; ovis_log(mylog, OVIS_LCRITICAL, SAMP " : Failed to create a set with " "a bigger heap. Error %d\n", rc); @@ -417,38 +481,67 @@ static int sample(struct ldmsd_sampler *self) static void term(struct ldmsd_plugin *self) { - if (mf) - fclose(mf); - mf = NULL; - base_set_delete(base); - base_del(base); - base = NULL; - if (mylog) - ovis_log_destroy(mylog); + procnetdev2_t p = (void*)self; + if (p->mf) { + fclose(p->mf); + p->mf = NULL; + } + p->mf = NULL; + base_set_delete(p->base_data); + base_del(p->base_data); + p->base_data = NULL; + + if (p->iface) { + free(p->iface); + p->iface = NULL; + } + if (p->iface_str) { + free(p->iface_str); + p->iface_str = NULL; + } + p->niface = 0; } -static struct ldmsd_sampler procnetdev2_plugin = { - .base = { - .name = SAMP, - .type = LDMSD_PLUGIN_SAMPLER, - .term = term, - .config = config, - .usage = usage, - }, - .get_set = get_set, - .sample = sample, -}; +static void __procnetdev2_del(struct ldmsd_cfgobj *self) +{ + procnetdev2_t p = (void*)self; + free(p); +} -struct ldmsd_plugin *get_plugin() +static void __once() { - int rc; - base = NULL; + static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; + pthread_mutex_lock(&mutex); + if (mylog) + goto out; mylog = ovis_log_register("sampler."SAMP, "Message for the " SAMP " plugin"); if (!mylog) { - rc = errno; ovis_log(NULL, OVIS_LWARN, "Failed to create the log subsystem " - "of '" SAMP "' plugin. Error %d\n", rc); + "of '" SAMP "' plugin. Error %d\n", errno); } - return &procnetdev2_plugin.base; + out: + pthread_mutex_unlock(&mutex); +} + +struct ldmsd_plugin *get_plugin_instance(const char *name, + uid_t uid, gid_t gid, int perm) +{ + procnetdev2_t p; + + __once(); + + p = (void*)ldmsd_sampler_alloc(name, sizeof(*p), __procnetdev2_del, + uid, gid, perm); + if (!p) + return NULL; + + snprintf(p->plugin.name, sizeof(p->plugin.name), "%s", SAMP); + p->plugin.term = term; + p->plugin.config = config; + p->plugin.usage = usage; + + p->sampler.sample = sample; + + return &p->plugin; }