import { duration } from "moment"; import humanizeDuration from "humanize-duration"; import { Metric, MetricValue } from "azure-arm-monitor/lib/models"; import { Rule } from "../rule"; import { Context } from "../context"; import { isVM } from "../utils"; import { Match, matchResource } from "../common/match"; import { Options } from "../options"; import { Client } from "../client"; export default class VmCpuMetrics extends Rule { private readonly min: number; private readonly max: number; private readonly metric: string; private readonly timespan: string; private readonly interval: string; private readonly aggregation: string; private readonly match: Match; constructor(context: Context, options: Options) { super(context, options); this.min = options.number("min", 2); this.max = options.number("max", 85); this.metric = options.string("metric", "Percentage CPU"); this.timespan = options.string("timespan", "P7D"); this.interval = options.string("interval", "24:00:00"); this.aggregation = options.string("aggregation", "Average"); this.match = matchResource(options); } async run(client: Client): Promise { const resources = await client.listResources(); for (const resource of resources) { if (isVM(resource) && this.match(resource)) { const metrics = await client.getMetrics( resource, this.timespan, this.interval, this.aggregation ); const value = getValue(metrics, this.metric, this.aggregation); if (value != null) { const data: any = { metric: this.metric, timespan: humanizeDuration( duration(this.timespan).asMilliseconds() ), interval: this.interval, aggregation: this.aggregation, value }; if (this.min !== -1 && value < this.min) { data.min = this.min; this.report(resource, "vmUsageLow", data); } if (this.max !== -1 && value > this.max) { data.max = this.max; this.report(resource, "vmUsageHigh", data); } } } } } } function getValue(metrics: Metric[], name: string, aggregation: string) { if (aggregation === "Minimum") { return minValue(metrics, name); } else if (aggregation === "Maximum") { return maxValue(metrics, name); } else if (aggregation === "Average") { return averageValue(metrics, name); } else { throw new Error(`Invalid aggregation: ${aggregation}`); } } function minValue(metrics: Metric[], name: string) { let cur: number | null = null; forEach(metrics, name, data => { for (const d of data) { if (d.minimum != null && (cur == null || d.minimum < cur)) { cur = d.minimum; } } }); return cur; } function maxValue(metrics: Metric[], name: string) { let cur: number | null = null; forEach(metrics, name, data => { for (const d of data) { if (d.maximum != null && (cur == null || d.maximum > cur)) { cur = d.maximum; } } }); return cur; } function averageValue(metrics: Metric[], name: string) { let count = 0; let sum = 0; forEach(metrics, name, data => { count += data.length; sum += data.reduce((s, cur) => s + (cur.average || 0), 0); }); return count > 0 ? sum / count : null; } function forEach( metrics: Metric[], name: string, callback: (arr: MetricValue[]) => void ) { for (const metric of metrics) { if (metric.name && metric.name.value === name) { for (const timeseries of metric.timeseries) { if (timeseries.data) { callback(timeseries.data); } } } } }