ASR_BASE
Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/external/subpack/utils/prometheus-node-exporter-ucode/files/metrics.uc b/external/subpack/utils/prometheus-node-exporter-ucode/files/metrics.uc
new file mode 100644
index 0000000..3dce77a
--- /dev/null
+++ b/external/subpack/utils/prometheus-node-exporter-ucode/files/metrics.uc
@@ -0,0 +1,227 @@
+{%
+'use strict';
+
+import * as fs from "fs";
+import { connect } from "ubus";
+import { cursor } from "uci";
+
+function debug(...s) {
+ if (global.debug)
+ warn("DEBUG: ", ...s, "\n");
+}
+
+function puts(...s) {
+ return uhttpd.send(...s, "\n");
+}
+
+function govalue(value) {
+ if (value == Infinity)
+ return "+Inf";
+ else if (value == -Infinity)
+ return "-Inf";
+ else if (value != value)
+ return "NaN";
+ else if (type(value) in [ "int", "double" ])
+ return value;
+ else if (type(value) in [ "bool", "string" ])
+ return +value;
+
+ return null;
+}
+
+function metric(name, mtype, help, skipdecl) {
+ let func;
+ let decl = skipdecl == true ? false : true;
+
+ let yield = function(labels, value) {
+ let v = govalue(value);
+
+ if (v == null) {
+ debug(`skipping metric: unsupported value '${value}' (${name})`);
+ return func;
+ }
+
+ let labels_str = "";
+ if (length(labels)) {
+ let sep = "";
+ let s;
+ labels_str = "{";
+ for (let l in labels) {
+ if (labels[l] == null)
+ s = "";
+ else if (type(labels[l]) == "string") {
+ s = labels[l];
+ s = replace(labels[l], "\\", "\\\\");
+ s = replace(s, "\"", "\\\"");
+ s = replace(s, "\n", "\\n");
+ } else {
+ s = govalue(labels[l]);
+
+ if (!s)
+ continue;
+ }
+
+ labels_str += sep + l + "=\"" + s + "\"";
+ sep = ",";
+ }
+ labels_str += "}";
+ }
+
+ if (decl) {
+ if (help)
+ puts("# HELP ", name, " ", help);
+ puts("# TYPE ", name, " ", mtype);
+ decl = false;
+ }
+
+ puts(name, labels_str, " ", v);
+ return func;
+ };
+
+ func = yield;
+ return func;
+}
+
+function counter(name, help, skipdecl) {
+ return metric(name, "counter", help, skipdecl);
+}
+
+function gauge(name, help, skipdecl) {
+ return metric(name, "gauge", help, skipdecl);
+}
+
+function httpstatus(status) {
+ puts("Status: ", status, "\nContent-Type: text/plain; version=0.0.4; charset=utf-8\n");
+}
+
+function clockdiff(t1, t2) {
+ return (t2[0] - t1[0]) * 1000000000 + t2[1] - t1[1];
+}
+
+let collectors = {};
+
+global.handle_request = function(env) {
+ let scope = {
+ config: null,
+ fs,
+ ubus: connect(),
+ counter,
+ gauge,
+ wsplit: function(line) {
+ return split(line, /\s+/);
+ },
+ nextline: function(f) {
+ return rtrim(f.read("line"), "\n");
+ },
+ oneline: function(fn) {
+ let f = fs.open(fn);
+
+ if (!f)
+ return null;
+
+ return nextline(f);
+ },
+ poneline: function(cmd) {
+ let f = fs.popen(cmd);
+
+ if (!f)
+ return null;
+
+ return nextline(f);
+ },
+ };
+
+ if (length(collectors) < 1) {
+ httpstatus("404 No Collectors found");
+ return;
+ }
+
+ let cols = [];
+ for (let q in split(env.QUERY_STRING, "&")) {
+ let s = split(q, "=", 2);
+ if (length(s) == 2 && s[0] == "collect") {
+ if (!(s[1] in collectors)) {
+ httpstatus(`404 Collector ${s[1]} not found`);
+ return;
+ }
+
+ push(cols, s[1]);
+ }
+ }
+
+ if (length(cols) > 0)
+ cols = uniq(cols);
+ else
+ cols = keys(collectors);
+
+ httpstatus("200 OK");
+
+ let duration = gauge("node_scrape_collector_duration_seconds");
+ let success = gauge("node_scrape_collector_success");
+
+ for (let col in cols) {
+ let ok = false;
+ let t1, t2;
+
+ scope["config"] = collectors[col].config;
+ t1 = clock(true);
+ try {
+ ok = call(collectors[col].func, null, scope) != false;
+ } catch(e) {
+ warn(`error running collector '${col}':\n${e.message}\n`);
+ }
+ t2 = clock(true);
+
+ duration({ collector: col }, clockdiff(t1, t2) / 1000000000.0);
+ success({ collector: col }, ok);
+ }
+};
+
+const lib = "/usr/share/ucode/node-exporter/lib";
+const opts = {
+ strict_declarations: true,
+ raw_mode: true,
+};
+
+let cols = fs.lsdir(lib, "*.uc");
+for (let col in cols) {
+ let func;
+ let uci = cursor();
+
+ try {
+ func = loadfile(lib + "/" + col, opts);
+ } catch(e) {
+ warn(`error compiling collector '${col}':\n${e.message}\n`);
+ continue;
+ }
+
+ let name = substr(col, 0, -3);
+ let config = uci.get_all("prometheus-node-exporter-ucode", name);
+ if (!config || config[".type"] != "collector")
+ config = {};
+ else {
+ delete config[".anonymous"];
+ delete config[".type"];
+ delete config[".name"];
+ }
+
+ collectors[name] = {
+ func,
+ config,
+ };
+}
+
+warn(`prometheus-node-exporter-ucode now serving requests with ${length(collectors)} collectors\n`);
+
+if (!("uhttpd" in global)) {
+ global.debug = true;
+
+ puts = function(...s) {
+ return print(...s, "\n");
+ };
+
+ handle_request({
+ QUERY_STRING: join("&", map(ARGV, v => "collect=" + v)),
+ });
+}
+%}