以下以边缘计算节点服务为例,展示如何通过边缘函数搭建监控看板,实现对CPU使用率、网络丢包率及出入向带宽等信息的数据监控。
相关操作,请参见添加域名。
相关操作,请参见创建函数。
相关操作,请参见为函数关联域名。
变量
var grafanaAccount = "xxx"; var grafanaKey = "xxx"; var accessKeyId = "xxxx"; var secretKey = "xxxx==";
示例代码
var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __commonJS = (cb, mod) => function __require() { return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod )); // node_modules/dayjs/dayjs.min.js var require_dayjs_min = __commonJS({ "node_modules/dayjs/dayjs.min.js"(exports, module) { !function(t, e) { "object" == typeof exports && "undefined" != typeof module ? module.exports = e() : "function" == typeof define && define.amd ? define(e) : (t = "undefined" != typeof globalThis ? globalThis : t || self).dayjs = e(); }(exports, function() { "use strict"; var t = 1e3, e = 6e4, n = 36e5, r = "millisecond", i = "second", s = "minute", u = "hour", a = "day", o = "week", f = "month", h = "quarter", c = "year", d = "date", $ = "Invalid Date", l = /^(\d{4})[-/]?(\d{1,2})?[-/]?(\d{0,2})[Tt\s]*(\d{1,2})?:?(\d{1,2})?:?(\d{1,2})?[.:]?(\d+)?$/, y = /\[([^\]]+)]|Y{1,4}|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|a|A|m{1,2}|s{1,2}|Z{1,2}|SSS/g, M = { name: "en", weekdays: "Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"), months: "January_February_March_April_May_June_July_August_September_October_November_December".split("_") }, m = function(t2, e2, n2) { var r2 = String(t2); return !r2 || r2.length >= e2 ? t2 : "" + Array(e2 + 1 - r2.length).join(n2) + t2; }, g = { s: m, z: function(t2) { var e2 = -t2.utcOffset(), n2 = Math.abs(e2), r2 = Math.floor(n2 / 60), i2 = n2 % 60; return (e2 <= 0 ? "+" : "-") + m(r2, 2, "0") + ":" + m(i2, 2, "0"); }, m: function t2(e2, n2) { if (e2.date() < n2.date()) return -t2(n2, e2); var r2 = 12 * (n2.year() - e2.year()) + (n2.month() - e2.month()), i2 = e2.clone().add(r2, f), s2 = n2 - i2 < 0, u2 = e2.clone().add(r2 + (s2 ? -1 : 1), f); return +(-(r2 + (n2 - i2) / (s2 ? i2 - u2 : u2 - i2)) || 0); }, a: function(t2) { return t2 < 0 ? Math.ceil(t2) || 0 : Math.floor(t2); }, p: function(t2) { return { M: f, y: c, w: o, d: a, D: d, h: u, m: s, s: i, ms: r, Q: h }[t2] || String(t2 || "").toLowerCase().replace(/s$/, ""); }, u: function(t2) { return void 0 === t2; } }, v = "en", D = {}; D[v] = M; var p = function(t2) { return t2 instanceof _; }, S = function t2(e2, n2, r2) { var i2; if (!e2) return v; if ("string" == typeof e2) { var s2 = e2.toLowerCase(); D[s2] && (i2 = s2), n2 && (D[s2] = n2, i2 = s2); var u2 = e2.split("-"); if (!i2 && u2.length > 1) return t2(u2[0]); } else { var a2 = e2.name; D[a2] = e2, i2 = a2; } return !r2 && i2 && (v = i2), i2 || !r2 && v; }, w = function(t2, e2) { if (p(t2)) return t2.clone(); var n2 = "object" == typeof e2 ? e2 : {}; return n2.date = t2, n2.args = arguments, new _(n2); }, O = g; O.l = S, O.i = p, O.w = function(t2, e2) { return w(t2, { locale: e2.$L, utc: e2.$u, x: e2.$x, $offset: e2.$offset }); }; var _ = function() { function M2(t2) { this.$L = S(t2.locale, null, true), this.parse(t2); } var m2 = M2.prototype; return m2.parse = function(t2) { this.$d = function(t3) { var e2 = t3.date, n2 = t3.utc; if (null === e2) return new Date(NaN); if (O.u(e2)) return new Date(); if (e2 instanceof Date) return new Date(e2); if ("string" == typeof e2 && !/Z$/i.test(e2)) { var r2 = e2.match(l); if (r2) { var i2 = r2[2] - 1 || 0, s2 = (r2[7] || "0").substring(0, 3); return n2 ? new Date(Date.UTC(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2)) : new Date(r2[1], i2, r2[3] || 1, r2[4] || 0, r2[5] || 0, r2[6] || 0, s2); } } return new Date(e2); }(t2), this.$x = t2.x || {}, this.init(); }, m2.init = function() { var t2 = this.$d; this.$y = t2.getFullYear(), this.$M = t2.getMonth(), this.$D = t2.getDate(), this.$W = t2.getDay(), this.$H = t2.getHours(), this.$m = t2.getMinutes(), this.$s = t2.getSeconds(), this.$ms = t2.getMilliseconds(); }, m2.$utils = function() { return O; }, m2.isValid = function() { return !(this.$d.toString() === $); }, m2.isSame = function(t2, e2) { var n2 = w(t2); return this.startOf(e2) <= n2 && n2 <= this.endOf(e2); }, m2.isAfter = function(t2, e2) { return w(t2) < this.startOf(e2); }, m2.isBefore = function(t2, e2) { return this.endOf(e2) < w(t2); }, m2.$g = function(t2, e2, n2) { return O.u(t2) ? this[e2] : this.set(n2, t2); }, m2.unix = function() { return Math.floor(this.valueOf() / 1e3); }, m2.valueOf = function() { return this.$d.getTime(); }, m2.startOf = function(t2, e2) { var n2 = this, r2 = !!O.u(e2) || e2, h2 = O.p(t2), $2 = function(t3, e3) { var i2 = O.w(n2.$u ? Date.UTC(n2.$y, e3, t3) : new Date(n2.$y, e3, t3), n2); return r2 ? i2 : i2.endOf(a); }, l2 = function(t3, e3) { return O.w(n2.toDate()[t3].apply(n2.toDate("s"), (r2 ? [0, 0, 0, 0] : [23, 59, 59, 999]).slice(e3)), n2); }, y2 = this.$W, M3 = this.$M, m3 = this.$D, g2 = "set" + (this.$u ? "UTC" : ""); switch (h2) { case c: return r2 ? $2(1, 0) : $2(31, 11); case f: return r2 ? $2(1, M3) : $2(0, M3 + 1); case o: var v2 = this.$locale().weekStart || 0, D2 = (y2 < v2 ? y2 + 7 : y2) - v2; return $2(r2 ? m3 - D2 : m3 + (6 - D2), M3); case a: case d: return l2(g2 + "Hours", 0); case u: return l2(g2 + "Minutes", 1); case s: return l2(g2 + "Seconds", 2); case i: return l2(g2 + "Milliseconds", 3); default: return this.clone(); } }, m2.endOf = function(t2) { return this.startOf(t2, false); }, m2.$set = function(t2, e2) { var n2, o2 = O.p(t2), h2 = "set" + (this.$u ? "UTC" : ""), $2 = (n2 = {}, n2[a] = h2 + "Date", n2[d] = h2 + "Date", n2[f] = h2 + "Month", n2[c] = h2 + "FullYear", n2[u] = h2 + "Hours", n2[s] = h2 + "Minutes", n2[i] = h2 + "Seconds", n2[r] = h2 + "Milliseconds", n2)[o2], l2 = o2 === a ? this.$D + (e2 - this.$W) : e2; if (o2 === f || o2 === c) { var y2 = this.clone().set(d, 1); y2.$d[$2](l2), y2.init(), this.$d = y2.set(d, Math.min(this.$D, y2.daysInMonth())).$d; } else $2 && this.$d[$2](l2); return this.init(), this; }, m2.set = function(t2, e2) { return this.clone().$set(t2, e2); }, m2.get = function(t2) { return this[O.p(t2)](); }, m2.add = function(r2, h2) { var d2, $2 = this; r2 = Number(r2); var l2 = O.p(h2), y2 = function(t2) { var e2 = w($2); return O.w(e2.date(e2.date() + Math.round(t2 * r2)), $2); }; if (l2 === f) return this.set(f, this.$M + r2); if (l2 === c) return this.set(c, this.$y + r2); if (l2 === a) return y2(1); if (l2 === o) return y2(7); var M3 = (d2 = {}, d2[s] = e, d2[u] = n, d2[i] = t, d2)[l2] || 1, m3 = this.$d.getTime() + r2 * M3; return O.w(m3, this); }, m2.subtract = function(t2, e2) { return this.add(-1 * t2, e2); }, m2.format = function(t2) { var e2 = this, n2 = this.$locale(); if (!this.isValid()) return n2.invalidDate || $; var r2 = t2 || "YYYY-MM-DDTHH:mm:ssZ", i2 = O.z(this), s2 = this.$H, u2 = this.$m, a2 = this.$M, o2 = n2.weekdays, f2 = n2.months, h2 = function(t3, n3, i3, s3) { return t3 && (t3[n3] || t3(e2, r2)) || i3[n3].slice(0, s3); }, c2 = function(t3) { return O.s(s2 % 12 || 12, t3, "0"); }, d2 = n2.meridiem || function(t3, e3, n3) { var r3 = t3 < 12 ? "AM" : "PM"; return n3 ? r3.toLowerCase() : r3; }, l2 = { YY: String(this.$y).slice(-2), YYYY: this.$y, M: a2 + 1, MM: O.s(a2 + 1, 2, "0"), MMM: h2(n2.monthsShort, a2, f2, 3), MMMM: h2(f2, a2), D: this.$D, DD: O.s(this.$D, 2, "0"), d: String(this.$W), dd: h2(n2.weekdaysMin, this.$W, o2, 2), ddd: h2(n2.weekdaysShort, this.$W, o2, 3), dddd: o2[this.$W], H: String(s2), HH: O.s(s2, 2, "0"), h: c2(1), hh: c2(2), a: d2(s2, u2, true), A: d2(s2, u2, false), m: String(u2), mm: O.s(u2, 2, "0"), s: String(this.$s), ss: O.s(this.$s, 2, "0"), SSS: O.s(this.$ms, 3, "0"), Z: i2 }; return r2.replace(y, function(t3, e3) { return e3 || l2[t3] || i2.replace(":", ""); }); }, m2.utcOffset = function() { return 15 * -Math.round(this.$d.getTimezoneOffset() / 15); }, m2.diff = function(r2, d2, $2) { var l2, y2 = O.p(d2), M3 = w(r2), m3 = (M3.utcOffset() - this.utcOffset()) * e, g2 = this - M3, v2 = O.m(this, M3); return v2 = (l2 = {}, l2[c] = v2 / 12, l2[f] = v2, l2[h] = v2 / 3, l2[o] = (g2 - m3) / 6048e5, l2[a] = (g2 - m3) / 864e5, l2[u] = g2 / n, l2[s] = g2 / e, l2[i] = g2 / t, l2)[y2] || g2, $2 ? v2 : O.a(v2); }, m2.daysInMonth = function() { return this.endOf(f).$D; }, m2.$locale = function() { return D[this.$L]; }, m2.locale = function(t2, e2) { if (!t2) return this.$L; var n2 = this.clone(), r2 = S(t2, e2, true); return r2 && (n2.$L = r2), n2; }, m2.clone = function() { return O.w(this.$d, this); }, m2.toDate = function() { return new Date(this.valueOf()); }, m2.toJSON = function() { return this.isValid() ? this.toISOString() : null; }, m2.toISOString = function() { return this.$d.toISOString(); }, m2.toString = function() { return this.$d.toUTCString(); }, M2; }(), T = _.prototype; return w.prototype = T, [["$ms", r], ["$s", i], ["$m", s], ["$H", u], ["$W", a], ["$M", f], ["$y", c], ["$D", d]].forEach(function(t2) { T[t2[1]] = function(e2) { return this.$g(e2, t2[0], t2[1]); }; }), w.extend = function(t2, e2) { return t2.$i || (t2(e2, _, w), t2.$i = true), w; }, w.locale = S, w.isDayjs = p, w.unix = function(t2) { return w(1e3 * t2); }, w.en = D[v], w.Ls = D, w.p = {}, w; }); } }); // node_modules/auth/src/sign.ts var import_dayjs = __toESM(require_dayjs_min()); var util = { crypto: { hmac: async function hmac(rawKey, content) { var algorithm = { name: "HMAC", hash: "SHA-256" }; var key = await crypto.subtle.importKey( "raw", rawKey, algorithm, false, ["sign", "verify"] ); return await crypto.subtle.sign("HMAC", key, content); }, sha256: async function sha256(data) { return await crypto.subtle.digest("SHA-256", data); } } }; var unsignableHeaders = [ "authorization", "content-type", "content-length", "user-agent", "presigned-expires", "expect" ]; var constant = { algorithm: "HMAC-SHA256", v4Identifier: "request", dateHeader: "X-Date", tokenHeader: "x-security-token", contentSha256Header: "X-Content-Sha256", kDatePrefix: "" }; var uriEscape = (str) => { try { return encodeURIComponent(str).replace(/[^A-Za-z0-9_.~\-%]+/g, escape).replace( /[*]/g, (ch) => `%${ch.charCodeAt(0).toString(16).toUpperCase()}` ); } catch (e) { return ""; } }; var queryParamsToString = (params) => Object.keys(params).map((key) => { const val = params[key]; if (typeof val === "undefined" || val === null) { return; } const escapedKey = uriEscape(key); if (!escapedKey) { return; } if (Array.isArray(val)) { return `${escapedKey}=${val.map(uriEscape).sort().join(`&${escapedKey}=`)}`; } return `${escapedKey}=${uriEscape(val)}`; }).filter((v) => v).join("&"); var Signer = class { constructor(request, serviceName, options) { this.request = request; this.request.headers = request.headers || {}; this.serviceName = serviceName; options = options || {}; this.bodySha256 = options.bodySha256; this.request.params = this.sortParams(this.request.params); } sortParams(params) { const newParams = {}; if (params) { Object.keys(params).sort().map((key) => { newParams[key] = params[key]; }); } return newParams; } async addAuthorization(credentials, date) { const datetime = this.iso8601(date).replace(/[:\-]|\.\d{3}/g, ""); await this.addHeaders(credentials, datetime); this.request.headers["Authorization"] = await this.authorization(credentials, datetime); } async addHeaders(credentials, datetime) { this.request.headers[constant.dateHeader] = datetime; if (credentials.sessionToken) { this.request.headers[constant.tokenHeader] = credentials.sessionToken; } if (this.request.body) { let enc = new TextEncoder(); let body = this.request.body; if (typeof body !== "string") { if (body instanceof URLSearchParams) { body = enc.encode(body.toString()); } else if (body instanceof Blob) { body = await body.arrayBuffer(); } else if (body instanceof FormData) { throw new Error("unsupport FormData"); } else { body = enc.encode(JSON.stringify(body)); } } this.request.headers[constant.contentSha256Header] = this.bodySha256 || this.buf2hex(await util.crypto.sha256(body)); } } async authorization(credentials, datetime) { const parts = []; const credString = this.credentialString(datetime); parts.push(`${constant.algorithm} Credential=${credentials.accessKeyId}/${credString}`); parts.push(`SignedHeaders=${this.signedHeaders()}`); const signature = await this.signature(credentials, datetime); parts.push(`Signature=${this.buf2hex(signature)}`); return parts.join(", "); } async signature(credentials, datetime) { let enc = new TextEncoder(); const signingKey = await this.getSigningKey( credentials, datetime.substr(0, 8), this.request.region, this.serviceName ); return await util.crypto.hmac( signingKey, enc.encode(await this.stringToSign(datetime)) ); } async stringToSign(datetime) { const parts = []; parts.push(constant.algorithm); parts.push(datetime); parts.push(this.credentialString(datetime)); parts.push( (await this.hexEncodedHash(await this.canonicalString())).toString() ); return parts.join("\n"); } async canonicalString() { const parts = [], pathname = this.request.pathname || "/"; parts.push(this.request.method.toUpperCase()); parts.push(pathname); const queryString = queryParamsToString(this.request.params) || ""; parts.push(queryString); parts.push(`${this.canonicalHeaders()} `); parts.push(this.signedHeaders()); parts.push(await this.hexEncodedBodyHash()); return parts.join("\n"); } canonicalHeaders() { const headers = []; Object.keys(this.request.headers).forEach((key) => { headers.push([key, this.request.headers[key]]); }); headers.sort((a, b) => a[0].toLowerCase() < b[0].toLowerCase() ? -1 : 1); const parts = []; headers.forEach((item) => { const key = item[0].toLowerCase(); if (this.isSignableHeader(key)) { const value = item[1]; if (typeof value === "undefined" || value === null || typeof value.toString !== "function") { throw new Error(`Header ${key} contains invalid value`); } parts.push(`${key}:${this.canonicalHeaderValues(value.toString())}`); } }); return parts.join("\n"); } canonicalHeaderValues(values) { return values.replace(/\s+/g, " ").replace(/^\s+|\s+$/g, ""); } signedHeaders() { const keys = []; Object.keys(this.request.headers).forEach((key) => { key = key.toLowerCase(); if (this.isSignableHeader(key)) { keys.push(key); } }); return keys.sort().join(";"); } signedQueries() { return Object.keys(this.request.params).join(";"); } credentialString(datetime) { return this.createScope( datetime.substr(0, 8), this.request.region, this.serviceName ); } buf2hex(buffer) { return [...new Uint8Array(buffer)].map((x) => x.toString(16).padStart(2, "0")).join(""); } async hexEncodedHash(str) { let enc = new TextEncoder(); let arraybuffer = await util.crypto.sha256(enc.encode(str)); return this.buf2hex(arraybuffer); } async hexEncodedBodyHash() { if (this.request.headers[constant.contentSha256Header]) { return this.request.headers[constant.contentSha256Header]; } if (this.request.body) { return await this.hexEncodedHash(queryParamsToString(this.request.body)); } return await this.hexEncodedHash(""); } isSignableHeader(key) { return unsignableHeaders.indexOf(key) < 0; } iso8601(date) { if (date === void 0) { date = Date.now(); } let time = (0, import_dayjs.default)(date); return time.toISOString().replace(/\.\d{3}Z$/, "Z"); } async getSigningKey(credentials, date, region, service) { let enc = new TextEncoder(); const kDate = await util.crypto.hmac( enc.encode(`${constant.kDatePrefix}${credentials.secretKey}`), enc.encode(date) ); const kRegion = await util.crypto.hmac(kDate, enc.encode(region)); const kService = await util.crypto.hmac(kRegion, enc.encode(service)); const signingKey = await util.crypto.hmac(kService, enc.encode(constant.v4Identifier)); return signingKey; } createScope(date, region, serviceName) { return [date.substr(0, 8), region, serviceName, constant.v4Identifier].join( "/" ); } }; // index_adaptor.js var grafanaAccount = "xxxx"; var grafanaKey = "xxxx"; var accessKeyId = "xxxx"; var secretKey = "xxxx=="; function constructRequest(action) { return { region: "cn-north-1", method: "GET", params: { "Version": "2021-04-30", "Action": action }, pathname: "/", headers: {}, body: "" }; } async function getInstancesDetail(ids) { let requestObj = constructRequest("ListInstances"); requestObj.params["instance_identity"] = ids.join(","); let resp = await metrixQuery(requestObj); let json = await resp.json(); let result = {}; for (const inst of json["Result"]["instances"]) { result[inst["instance_identity"]] = inst; } return result; } async function onIspVariable(args) { let requestObj = constructRequest("ListInstances"); let resp = await metrixQuery(requestObj); let json = await resp.json(); let isps = []; for (const inst of json["Result"]["instances"]) { isps.push(inst["cluster"]["isp"]); } isps = [...new Set(isps)]; let result = []; for (const isp of isps) { result.push({ "text": isp, "value": isp }); } return result; } async function onClusterVariable(args) { let requestObj = constructRequest("ListInstances"); if (args["isps"]) { requestObj.params["isps"] = args["isps"].join(","); } let resp = await metrixQuery(requestObj); let json = await resp.json(); let clusters = []; for (const inst of json["Result"]["instances"]) { clusters.push(inst["cluster"]["cluster_name"]); } clusters = [...new Set(clusters)]; let renameCluster = function(name) { const prefix = "bdcdn"; if (name.startsWith(prefix)) { return "veen" + name.slice(prefix.length); } return name; }; let result = []; for (const cluster of clusters) { result.push({ "text": renameCluster(cluster), "value": cluster }); } return result; } async function onInstanceVariable(args) { let requestObj = constructRequest("ListInstances"); if (args["isps"]) { requestObj.params["isps"] = args["isps"].join(","); } if (args["clusters"]) { requestObj.params["clusters"] = args["clusters"].join(","); } let resp = await metrixQuery(requestObj); let json = await resp.json(); let result = []; for (const inst of json["Result"]["instances"]) { result.push({ "text": inst["instance_identity"], "value": inst["instance_identity"] }); } return result; } async function onNetQuery(requestObj, args) { const vars = args["scopedVars"]; if (vars === void 0) { throw Error("invalid format"); } let instances = []; if (vars["InstanceId"]) { for (const inst of vars["InstanceId"]["value"]) { instances.push(...inst.split(",")); } } let clusters = []; if (vars["RegionId"]) { clusters = vars["RegionId"]["value"]; } const mode = args["targets"][0]["data"]["mode"] || "top"; const type = args["targets"][0]["data"]["type"] || "rx"; const idx = type == "rx" ? 0 : 1; let rateConverter = function(unit, value) { let result = value; if (unit > 2) { result = value * ((unit - 2) * 1e3); } if (unit < 2) { result = value / ((2 - unit) * 1e3); } return result; }; let aggHandle = async function(_) { requestObj.params["instances"] = instances.join(","); if (vars["ISP"]) { requestObj.params["isps"] = vars["ISP"]["value"].join(","); } if (vars["RegionId"]) { requestObj.params["clusters"] = vars["RegionId"]["value"].join(","); } let resp = await metrixQuery(requestObj); let j = await resp.json(); let result = []; if (type == "all") { result.push({ "target": "rx", "datapoints": j["Result"][0]["data"].map( (x) => [rateConverter(j["Result"][0]["unit"], x.value), x.timestamp] ) }); result.push({ "target": "tx", "datapoints": j["Result"][1]["data"].map( (x) => [rateConverter(j["Result"][1]["unit"], x.value), x.timestamp] ) }); } else if (type == "rx") { result.push({ "target": "rx", "datapoints": j["Result"][0]["data"].map( (x) => [rateConverter(j["Result"][0]["unit"], x.value), x.timestamp] ) }); } else if (type == "tx") { result.push({ "target": "tx", "datapoints": j["Result"][1]["data"].map( (x) => [rateConverter(j["Result"][1]["unit"], x.value), x.timestamp] ) }); } return result; }; let topHandle = async function(topk) { requestObj.params["Action"] = "QueryCloudInstanceMetricNonAggregate"; let instancesDetail = await getInstancesDetail(instances); let getInstanceDetailName = function(id) { let result2 = instancesDetail[id]["cluster"]["alias"]; result2 += " id: " + id; return result2; }; let closures = []; const groupInst = sliceArray(instances, 20); for (const insts of groupInst) { requestObj.params["instances"] = insts.join(","); let obj = JSON.parse(JSON.stringify(requestObj)); closures.push(metrixQuery.bind(null, obj)); } let resps = await batch(closures, 10, 3, (resp) => { return resp.ok; }); let result = []; for (let i = 0; i < resps.length; ++i) { let j = await resps[i].json(); if (j["Result"] === void 0 || j["Result"].length < 2) { continue; } for (let n = 0; n < j["Result"].length - 1; ) { if (j["Result"][n]["metrics"] === void 0) { ++n; continue; } if (j["Result"][n]["metrics"]["name"] === void 0 || j["Result"][n + 1]["metrics"]["name"] === void 0 || j["Result"][n]["metrics"]["name"] != j["Result"][n + 1]["metrics"]["name"]) { ++n; continue; } result.push({ "target": getInstanceDetailName(j["Result"][n + idx]["metrics"]["name"]), "max": j["Result"][n + idx]["max_value"], "datapoints": j["Result"][n + idx]["data"].map( (x) => [ rateConverter(j["Result"][n + idx]["unit"], x.value), x.timestamp ] ) }); n += 2; } } result.sort(function(lhs, rhs) { return lhs.max - rhs.max; }); return result.slice(-topk); }; let nodeTopHandle = async function(topk) { let clusterMapAlias = {}; const instancesDetail = await getInstancesDetail(instances); for (const item of Object.values(instancesDetail)) { clusterMapAlias[item["cluster"]["cluster_name"]] = item["cluster"]["alias"]; } let getClusterDetailName = function(clusterId) { let result2 = clusterMapAlias[clusterId]; result2 += " id: " + clusterId; return result2; }; let closures = []; for (const cluster of clusters) { requestObj.params["clusters"] = cluster; let obj = JSON.parse(JSON.stringify(requestObj)); closures.push(metrixQuery.bind(null, obj)); } let resps = await batch(closures, 10, 3, (resp) => { return resp.ok; }); let result = []; for (let i = 0; i < resps.length; ++i) { let j = await resps[i].json(); if (j["Result"] === void 0 || j["Result"].length != 2) { continue; } result.push({ "target": getClusterDetailName(clusters[i]), "max": j["Result"][idx]["max_value"], "datapoints": j["Result"][idx]["data"].map( (x) => [ rateConverter(j["Result"][idx]["unit"], x.value), x.timestamp ] ) }); } result.sort(function(lhs, rhs) { return lhs.max - rhs.max; }); return result.slice(-topk); }; if (mode == "agg") { return aggHandle(); } else if (mode == "top") { let topk = parseInt(vars["TopN"]["value"]); if (isNaN(topk)) { topk = 30; } return topHandle(topk); } else if (mode == "node-top") { let topk = parseInt(vars["TopN"]["value"]); if (isNaN(topk)) { topk = 30; } return nodeTopHandle(topk); } throw Error("unsupported network query"); } async function batch(closures, batchSize, retryTimes, validate) { let results = []; let count = 0; const rounds = Math.ceil(closures.length / batchSize); for (let i = 0; i < rounds; ++i) { while (retryTimes >= 0) { let arr = []; try { for (let j = 0; j < batchSize && count < closures.length; ++j, ++count) { arr.push(closures[count]()); } let success = []; let res = await Promise.all(arr); for (const entry of res) { if (validate(entry)) { success.push(entry); } } if (success.length == arr.length) { results.push(...success); break; } } catch (e) { } --retryTimes; count -= arr.length; } if (retryTimes < 0) { throw new Error("batch failed"); } } return results; } function sliceArray(arr, size) { var result = []; for (var i = 0; i < arr.length; i += size) { result.push(arr.slice(i, i + size)); } return result; } async function onDropQuery(requestObj, args) { const vars = args["scopedVars"]; if (vars === void 0) { throw Error("invalid format"); } let instances = []; if (vars["InstanceId"]) { for (const inst of vars["InstanceId"]["value"]) { instances.push(...inst.split(",")); } } const mode = args["targets"][0]["data"]["mode"] || "top"; let aggHandle = async function() { requestObj.params["instances"] = instances.join(","); if (vars["ISP"]) { requestObj.params["isps"] = vars["ISP"]["value"].join(","); } if (vars["RegionId"]) { requestObj.params["clusters"] = vars["RegionId"]["value"].join(","); } let resp = await metrixQuery(requestObj); let j = await resp.json(); let result = []; result.push({ "target": "drop", "datapoints": j["Result"][0]["data"].map((x) => [x.value, x.timestamp]) }); return result; }; let topHandle = async function(topk) { requestObj.params["Action"] = "QueryCloudInstanceMetricNonAggregate"; let instancesDetail = await getInstancesDetail(instances); let getInstanceDetailName = function(id) { let result2 = instancesDetail[id]["cluster"]["alias"]; result2 += " id: " + id; return result2; }; let closures = []; const groupInst = sliceArray(instances, 50); for (const insts of groupInst) { requestObj.params["instances"] = insts.join(","); let obj = JSON.parse(JSON.stringify(requestObj)); closures.push(metrixQuery.bind(null, obj)); } let resps = await batch(closures, 10, 3, (resp) => { return resp.ok; }); let result = []; for (let i = 0; i < resps.length; ++i) { let j = await resps[i].json(); if (j["Result"] === void 0) { continue; } for (let n = 0; n < j["Result"].length; ++n) { if (j["Result"][n]["metrics"] === void 0 || j["Result"][n]["metrics"]["name"] === void 0) { continue; } result.push({ "target": getInstanceDetailName(j["Result"][n]["metrics"]["name"]), "max": j["Result"][n]["max_value"], "datapoints": j["Result"][n]["data"].map((x) => [x.value, x.timestamp]) }); } } result.sort(function(lhs, rhs) { return lhs.max - rhs.max; }); return result.slice(-topk); }; if (mode == "agg") { return aggHandle(); } else if (mode == "top") { let topk = parseInt(vars["TopN"]["value"]); if (isNaN(topk)) { topk = 30; } return topHandle(topk); } throw Error("unsupported drop query"); } async function onQuery(request) { let requestObj = constructRequest("QueryCloudInstanceMetric"); const args = await request.json(); const range = args["range"]; if (range["from"] === void 0 || range["to"] === void 0) { throw Error("invalid format"); } requestObj.params["end_time"] = Math.round(new Date(range["to"]).valueOf() / 1e3); requestObj.params["start_time"] = Math.round(new Date(range["from"]).valueOf() / 1e3); if (args["targets"] === void 0) { throw Error("invalid format"); } const target = args["targets"][0]; requestObj.params["type"] = target["target"]; if (target["target"] == "network") { return onNetQuery(requestObj, args); } else if (target["target"] == "drop") { return onDropQuery(requestObj, args); } else if (target["target"] == "cpu") { } throw Error("unsupported query"); } var metricList = ["network", "cpu", "drop"]; async function onSearch(request) { const args = await request.json(); if (args["type"] == "timeseries") { return metricList; } const target = args["target"]; try { const json = JSON.parse(target); if (json["type"] == "isp") { return onIspVariable(json); } else if (json["type"] == "cluster") { return onClusterVariable(json); } else if (json["type"] == "instance") { return onInstanceVariable(json); } } catch (e) { throw Error("invalid format"); } throw Error("unsupported search type"); } async function handle(event) { if (!AuthRequest(event.request.headers)) { return new Response("Unauthorized", { status: 401, statusText: "Unauthorized" }); } try { let result = {}; let path = event.request.path; if (path.endsWith("/search")) { result = await onSearch(event.request); } else if (path.endsWith("/query")) { result = await onQuery(event.request); } else { return new Response("OK"); } return createResponse(result); } catch (e) { console.error(e.stack); return new Response(e.stack, { status: 500 }); } } function AuthRequest(headers) { let auth = headers.get("Authorization") || headers.get("authorization"); if (auth == null) { return false; } return auth === `Basic ${btoa(grafanaAccount + ":" + grafanaKey)}`; } async function metrixQuery(requestObj) { let signer = new Signer(requestObj, "veenedge"); let credentials = { accessKeyId, secretKey }; await signer.addAuthorization(credentials, Date.now()); let url = `http://veenedge.volcengineapi.com/?${queryParamsToString(requestObj.params)}`; let req = new Request(url, { method: requestObj.method, headers: requestObj.headers }); return fetch(req); } function createResponse(json) { let headers = new Headers(); headers.append("Content-Type", "application/json"); return new Response(JSON.stringify(json), { status: 200, statusText: "OK", headers }); } addEventListener("fetch", (event) => { event.respondWith(handle(event)); });