linkerd2/web/app/js/components/util/Utils.js

222 lines
5.3 KiB
JavaScript

import _has from 'lodash/has';
import _isNil from 'lodash/isNil';
import _lowerCase from 'lodash/lowerCase';
import _startCase from 'lodash/startCase';
import { format as d3Format } from 'd3-format';
/*
* Display grid constants
*/
export const baseWidth = 8; // design base width of 8px
export const rowGutter = 3 * baseWidth;
/*
* Number formatters
*/
const successRateFormatter = d3Format(".2%");
const commaFormatter = d3Format(",");
const secondsFormatter = d3Format(",.3s");
export const formatWithComma = m => {
if (_isNil(m)) {
return "---";
} else {
return commaFormatter(m);
}
};
export const formatLatencyMs = m => {
if (_isNil(m)) {
return "---";
} else {
return `${formatLatencySec(m / 1000)}`;
}
};
const niceLatency = l => commaFormatter(Math.round(l));
export const formatLatencySec = latency => {
let s = parseFloat(latency);
if (_isNil(s)) {
return "---";
} else if (s === parseFloat(0.0)) {
return "0 s";
} else if (s < 0.001) {
return `${niceLatency(s * 1000 * 1000)} µs`;
} else if (s < 1.0) {
return `${niceLatency(s * 1000)} ms`;
} else {
return `${secondsFormatter(s)} s`;
}
};
export const metricToFormatter = {
"REQUEST_RATE": m => _isNil(m) ? "---" : styleNum(m, " RPS", true),
"SUCCESS_RATE": m => _isNil(m) ? "---" : successRateFormatter(m),
"LATENCY": formatLatencyMs,
"UNTRUNCATED": m => styleNum(m, "", false),
"NO_UNIT": m => _isNil(m) ? "---" : styleNum(m, "", true)
};
/*
* Add commas to a number (converting it to a string in the process)
*/
export function addCommas(nStr) {
nStr += '';
let x = nStr.split('.');
let x1 = x[0];
let x2 = x.length > 1 ? '.' + x[1] : '';
let rgx = /(\d+)(\d{3})/;
while (rgx.test(x1)) {
x1 = x1.replace(rgx, '$1' + ',' + '$2');
}
return x1 + x2;
}
/*
* Round a number to a given number of decimals
*/
export const roundNumber = (num, dec) => {
return Math.round(num * Math.pow(10,dec)) / Math.pow(10,dec);
};
/*
* Shorten and style number
*/
export const styleNum = (number, unit = "", truncate = true) => {
if (Number.isNaN(number)) {
return "N/A";
}
if (truncate && number > 999999999) {
number = roundNumber(number / 1000000000.0, 3);
return addCommas(number) + "G" + unit;
} else if (truncate && number > 999999) {
number = roundNumber(number / 1000000.0, 3);
return addCommas(number) + "M" + unit;
} else if (truncate && number > 999) {
number = roundNumber(number / 1000.0, 3);
return addCommas(number) + "k" + unit;
} else if (number > 999) {
number = roundNumber(number, 0);
return addCommas(number) + unit;
} else {
number = roundNumber(number, 2);
return addCommas(number) + unit;
}
};
/*
* Convert a string to a valid css class name
*/
export const toClassName = name => {
if (!name) { return ""; }
return _lowerCase(name).replace(/[^a-zA-Z0-9]/g, "_");
};
/*
Definition of sort, for numeric column sorting
*/
export const numericSort = (a, b) => (_isNil(a) ? -1 : a) - (_isNil(b) ? -1 : b);
/*
Nicely readable names for the stat resources
*/
export const friendlyTitle = singularOrPluralResource => {
let resource = singularResource(singularOrPluralResource);
let titleCase = _startCase(resource);
if (resource === "replicationcontroller") {
titleCase = _startCase("replication controller");
}
let titles = { singular: titleCase };
if (resource === "authority") {
titles.plural = "Authorities";
} else {
titles.plural = titles.singular + "s";
}
return titles;
};
/*
Get a singular resource name from a plural resource
*/
export const singularResource = resource => {
if (resource === "authorities") {
return "authority";
} else {return resource.replace(/s$/, "");}
};
/*
Get the resource type from the /pods response, whose json
is camelCased.
*/
const camelCaseLookUp = {
"replicaset": "replicaSet",
"replicationcontroller": "replicationController",
"statefulset": "statefulSet",
"daemonset": "daemonSet"
};
export const resourceTypeToCamelCase = resource => camelCaseLookUp[resource] || resource;
/*
A simplified version of ShortNameFromCanonicalResourceName
*/
export const shortNameLookup = {
"deployment": "deploy",
"daemonset": "ds",
"namespace": "ns",
"pod": "po",
"replicationcontroller": "rc",
"replicaset": "rs",
"service": "svc",
"statefulset": "sts",
"authority": "au"
};
export const podOwnerLookup = {
"deployment": "deploy",
"daemonset": "ds",
"replicationcontroller": "rc",
"replicaset": "rs",
"statefulset": "sts",
};
export const toShortResourceName = name => shortNameLookup[name] || name;
export const displayName = resource => `${toShortResourceName(resource.type)}/${resource.name}`;
export const isResource = name => {
let singularResourceName = singularResource(name);
return _has(shortNameLookup, singularResourceName);
};
/*
produce octets given an ip address
*/
const decodeIPToOctets = ip => {
ip = parseInt(ip, 10);
return [
ip >> 24 & 255,
ip >> 16 & 255,
ip >> 8 & 255,
ip & 255
];
};
/*
converts an address to an ipv4 formatted host:port pair
*/
export const publicAddressToString = (ipv4, port) => {
let octets = decodeIPToOctets(ipv4);
return octets.join(".") + ":" + port;
};
export const getSrClassification = sr => {
if (sr < 0.9) {
return "status-poor";
} else if (sr < 0.95) {
return "status-ok";
} else {return "status-good";}
};