diff --git a/docs/mibs/OCP-ONL-RESOURCE-MIB.txt b/docs/mibs/OCP-ONL-RESOURCE-MIB.txt index 21da5843..3167c31b 100644 --- a/docs/mibs/OCP-ONL-RESOURCE-MIB.txt +++ b/docs/mibs/OCP-ONL-RESOURCE-MIB.txt @@ -34,7 +34,7 @@ CpuAllPercentUtilization OBJECT-TYPE MAX-ACCESS read-only STATUS current DESCRIPTION - "The average CPU utilization (in percent). Provided by mpstat." + "The average CPU utilization in percent, multiplied by 100 and rounded to the nearest integer. Provided by mpstat." ::= { Basic 1 } CpuAllPercentIdle OBJECT-TYPE @@ -42,7 +42,7 @@ CpuAllPercentIdle OBJECT-TYPE MAX-ACCESS read-only STATUS current DESCRIPTION - "The average CPU idle time (in percent). Provided by mpstat." + "The average CPU idle time in percent, multiplied by 100 and rounded to the nearest integer. Provided by mpstat." ::= { Basic 2 } END diff --git a/packages/base/any/onlp-snmpd/APKG.yml b/packages/base/any/onlp-snmpd/APKG.yml index 76214600..bb37f217 100644 --- a/packages/base/any/onlp-snmpd/APKG.yml +++ b/packages/base/any/onlp-snmpd/APKG.yml @@ -16,6 +16,7 @@ packages: files: builds/$BUILD_DIR/${TOOLCHAIN}/bin/onlp-snmpd: /usr/bin/onlp-snmpd ${ONL}/packages/base/any/onlp-snmpd/bin/onl-snmpwalk : /usr/bin/onl-snmpwalk + ${ONL}/packages/base/any/onlp-snmpd/bin/onl-snmp-mpstat : /usr/bin/onl-snmp-mpstat init: ${ONL}/packages/base/any/onlp-snmpd/onlp-snmpd.init diff --git a/packages/base/any/onlp-snmpd/bin/onl-snmp-mpstat b/packages/base/any/onlp-snmpd/bin/onl-snmp-mpstat new file mode 100755 index 00000000..e554a223 --- /dev/null +++ b/packages/base/any/onlp-snmpd/bin/onl-snmp-mpstat @@ -0,0 +1,33 @@ +#!/usr/bin/python + +# call mpstat and generate json output containing stats for all cpus + +""" +sample output from "mpstat 1 1": +# mpstat 1 1 +Linux 3.8.13-OpenNetworkLinux-e500mc-1.5 (as6700-3) 2016-12-15 _ppc_(4 CPU) + +04:59:31 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %idle +04:59:32 PM all 5.17 0.00 2.07 0.00 0.00 0.00 0.00 0.00 92.76 +Average: all 5.17 0.00 2.07 0.00 0.00 0.00 0.00 0.00 92.76 +""" + +import subprocess +import json + +stats = {} + +# 1 second interval, 1 count +out = subprocess.check_output(['mpstat','1','1']) + +for line in out.split('\n'): + if "%idle" in line: + # extract keys from header line, skipping over time and AM/PM + keys = line.split()[2:] + + if "Average" in line: + vals = line.split()[1:] + stats[vals[0]] = { k:int(round(100*float(v))) \ + for (k,v) in zip(keys[1:],vals[1:]) } + +print json.dumps(stats) diff --git a/packages/base/any/onlp-snmpd/builds/Makefile b/packages/base/any/onlp-snmpd/builds/Makefile index f423035f..6a5f08da 100644 --- a/packages/base/any/onlp-snmpd/builds/Makefile +++ b/packages/base/any/onlp-snmpd/builds/Makefile @@ -3,7 +3,7 @@ include $(ONL)/make/any.mk MODULE := onlp-snmpd include $(BUILDER)/standardinit.mk -DEPENDMODULES := onlp_snmp AIM OS snmp_subagent IOF onlplib +DEPENDMODULES := onlp_snmp AIM OS snmp_subagent IOF onlplib cjson cjson_util DEPENDMODULE_HEADERS := onlp include $(BUILDER)/dependmodules.mk diff --git a/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/auto/onlp_snmp.yml b/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/auto/onlp_snmp.yml index 6e62a16c..0feaa662 100644 --- a/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/auto/onlp_snmp.yml +++ b/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/auto/onlp_snmp.yml @@ -56,6 +56,9 @@ cdefs: &cdefs - ONLP_SNMP_CONFIG_AS_SUBAGENT: doc: "Configure as an snmp_subagent client." default: 0 +- ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS: + doc: "Resource object update period in seconds." + default: 5 definitions: cdefs: diff --git a/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/inc/onlp_snmp/onlp_snmp_config.h b/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/inc/onlp_snmp/onlp_snmp_config.h index 383142b2..ef7bf971 100644 --- a/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/inc/onlp_snmp/onlp_snmp_config.h +++ b/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/inc/onlp_snmp/onlp_snmp_config.h @@ -191,6 +191,16 @@ #define ONLP_SNMP_CONFIG_AS_SUBAGENT 0 #endif +/** + * ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS + * + * Resource object update period in seconds. */ + + +#ifndef ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS +#define ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS 5 +#endif + /** diff --git a/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/src/onlp_snmp_config.c b/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/src/onlp_snmp_config.c index 339c5f42..418b89f1 100644 --- a/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/src/onlp_snmp_config.c +++ b/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/src/onlp_snmp_config.c @@ -94,6 +94,11 @@ onlp_snmp_config_settings_t onlp_snmp_config_settings[] = { __onlp_snmp_config_STRINGIFY_NAME(ONLP_SNMP_CONFIG_AS_SUBAGENT), __onlp_snmp_config_STRINGIFY_VALUE(ONLP_SNMP_CONFIG_AS_SUBAGENT) }, #else { ONLP_SNMP_CONFIG_AS_SUBAGENT(__onlp_snmp_config_STRINGIFY_NAME), "__undefined__" }, +#endif +#ifdef ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS + { __onlp_snmp_config_STRINGIFY_NAME(ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS), __onlp_snmp_config_STRINGIFY_VALUE(ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS) }, +#else +{ ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS(__onlp_snmp_config_STRINGIFY_NAME), "__undefined__" }, #endif { NULL, NULL } }; diff --git a/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/src/onlp_snmp_platform.c b/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/src/onlp_snmp_platform.c index 1a6b3e55..88a5ef52 100644 --- a/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/src/onlp_snmp_platform.c +++ b/packages/base/any/onlp-snmpd/builds/src/onlp_snmp/module/src/onlp_snmp_platform.c @@ -25,6 +25,9 @@ #include #include "onlp_snmp_log.h" +#include +#include +#include #include #include #include @@ -54,7 +57,7 @@ platform_string_register(int index, const char* desc, char* value) netsnmp_register_watched_scalar( reg, winfo ); } -void +static void platform_int_register(int index, char* desc, int value) { oid tree[] = { 1, 3, 6, 1, 4, 1, 42623, 1, 1, 1, 1, 1}; @@ -69,6 +72,106 @@ platform_int_register(int index, char* desc, int value) v, NULL); } +static void +resource_int_register(int index, const char* desc, + Netsnmp_Node_Handler *handler) +{ + oid tree[] = { 1, 3, 6, 1, 4, 1, 42623, 1, 3, 1, 1 }; + tree[10] = index; + + netsnmp_handler_registration *reg = + netsnmp_create_handler_registration(desc, handler, + tree, OID_LENGTH(tree), + HANDLER_CAN_RONLY); + if (netsnmp_register_scalar(reg) != MIB_REGISTERED_OK) { + AIM_LOG_ERROR("registering handler for %s failed", desc); + } +} + + +/* resource objects */ +typedef struct { + uint32_t utilization_percent; + uint32_t idle_percent; +} resources_t; + +static resources_t resources; +static uint64_t resource_update_time; + +void resource_update(void) +{ + uint64_t now = aim_time_monotonic(); + if (now - resource_update_time > + (ONLP_SNMP_CONFIG_RESOURCE_UPDATE_SECONDS * 1000 * 1000)) { + resource_update_time = now; + AIM_LOG_INFO("update resource objects"); + + /* invoke mpstat collection script for json output */ + FILE *fp = popen("/usr/bin/onl-snmp-mpstat", "r"); + if (fp == NULL) { + AIM_LOG_ERROR("failed invoking onl-snmp-mpstat"); + return; + } + + /* parse json output */ + char line[1024]; + while (fgets(line, sizeof(line), fp) != NULL) { + cJSON *root = cJSON_Parse(line); + int result; + int rv = cjson_util_lookup_int(root, &result, "all.%%idle"); + if (rv == 0) { + /* save it */ + resources.idle_percent = result; + resources.utilization_percent = 100*100 - result; + } + cJSON_Delete(root); + } + } +} + +static int +utilization_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + if (MODE_GET == reqinfo->mode) { + resource_update(); + snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE, + (u_char *) &resources.utilization_percent, + sizeof(resources.utilization_percent)); + } else { + netsnmp_assert("bad mode in RO handler"); + } + + if (handler->next && handler->next->access_method) { + return netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); + } + + return SNMP_ERR_NOERROR; +} + +static int +idle_handler(netsnmp_mib_handler *handler, + netsnmp_handler_registration *reginfo, + netsnmp_agent_request_info *reqinfo, + netsnmp_request_info *requests) +{ + if (MODE_GET == reqinfo->mode) { + resource_update(); + snmp_set_var_typed_value(requests->requestvb, ASN_GAUGE, + (u_char *) &resources.idle_percent, + sizeof(resources.idle_percent)); + } else { + netsnmp_assert("bad mode in RO handler"); + } + + if (handler->next && handler->next->access_method) { + return netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); + } + + return SNMP_ERR_NOERROR; +} void onlp_snmp_platform_init(void) @@ -110,5 +213,8 @@ onlp_snmp_platform_init(void) REGISTER_STR(14, service_tag); REGISTER_STR(15, onie_version); } + + resource_int_register(1, "CpuAllPercentUtilization", utilization_handler); + resource_int_register(2, "CpuAllPercentIdle", idle_handler); }