diff options
Diffstat (limited to '')
-rw-r--r-- | src/plugin_tc.c | 679 |
1 files changed, 363 insertions, 316 deletions
diff --git a/src/plugin_tc.c b/src/plugin_tc.c index 0fa59532..7dcfedb3 100644 --- a/src/plugin_tc.c +++ b/src/plugin_tc.c @@ -1,7 +1,6 @@ #include "common.h" -#define RRD_TYPE_TC "tc" -#define RRD_TYPE_TC_LEN strlen(RRD_TYPE_TC) +#define RRD_TYPE_TC "tc" // ---------------------------------------------------------------------------- // /sbin/tc processor @@ -25,6 +24,9 @@ struct tc_class { char hasparent; char isleaf; + char isqdisc; + char render; + unsigned long long bytes; unsigned long long packets; unsigned long long dropped; @@ -68,6 +70,7 @@ struct tc_device { char enabled_dropped; char enabled_tokens; char enabled_ctokens; + char enabled_all_classes_qdiscs; RRDSET *st_bytes; RRDSET *st_packets; @@ -184,103 +187,171 @@ static inline void tc_device_classes_cleanup(struct tc_device *d) { } static inline void tc_device_commit(struct tc_device *d) { - static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1, enable_tokens = -1, enable_ctokens = -1; + static int enable_new_interfaces = -1, enable_bytes = -1, enable_packets = -1, enable_dropped = -1, enable_tokens = -1, enable_ctokens = -1, enabled_all_classes_qdiscs = -1; if(unlikely(enable_new_interfaces == -1)) { - enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_ONDEMAND_YES); - enable_bytes = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - enable_packets = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - enable_dropped = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_ONDEMAND_ONDEMAND); - enable_tokens = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_ONDEMAND_NO); - enable_ctokens = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_ONDEMAND_NO); + enable_new_interfaces = config_get_boolean_ondemand("plugin:tc", "enable new interfaces detected at runtime", CONFIG_BOOLEAN_YES); + enable_bytes = config_get_boolean_ondemand("plugin:tc", "enable traffic charts for all interfaces", CONFIG_BOOLEAN_AUTO); + enable_packets = config_get_boolean_ondemand("plugin:tc", "enable packets charts for all interfaces", CONFIG_BOOLEAN_AUTO); + enable_dropped = config_get_boolean_ondemand("plugin:tc", "enable dropped charts for all interfaces", CONFIG_BOOLEAN_AUTO); + enable_tokens = config_get_boolean_ondemand("plugin:tc", "enable tokens charts for all interfaces", CONFIG_BOOLEAN_NO); + enable_ctokens = config_get_boolean_ondemand("plugin:tc", "enable ctokens charts for all interfaces", CONFIG_BOOLEAN_NO); + enabled_all_classes_qdiscs = config_get_boolean_ondemand("plugin:tc", "enable show all classes and qdiscs for all interfaces", CONFIG_BOOLEAN_NO); + } + + if(unlikely(d->enabled == (char)-1)) { + char var_name[CONFIG_MAX_NAME + 1]; + snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id); + + d->enabled = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces); + + snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id); + d->enabled_bytes = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); + + snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id); + d->enabled_packets = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_packets); + + snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id); + d->enabled_dropped = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped); + + snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id); + d->enabled_tokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens); + + snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id); + d->enabled_ctokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); + + snprintfz(var_name, CONFIG_MAX_NAME, "show all classes for %s", d->id); + d->enabled_all_classes_qdiscs = (char)config_get_boolean_ondemand("plugin:tc", var_name, enabled_all_classes_qdiscs); } // we only need to add leaf classes - struct tc_class *c, *x; + struct tc_class *c, *x /*, *root = NULL */; unsigned long long bytes_sum = 0, packets_sum = 0, dropped_sum = 0, tokens_sum = 0, ctokens_sum = 0; - int active_classes = 0; + int active_nodes = 0, updated_classes = 0, updated_qdiscs = 0; + + // prepare all classes + // we set reasonable defaults for the rest of the code below - // set all classes for(c = d->classes ; c ; c = c->next) { - c->isleaf = 1; - c->hasparent = 0; + c->render = 0; // do not render this class + + c->isleaf = 1; // this is a leaf class + c->hasparent = 0; // without a parent if(unlikely(!c->updated)) - c->unupdated++; - else - c->unupdated = 0; + c->unupdated++; // increase its unupdated counter + else { + c->unupdated = 0; // reset its unupdated counter + + // count how many of each kind + if(c->isqdisc) + updated_qdiscs++; + else + updated_classes++; + } } - // mark the classes as leafs and parents - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + if(unlikely(!d->enabled || (!updated_classes && !updated_qdiscs))) { + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. It is not enabled/updated.", d->name?d->name:d->id); + tc_device_classes_cleanup(d); + return; + } - for(x = d->classes ; x ; x = x->next) { - if(unlikely(!x->updated)) continue; + if(unlikely(updated_classes && updated_qdiscs)) { + error("TC: device '%s' has active both classes (%d) and qdiscs (%d). Will render only qdiscs.", d->id, updated_classes, updated_qdiscs); - if(unlikely(c == x)) continue; + // set all classes to !updated + for(c = d->classes ; c ; c = c->next) + if(unlikely(!c->isqdisc && c->updated)) + c->updated = 0; - if(x->parentid && ( - ( c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) || - (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0))) { - // debug(D_TC_LOOP, "TC: In device '%s', class '%s' (leafid: '%s') has as leaf class '%s' (parentid: '%s').", d->name?d->name:d->id, c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->name?x->name:x->id, x->parentid?x->parentid:x->id); - c->isleaf = 0; - x->hasparent = 1; - } - } + updated_classes = 0; } - // debugging only - /* - if(unlikely(debug_flags & D_TC_LOOP)) { - for(c = d->classes ; c ; c = c->next) { - if(c->isleaf && c->hasparent) debug(D_TC_LOOP, "TC: Device '%s', class %s, OK", d->name, c->id); - else debug(D_TC_LOOP, "TC: Device '%s', class %s, IGNORE (isleaf: %d, hasparent: %d, parent: %s)", d->name?d->name:d->id, c->id, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)"); + // mark the classes as leafs and parents + // + // TC is hierarchical: + // - classes can have other classes in them + // - the same is true for qdiscs (i.e. qdiscs have classes, that have other qdiscs) + // + // we need to present a chart with leaf nodes only, so that the sum + // of all dimensions of the chart, will be the total utilization + // of the interface. + // + // here we try to find the ones we need to report + // by default all nodes are marked with: isleaf = 1 (see above) + // + // so, here we remove the isleaf flag from nodes in the middle + // and we add the hasparent flag to leaf nodes we found their parent + if(likely(!d->enabled_all_classes_qdiscs)) { + for(c = d->classes; c; c = c->next) { + if(unlikely(!c->updated)) continue; + + //debug(D_TC_LOOP, "TC: In device '%s', %s '%s' has leafid: '%s' and parentid '%s'.", + // d->id, + // c->isqdisc?"qdisc":"class", + // c->id, + // c->leafid?c->leafid:"NULL", + // c->parentid?c->parentid:"NULL"); + + // find if c is leaf or not + for(x = d->classes; x; x = x->next) { + if(unlikely(!x->updated || c == x || !x->parentid)) continue; + + // classes have both parentid and leafid + // qdiscs have only parentid + // the following works for both (it is an OR) + + if((c->hash == x->parent_hash && strcmp(c->id, x->parentid) == 0) || + (c->leafid && c->leaf_hash == x->parent_hash && strcmp(c->leafid, x->parentid) == 0)) { + // debug(D_TC_LOOP, "TC: In device '%s', %s '%s' (leafid: '%s') has as leaf %s '%s' (parentid: '%s').", d->name?d->name:d->id, c->isqdisc?"qdisc":"class", c->name?c->name:c->id, c->leafid?c->leafid:c->id, x->isqdisc?"qdisc":"class", x->name?x->name:x->id, x->parentid?x->parentid:x->id); + c->isleaf = 0; + x->hasparent = 1; + } + } } } - */ - // we need at least a class for(c = d->classes ; c ; c = c->next) { - // debug(D_TC_LOOP, "TC: Device '%s', class '%s', isLeaf=%d, HasParent=%d, Seen=%d", d->name?d->name:d->id, c->name?c->name:c->id, c->isleaf, c->hasparent, c->seen); - if(unlikely(c->updated && c->isleaf && c->hasparent)) { - active_classes++; + if(unlikely(!c->updated)) continue; + + // debug(D_TC_LOOP, "TC: device '%s', %s '%s' isleaf=%d, hasparent=%d", d->id, (c->isqdisc)?"qdisc":"class", c->id, c->isleaf, c->hasparent); + + if(unlikely((c->isleaf && c->hasparent) || d->enabled_all_classes_qdiscs)) { + c->render = 1; + active_nodes++; bytes_sum += c->bytes; packets_sum += c->packets; dropped_sum += c->dropped; tokens_sum += c->tokens; ctokens_sum += c->ctokens; } - } - if(unlikely(!active_classes)) { - debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No leaf classes.", d->name?d->name:d->id); - tc_device_classes_cleanup(d); - return; + //if(unlikely(!c->hasparent)) { + // if(root) error("TC: multiple root class/qdisc for device '%s' (old: '%s', new: '%s')", d->id, root->id, c->id); + // root = c; + // debug(D_TC_LOOP, "TC: found root class/qdisc '%s'", root->id); + //} } - if(unlikely(d->enabled == (char)-1)) { - char var_name[CONFIG_MAX_NAME + 1]; - snprintfz(var_name, CONFIG_MAX_NAME, "qos for %s", d->id); - d->enabled = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_new_interfaces); - - snprintfz(var_name, CONFIG_MAX_NAME, "traffic chart for %s", d->id); - d->enabled_bytes = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_bytes); +#ifdef NETDATA_INTERNAL_CHECKS + // dump all the list to see what we know - snprintfz(var_name, CONFIG_MAX_NAME, "packets chart for %s", d->id); - d->enabled_packets = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_packets); - - snprintfz(var_name, CONFIG_MAX_NAME, "dropped packets chart for %s", d->id); - d->enabled_dropped = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_dropped); - - snprintfz(var_name, CONFIG_MAX_NAME, "tokens chart for %s", d->id); - d->enabled_tokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_tokens); + if(unlikely(debug_flags & D_TC_LOOP)) { + for(c = d->classes ; c ; c = c->next) { + if(c->render) debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, OK", d->name, c->id); + else debug(D_TC_LOOP, "TC: final nodes dump for '%s': class %s, IGNORE (updated: %d, isleaf: %d, hasparent: %d, parent: %s)", d->name?d->name:d->id, c->id, c->updated, c->isleaf, c->hasparent, c->parentid?c->parentid:"(unset)"); + } + } +#endif - snprintfz(var_name, CONFIG_MAX_NAME, "ctokens chart for %s", d->id); - d->enabled_ctokens = (char)config_get_boolean_ondemand("plugin:tc", var_name, enable_ctokens); + if(unlikely(!active_nodes)) { + debug(D_TC_LOOP, "TC: Ignoring TC device '%s'. No useful classes/qdiscs.", d->name?d->name:d->id); + tc_device_classes_cleanup(d); + return; } - debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d), classes = %d (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).", + debug(D_TC_LOOP, "TC: evaluating TC device '%s'. enabled = %d/%d (bytes: %d/%d, packets: %d/%d, dropped: %d/%d, tokens: %d/%d, ctokens: %d/%d, all_classes_qdiscs: %d/%d), classes: (bytes = %llu, packets = %llu, dropped = %llu, tokens = %llu, ctokens = %llu).", d->name?d->name:d->id, d->enabled, enable_new_interfaces, d->enabled_bytes, enable_bytes, @@ -288,7 +359,7 @@ static inline void tc_device_commit(struct tc_device *d) { d->enabled_dropped, enable_dropped, d->enabled_tokens, enable_tokens, d->enabled_ctokens, enable_ctokens, - active_classes, + d->enabled_all_classes_qdiscs, enabled_all_classes_qdiscs, bytes_sum, packets_sum, dropped_sum, @@ -296,287 +367,226 @@ static inline void tc_device_commit(struct tc_device *d) { ctokens_sum ); - if(likely(d->enabled)) { - // -------------------------------------------------------------------- - // bytes + // -------------------------------------------------------------------- + // bytes - if(d->enabled_bytes == CONFIG_ONDEMAND_YES || (d->enabled_bytes == CONFIG_ONDEMAND_ONDEMAND && bytes_sum)) { - d->enabled_bytes = CONFIG_ONDEMAND_YES; + if(d->enabled_bytes == CONFIG_BOOLEAN_YES || (d->enabled_bytes == CONFIG_BOOLEAN_AUTO && bytes_sum)) { + d->enabled_bytes = CONFIG_BOOLEAN_YES; - if(unlikely(!d->st_bytes)) { - d->st_bytes = rrdset_find_bytype(RRD_TYPE_TC, d->id); - if(unlikely(!d->st_bytes)) { - debug(D_TC_LOOP, "TC: Creating new chart for device '%s'", d->name?d->name:d->id); - d->st_bytes = rrdset_create(RRD_TYPE_TC, d->id, d->name?d->name:d->id, d->family?d->family:d->id, RRD_TYPE_TC ".qos", "Class Usage", "kilobits/s", 7000, rrd_update_every, RRDSET_TYPE_STACKED); - } - } - else { - debug(D_TC_LOOP, "TC: Updating chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_bytes); + if(unlikely(!d->st_bytes)) + d->st_bytes = rrdset_create_localhost(RRD_TYPE_TC, d->id, d->name ? d->name : d->id + , d->family ? d->family : d->id, RRD_TYPE_TC ".qos", "Class Usage" + , "kilobits/s", 7000, localhost->rrd_update_every + , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE + : RRDSET_TYPE_STACKED); - if(unlikely(d->name_updated && d->name && strcmp(d->id, d->name) != 0)) { - rrdset_set_name(d->st_bytes, d->name); - d->name_updated = 0; - } + else { + rrdset_next(d->st_bytes); + if(unlikely(d->name_updated)) rrdset_set_name(d->st_bytes, d->name); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - if(c->isleaf && c->hasparent) { - if(unlikely(!c->rd_bytes)) { - c->rd_bytes = rrddim_find(d->st_bytes, c->id); - if(unlikely(!c->rd_bytes)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_bytes->id, c->id, c->name); + if(unlikely(!c->rd_bytes)) + c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRD_ALGORITHM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_bytes, c->rd_bytes, c->name); - // new class, we have to add it - c->rd_bytes = rrddim_add(d->st_bytes, c->id, c->name?c->name:c->id, 8, 1024, RRDDIM_INCREMENTAL); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_bytes->id, c->id); - } + rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes); + } + rrdset_done(d->st_bytes); + } - rrddim_set_by_pointer(d->st_bytes, c->rd_bytes, c->bytes); + // -------------------------------------------------------------------- + // packets - // if it has a name, different to the id - if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) { - // update the rrd dimension with the new name - debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_bytes->id, c->rd_bytes->id, c->name); - rrddim_set_name(d->st_bytes, c->rd_bytes, c->name); - } - } - } - rrdset_done(d->st_bytes); - } + if(d->enabled_packets == CONFIG_BOOLEAN_YES || (d->enabled_packets == CONFIG_BOOLEAN_AUTO && packets_sum)) { + d->enabled_packets = CONFIG_BOOLEAN_YES; - // -------------------------------------------------------------------- - // packets - - if(d->enabled_packets == CONFIG_ONDEMAND_YES || (d->enabled_packets == CONFIG_ONDEMAND_ONDEMAND && packets_sum)) { - d->enabled_packets = CONFIG_ONDEMAND_YES; + if(unlikely(!d->st_packets)) { + char id[RRD_ID_LENGTH_MAX + 1]; + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id); - if(unlikely(!d->st_packets)) { - char id[RRD_ID_LENGTH_MAX + 1]; + d->st_packets = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id + , RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010 + , localhost->rrd_update_every, d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE + : RRDSET_TYPE_STACKED); + } + else { + rrdset_next(d->st_packets); + + if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_packets", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_packets", d->name?d->name:d->id); - - d->st_packets = rrdset_find_bytype(RRD_TYPE_TC, id); - if(unlikely(!d->st_packets)) { - debug(D_TC_LOOP, "TC: Creating new _packets chart for device '%s'", d->name?d->name:d->id); - d->st_packets = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_packets", "Class Packets", "packets/s", 7010, rrd_update_every, RRDSET_TYPE_STACKED); - } + rrdset_set_name(d->st_packets, name); } - else { - debug(D_TC_LOOP, "TC: Updating _packets chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_packets); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - if(c->isleaf && c->hasparent) { - if(unlikely(!c->rd_packets)) { - c->rd_packets = rrddim_find(d->st_packets, c->id); - if(unlikely(!c->rd_packets)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_packets->id, c->id, c->name); + if(unlikely(!c->rd_packets)) + c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_packets, c->rd_packets, c->name); - // new class, we have to add it - c->rd_packets = rrddim_add(d->st_packets, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_packets->id, c->id); - } + rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets); + } + rrdset_done(d->st_packets); + } - rrddim_set_by_pointer(d->st_packets, c->rd_packets, c->packets); + // -------------------------------------------------------------------- + // dropped - // if it has a name, different to the id - if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) { - // update the rrd dimension with the new name - debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_packets->id, c->rd_packets->id, c->name); - rrddim_set_name(d->st_packets, c->rd_packets, c->name); - } - } - } - rrdset_done(d->st_packets); + if(d->enabled_dropped == CONFIG_BOOLEAN_YES || (d->enabled_dropped == CONFIG_BOOLEAN_AUTO && dropped_sum)) { + d->enabled_dropped = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_dropped)) { + char id[RRD_ID_LENGTH_MAX + 1]; + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id); + + d->st_dropped = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id + , RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s" + , 7020, localhost->rrd_update_every + , d->enabled_all_classes_qdiscs ? RRDSET_TYPE_LINE + : RRDSET_TYPE_STACKED); } + else { + rrdset_next(d->st_dropped); - // -------------------------------------------------------------------- - // dropped - - if(d->enabled_dropped == CONFIG_ONDEMAND_YES || (d->enabled_dropped == CONFIG_ONDEMAND_ONDEMAND && dropped_sum)) { - d->enabled_dropped = CONFIG_ONDEMAND_YES; - - if(unlikely(!d->st_dropped)) { - char id[RRD_ID_LENGTH_MAX + 1]; + if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_dropped", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_dropped", d->name?d->name:d->id); - - d->st_dropped = rrdset_find_bytype(RRD_TYPE_TC, id); - if(unlikely(!d->st_dropped)) { - debug(D_TC_LOOP, "TC: Creating new _dropped chart for device '%s'", d->name?d->name:d->id); - d->st_dropped = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_dropped", "Class Dropped Packets", "packets/s", 7020, rrd_update_every, RRDSET_TYPE_STACKED); - } + rrdset_set_name(d->st_dropped, name); } - else { - debug(D_TC_LOOP, "TC: Updating _dropped chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_dropped); - // FIXME - // update the family - } + // FIXME + // update the family + } - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - if(c->isleaf && c->hasparent) { - if(unlikely(!c->rd_dropped)) { - c->rd_dropped = rrddim_find(d->st_dropped, c->id); - if(unlikely(!c->rd_dropped)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_dropped->id, c->id, c->name); + if(unlikely(!c->rd_dropped)) + c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_INCREMENTAL); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_dropped, c->rd_dropped, c->name); - // new class, we have to add it - c->rd_dropped = rrddim_add(d->st_dropped, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_INCREMENTAL); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_dropped->id, c->id); - } + rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped); + } + rrdset_done(d->st_dropped); + } - rrddim_set_by_pointer(d->st_dropped, c->rd_dropped, c->dropped); + // -------------------------------------------------------------------- + // tokens - // if it has a name, different to the id - if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) { - // update the rrd dimension with the new name - debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_dropped->id, c->rd_dropped->id, c->name); - rrddim_set_name(d->st_dropped, c->rd_dropped, c->name); - } - } - } - rrdset_done(d->st_dropped); + if(d->enabled_tokens == CONFIG_BOOLEAN_YES || (d->enabled_tokens == CONFIG_BOOLEAN_AUTO && tokens_sum)) { + d->enabled_tokens = CONFIG_BOOLEAN_YES; + + if(unlikely(!d->st_tokens)) { + char id[RRD_ID_LENGTH_MAX + 1]; + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id); + + d->st_tokens = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id + , RRD_TYPE_TC ".qos_tokens", "Class Tokens", "tokens", 7030 + , localhost->rrd_update_every, RRDSET_TYPE_LINE); } + else { + rrdset_next(d->st_tokens); - // -------------------------------------------------------------------- - // tokens - - if(d->enabled_tokens == CONFIG_ONDEMAND_YES || (d->enabled_tokens == CONFIG_ONDEMAND_ONDEMAND && tokens_sum)) { - d->enabled_tokens = CONFIG_ONDEMAND_YES; - - if(unlikely(!d->st_tokens)) { - char id[RRD_ID_LENGTH_MAX + 1]; + if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_tokens", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_tokens", d->name?d->name:d->id); - - d->st_tokens = rrdset_find_bytype(RRD_TYPE_TC, id); - if(unlikely(!d->st_tokens)) { - debug(D_TC_LOOP, "TC: Creating new _tokens chart for device '%s'", d->name?d->name:d->id); - d->st_tokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_tokens", "Class Tokens", "tokens", 7030, rrd_update_every, RRDSET_TYPE_LINE); - } + rrdset_set_name(d->st_tokens, name); } - else { - debug(D_TC_LOOP, "TC: Updating _tokens chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_tokens); - // FIXME - // update the family + // FIXME + // update the family + } + + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; + + if(unlikely(!c->rd_tokens)) { + c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE); } + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_tokens, c->rd_tokens, c->name); - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; + rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens); + } + rrdset_done(d->st_tokens); + } - if(c->isleaf && c->hasparent) { - if(unlikely(!c->rd_tokens)) { - c->rd_tokens = rrddim_find(d->st_tokens, c->id); - if(unlikely(!c->rd_tokens)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_tokens->id, c->id, c->name); + // -------------------------------------------------------------------- + // ctokens - // new class, we have to add it - c->rd_tokens = rrddim_add(d->st_tokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_tokens->id, c->id); - } + if(d->enabled_ctokens == CONFIG_BOOLEAN_YES || (d->enabled_ctokens == CONFIG_BOOLEAN_AUTO && ctokens_sum)) { + d->enabled_ctokens = CONFIG_BOOLEAN_YES; - rrddim_set_by_pointer(d->st_tokens, c->rd_tokens, c->tokens); + if(unlikely(!d->st_ctokens)) { + char id[RRD_ID_LENGTH_MAX + 1]; + char name[RRD_ID_LENGTH_MAX + 1]; + snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id); + snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id); - // if it has a name, different to the id - if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) { - // update the rrd dimension with the new name - debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_tokens->id, c->rd_tokens->id, c->name); - rrddim_set_name(d->st_tokens, c->rd_tokens, c->name); - } - } - } - rrdset_done(d->st_tokens); + d->st_ctokens = rrdset_create_localhost(RRD_TYPE_TC, id, name, d->family ? d->family : d->id + , RRD_TYPE_TC ".qos_ctokens", "Class cTokens", "ctokens", 7040 + , localhost->rrd_update_every, RRDSET_TYPE_LINE); } + else { + debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id); + rrdset_next(d->st_ctokens); - // -------------------------------------------------------------------- - // ctokens - - if(d->enabled_ctokens == CONFIG_ONDEMAND_YES || (d->enabled_ctokens == CONFIG_ONDEMAND_ONDEMAND && ctokens_sum)) { - d->enabled_ctokens = CONFIG_ONDEMAND_YES; - - if(unlikely(!d->st_ctokens)) { - char id[RRD_ID_LENGTH_MAX + 1]; + if(unlikely(d->name_updated)) { char name[RRD_ID_LENGTH_MAX + 1]; - snprintfz(id, RRD_ID_LENGTH_MAX, "%s_ctokens", d->id); snprintfz(name, RRD_ID_LENGTH_MAX, "%s_ctokens", d->name?d->name:d->id); - - d->st_ctokens = rrdset_find_bytype(RRD_TYPE_TC, id); - if(unlikely(!d->st_ctokens)) { - debug(D_TC_LOOP, "TC: Creating new _ctokens chart for device '%s'", d->name?d->name:d->id); - d->st_ctokens = rrdset_create(RRD_TYPE_TC, id, name, d->family?d->family:d->id, RRD_TYPE_TC ".qos_ctokens", "Class cTokens", "ctokens", 7040, rrd_update_every, RRDSET_TYPE_LINE); - } + rrdset_set_name(d->st_ctokens, name); } - else { - debug(D_TC_LOOP, "TC: Updating _ctokens chart for device '%s'", d->name?d->name:d->id); - rrdset_next(d->st_ctokens); - - // FIXME - // update the family - } - - for(c = d->classes ; c ; c = c->next) { - if(unlikely(!c->updated)) continue; - if(c->isleaf && c->hasparent) { - if(unlikely(!c->rd_ctokens)) { - c->rd_ctokens = rrddim_find(d->st_ctokens, c->id); - if(unlikely(!c->rd_ctokens)) { - debug(D_TC_LOOP, "TC: Adding to chart '%s', dimension '%s' (name: '%s')", d->st_ctokens->id, c->id, c->name); + // FIXME + // update the family + } - // new class, we have to add it - c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRDDIM_ABSOLUTE); - } - else debug(D_TC_LOOP, "TC: Updating chart '%s', dimension '%s'", d->st_ctokens->id, c->id); - } + for(c = d->classes ; c ; c = c->next) { + if(unlikely(!c->render)) continue; - rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens); + if(unlikely(!c->rd_ctokens)) + c->rd_ctokens = rrddim_add(d->st_ctokens, c->id, c->name?c->name:c->id, 1, 1, RRD_ALGORITHM_ABSOLUTE); + else if(unlikely(c->name_updated)) + rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name); - // if it has a name, different to the id - if(unlikely(c->name_updated && c->name && strcmp(c->id, c->name) != 0)) { - // update the rrd dimension with the new name - debug(D_TC_LOOP, "TC: Setting chart '%s', dimension '%s' name to '%s'", d->st_ctokens->id, c->rd_ctokens->id, c->name); - rrddim_set_name(d->st_ctokens, c->rd_ctokens, c->name); - } - } - } - rrdset_done(d->st_ctokens); + rrddim_set_by_pointer(d->st_ctokens, c->rd_ctokens, c->ctokens); } + rrdset_done(d->st_ctokens); } tc_device_classes_cleanup(d); } -static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name) -{ +static inline void tc_device_set_class_name(struct tc_device *d, char *id, char *name) { + if(unlikely(!name || !*name)) return; + struct tc_class *c = tc_class_index_find(d, id, 0); if(likely(c)) { - freez(c->name); - c->name = NULL; + if(likely(c->name)) { + if(!strcmp(c->name, name)) return; + freez(c->name); + c->name = NULL; + } if(likely(name && *name && strcmp(c->id, name) != 0)) { debug(D_TC_LOOP, "TC: Setting device '%s', class '%s' name to '%s'", d->id, id, name); @@ -587,10 +597,15 @@ static inline void tc_device_set_class_name(struct tc_device *d, char *id, char } static inline void tc_device_set_device_name(struct tc_device *d, char *name) { - freez(d->name); - d->name = NULL; + if(unlikely(!name || !*name)) return; + + if(d->name) { + if(!strcmp(d->name, name)) return; + freez(d->name); + d->name = NULL; + } - if(likely(name && *name && strcmp(d->id, name) != 0)) { + if(likely(name && *name && strcmp(d->id, name))) { debug(D_TC_LOOP, "TC: Setting device '%s' name to '%s'", d->id, name); d->name = strdupz(name); d->name_updated = 1; @@ -639,7 +654,7 @@ static inline struct tc_device *tc_device_create(char *id) return(d); } -static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char *parentid, char *leafid) +static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char qdisc, char *parentid, char *leafid) { struct tc_class *c = tc_class_index_find(n, id, 0); @@ -655,6 +670,7 @@ static inline struct tc_class *tc_class_add(struct tc_device *n, char *id, char c->id = strdupz(id); c->hash = simple_hash(c->id); + c->isqdisc = qdisc; if(parentid && *parentid) { c->parentid = strdupz(parentid); c->parent_hash = simple_hash(c->parentid); @@ -767,6 +783,7 @@ void *tc_main(void *ptr) { uint32_t BEGIN_HASH = simple_hash("BEGIN"); uint32_t END_HASH = simple_hash("END"); + uint32_t QDISC_HASH = simple_hash("qdisc"); uint32_t CLASS_HASH = simple_hash("class"); uint32_t SENT_HASH = simple_hash("Sent"); uint32_t LENDED_HASH = simple_hash("lended:"); @@ -780,7 +797,7 @@ void *tc_main(void *ptr) { #endif uint32_t first_hash; - snprintfz(buffer, TC_LINE_MAX, "%s/tc-qos-helper.sh", config_get("plugins", "plugins directory", PLUGINS_DIR)); + snprintfz(buffer, TC_LINE_MAX, "%s/tc-qos-helper.sh", netdata_configured_plugins_dir); char *tc_script = config_get("plugin:tc", "script to run to get tc values", buffer); for(;1;) { @@ -790,7 +807,7 @@ void *tc_main(void *ptr) { struct tc_device *device = NULL; struct tc_class *class = NULL; - snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, rrd_update_every); + snprintfz(buffer, TC_LINE_MAX, "exec %s %d", tc_script, localhost->rrd_update_every); debug(D_TC_LOOP, "executing '%s'", buffer); fp = mypopen(buffer, (pid_t *)&tc_child_pid); @@ -815,25 +832,51 @@ void *tc_main(void *ptr) { first_hash = simple_hash(words[0]); - if(unlikely(device && first_hash == CLASS_HASH && strcmp(words[0], "class") == 0)) { + if(unlikely(device && ((first_hash == CLASS_HASH && strcmp(words[0], "class") == 0) || (first_hash == QDISC_HASH && strcmp(words[0], "qdisc") == 0)))) { // debug(D_TC_LOOP, "CLASS line on class id='%s', parent='%s', parentid='%s', leaf='%s', leafid='%s'", words[2], words[3], words[4], words[5], words[6]); - // words[1] : class type - // words[2] : N:XX - // words[3] : parent or root - if(likely(words[1] && words[2] && words[3] && (strcmp(words[3], "parent") == 0 || strcmp(words[3], "root") == 0))) { - //char *type = words[1]; // the class: htb, fq_codel, etc + char *type = words[1]; // the class/qdisc type: htb, fq_codel, etc + char *id = words[2]; // the class/qdisc major:minor + char *parent = words[3]; // the word 'parent' or 'root' + char *parentid = words[4]; // parentid + char *leaf = words[5]; // the word 'leaf' + char *leafid = words[6]; // leafid + + int parent_is_root = 0; + int parent_is_parent = 0; + if(likely(parent)) { + parent_is_parent = !strcmp(parent, "parent"); - // we are only interested for HTB classes - //if(strcmp(type, "htb") != 0) continue; + if(!parent_is_parent) + parent_is_root = !strcmp(parent, "root"); + } + + if(likely(type && id && (parent_is_root || parent_is_parent))) { + char qdisc = 0; + + if(first_hash == QDISC_HASH) { + qdisc = 1; - char *id = words[2]; // the class major:minor - char *parent = words[3]; // 'parent' or 'root' - char *parentid = words[4]; // the parent's id - char *leaf = words[5]; // 'leaf' - char *leafid = words[6]; // leafid + if(!strcmp(type, "ingress")) { + // we don't want to get the ingress qdisc + // there should be an IFB interface for this + + class = NULL; + continue; + } + + if(parent_is_parent && parentid) { + // eliminate the minor number from parentid + // why: parentid is the id of the parent class + // but major: is also the id of the parent qdisc + + char *s = parentid; + while(*s && *s != ':') s++; + if(*s == ':') s[1] = '\0'; + } + } - if(strcmp(parent, "root") == 0) { + if(parent_is_root) { parentid = NULL; leafid = NULL; } @@ -847,7 +890,7 @@ void *tc_main(void *ptr) { leafid = leafbuf; } - class = tc_class_add(device, id, parentid, leafid); + class = tc_class_add(device, id, qdisc, parentid, leafid); } else { // clear the last class @@ -946,11 +989,13 @@ void *tc_main(void *ptr) { // debug(D_TC_LOOP, "WORKTIME line '%s' '%s'", words[1], words[2]); getrusage(RUSAGE_THREAD, &thread); - if(unlikely(!stcpu)) stcpu = rrdset_find("netdata.plugin_tc_cpu"); + if(unlikely(!stcpu)) stcpu = rrdset_find_localhost("netdata.plugin_tc_cpu"); if(unlikely(!stcpu)) { - stcpu = rrdset_create("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL, "NetData TC CPU usage", "milliseconds/s", 135000, rrd_update_every, RRDSET_TYPE_STACKED); - rrddim_add(stcpu, "user", NULL, 1, 1000, RRDDIM_INCREMENTAL); - rrddim_add(stcpu, "system", NULL, 1, 1000, RRDDIM_INCREMENTAL); + stcpu = rrdset_create_localhost("netdata", "plugin_tc_cpu", NULL, "tc.helper", NULL + , "NetData TC CPU usage", "milliseconds/s", 135000, localhost->rrd_update_every + , RRDSET_TYPE_STACKED); + rrddim_add(stcpu, "user", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); + rrddim_add(stcpu, "system", NULL, 1, 1000, RRD_ALGORITHM_INCREMENTAL); } else rrdset_next(stcpu); @@ -958,10 +1003,12 @@ void *tc_main(void *ptr) { rrddim_set(stcpu, "system", thread.ru_stime.tv_sec * 1000000ULL + thread.ru_stime.tv_usec); rrdset_done(stcpu); - if(unlikely(!sttime)) stcpu = rrdset_find("netdata.plugin_tc_time"); + if(unlikely(!sttime)) sttime = rrdset_find_localhost("netdata.plugin_tc_time"); if(unlikely(!sttime)) { - sttime = rrdset_create("netdata", "plugin_tc_time", NULL, "tc.helper", NULL, "NetData TC script execution", "milliseconds/run", 135001, rrd_update_every, RRDSET_TYPE_AREA); - rrddim_add(sttime, "run_time", "run time", 1, 1, RRDDIM_ABSOLUTE); + sttime = rrdset_create_localhost("netdata", "plugin_tc_time", NULL, "tc.helper", NULL + , "NetData TC script execution", "milliseconds/run", 135001 + , localhost->rrd_update_every, RRDSET_TYPE_AREA); + rrddim_add(sttime, "run_time", "run time", 1, 1, RRD_ALGORITHM_ABSOLUTE); } else rrdset_next(sttime); @@ -1009,7 +1056,7 @@ void *tc_main(void *ptr) { goto cleanup; } - sleep((unsigned int) rrd_update_every); + sleep((unsigned int) localhost->rrd_update_every); } cleanup: |