-
- METRICS
+ className="metric-selector">
+
+ {items}
- {items}
);
}
diff --git a/client/app/scripts/constants/styles.js b/client/app/scripts/constants/styles.js
index 44d3afc85..f626c6733 100644
--- a/client/app/scripts/constants/styles.js
+++ b/client/app/scripts/constants/styles.js
@@ -8,3 +8,5 @@ export const DETAILS_PANEL_MARGINS = {
};
export const DETAILS_PANEL_OFFSET = 8;
+
+export const CANVAS_METRIC_FONT_SIZE = 0.19;
diff --git a/client/app/scripts/stores/app-store.js b/client/app/scripts/stores/app-store.js
index 4eea62d0d..33f427a50 100644
--- a/client/app/scripts/stores/app-store.js
+++ b/client/app/scripts/stores/app-store.js
@@ -8,7 +8,6 @@ import ActionTypes from '../constants/action-types';
import { EDGE_ID_SEPARATOR } from '../constants/naming';
import { findTopologyById, setTopologyUrlsById, updateTopologyIds,
filterHiddenTopologies } from '../utils/topology-utils';
-import { METRIC_LABELS } from '../utils/data-utils';
const makeList = List;
const makeMap = Map;
@@ -611,15 +610,17 @@ export class AppStore extends Store {
availableCanvasMetrics = nodes
.valueSeq()
- .flatMap(n => (n.get('metrics') || makeMap()).keys())
+ .flatMap(n => (n.get('metrics') || makeList()).map(m => (
+ makeMap({id: m.get('id'), label: m.get('label')})
+ )))
.toSet()
- .sortBy(n => METRIC_LABELS[n])
- .toJS()
- .map(v => ({id: v, label: METRIC_LABELS[v]}));
+ .sortBy(m => m.get('label'))
+ .toJS();
const similarTypeMetric = availableCanvasMetrics.find(m => m.label === lockedMetricType);
lockedMetric = similarTypeMetric && similarTypeMetric.id;
- if (!availableCanvasMetrics.map(m => m.id).includes(selectedMetric)) {
+ // if something in the current topo is not already selected, select it.
+ if (availableCanvasMetrics.map(m => m.id).indexOf(selectedMetric) === -1) {
selectedMetric = lockedMetric;
}
diff --git a/client/app/scripts/utils/data-utils.js b/client/app/scripts/utils/data-generator-utils.js
similarity index 59%
rename from client/app/scripts/utils/data-utils.js
rename to client/app/scripts/utils/data-generator-utils.js
index 7f45c7382..e79717eeb 100644
--- a/client/app/scripts/utils/data-utils.js
+++ b/client/app/scripts/utils/data-generator-utils.js
@@ -1,8 +1,5 @@
import _ from 'lodash';
import d3 from 'd3';
-import { formatMetric } from './string-utils';
-import { colors } from './color-utils';
-import AppStore from '../stores/app-store';
// Inspired by Lee Byron's test data generator.
@@ -61,20 +58,6 @@ export function label(m) {
}
-const METRIC_FORMATS = {
- docker_cpu_total_usage: 'percent',
- docker_memory_usage: 'filesize',
- host_cpu_usage_percent: 'percent',
- host_mem_usage_bytes: 'filesize',
- load1: 'number',
- load15: 'number',
- load5: 'number',
- open_files_count: 'integer',
- process_cpu_usage_percent: 'percent',
- process_memory_usage_bytes: 'filesize'
-};
-
-
const memoryMetric = (node, name, max = 1024 * 1024 * 1024) => ({
samples: [{value: getNextValue([node.id, name], max)}],
max
@@ -151,57 +134,3 @@ export function addMetrics(delta, prevNodes) {
update: handleUpdated(delta.update, prevNodes)
});
}
-
-const openFilesScale = d3.scale.log().domain([1, 100000]).range([0, 1]);
-//
-// loadScale(1) == 0.5; E.g. a nicely balanced system :).
-const loadScale = d3.scale.log().domain([0.01, 100]).range([0, 1]);
-
-export function getMetricValue(metric, size) {
- if (!metric) {
- return {height: 0, value: null, formattedValue: 'n/a'};
- }
-
- const max = metric.getIn(['max']);
- const value = metric.getIn(['samples', 0, 'value']);
- const selectedMetric = AppStore.getSelectedMetric();
-
- let valuePercentage = value === 0 ? 0 : value / max;
- if (selectedMetric === 'open_files_count') {
- valuePercentage = openFilesScale(value);
- } else if (_.includes(['load1', 'load5', 'load15'], selectedMetric)) {
- valuePercentage = loadScale(value);
- }
-
- let displayedValue = Number(value).toFixed(1);
- if (displayedValue > 0) {
- const baseline = 0.1;
- displayedValue = valuePercentage * (1 - baseline) + baseline;
- }
- const height = size * displayedValue;
- const metricWithFormat = Object.assign(
- {}, {format: METRIC_FORMATS[selectedMetric]}, metric.toJS());
-
- return {
- height,
- value,
- formattedValue: formatMetric(value, metricWithFormat, true)
- };
-}
-
-export function getMetricColor() {
- const selectedMetric = AppStore.getSelectedMetric();
- // bluey
- if (/memory/.test(selectedMetric)) {
- return '#1f77b4';
- } else if (/cpu/.test(selectedMetric)) {
- return colors('cpu');
- } else if (/files/.test(selectedMetric)) {
- // return colors('files');
- // purple
- return '#9467bd';
- } else if (/load/.test(selectedMetric)) {
- return colors('load');
- }
- return 'steelBlue';
-}
diff --git a/client/app/scripts/utils/math-utils.js b/client/app/scripts/utils/math-utils.js
index 7389776d4..31911ade8 100644
--- a/client/app/scripts/utils/math-utils.js
+++ b/client/app/scripts/utils/math-utils.js
@@ -1,5 +1,21 @@
// http://stackoverflow.com/questions/4467539/javascript-modulo-not-behaving
+//
+// A modulo that "behaves" w/ negatives.
+//
+// modulo(5, 5) => 0
+// modulo(4, 5) => 4
+// modulo(3, 5) => 3
+// modulo(2, 5) => 2
+// modulo(1, 5) => 1
+// modulo(0, 5) => 0
+// modulo(-1, 5) => 4
+// modulo(-2, 5) => 3
+// modulo(-2, 5) => 3
+// modulo(-3, 5) => 2
+// modulo(-4, 5) => 1
+// modulo(-5, 5) => 0
+//
export function modulo(i, n) {
return ((i % n) + n) % n;
}
diff --git a/client/app/scripts/utils/metric-utils.js b/client/app/scripts/utils/metric-utils.js
new file mode 100644
index 000000000..148c2e022
--- /dev/null
+++ b/client/app/scripts/utils/metric-utils.js
@@ -0,0 +1,56 @@
+import _ from 'lodash';
+import d3 from 'd3';
+import { formatMetric } from './string-utils';
+import { colors } from './color-utils';
+
+
+const openFilesScale = d3.scale.log().domain([1, 100000]).range([0, 1]);
+//
+// loadScale(1) == 0.5; E.g. a nicely balanced system :).
+const loadScale = d3.scale.log().domain([0.01, 100]).range([0, 1]);
+
+export function getMetricValue(metric, size) {
+ if (!metric) {
+ return {height: 0, value: null, formattedValue: 'n/a'};
+ }
+ const m = metric.toJS();
+ const value = m.value;
+
+ let valuePercentage = value === 0 ? 0 : value / m.max;
+ if (m.id === 'open_files_count') {
+ valuePercentage = openFilesScale(value);
+ } else if (_.includes(['load1', 'load5', 'load15'], m.id)) {
+ valuePercentage = loadScale(value);
+ }
+
+ let displayedValue = Number(value).toFixed(1);
+ if (displayedValue > 0) {
+ const baseline = 0.1;
+ displayedValue = valuePercentage * (1 - baseline) + baseline;
+ }
+ const height = size * displayedValue;
+
+ return {
+ height,
+ value,
+ formattedValue: formatMetric(value, m, true)
+ };
+}
+
+
+export function getMetricColor(metric) {
+ const selectedMetric = metric && metric.get('id');
+ // bluey
+ if (/memory/.test(selectedMetric)) {
+ return '#1f77b4';
+ } else if (/cpu/.test(selectedMetric)) {
+ return colors('cpu');
+ } else if (/files/.test(selectedMetric)) {
+ // return colors('files');
+ // purple
+ return '#9467bd';
+ } else if (/load/.test(selectedMetric)) {
+ return colors('load');
+ }
+ return 'steelBlue';
+}
diff --git a/client/app/styles/main.less b/client/app/styles/main.less
index be28b7e8a..14b79d5b2 100644
--- a/client/app/styles/main.less
+++ b/client/app/styles/main.less
@@ -401,6 +401,7 @@ h2 {
.stack .shape .metric-fill {
display: none;
}
+
.stack .onlyMetrics .shape .metric-fill {
display: inline-block;
}
@@ -422,6 +423,7 @@ h2 {
.metric-fill {
stroke: none;
fill: #A0BE7E;
+ fill-opacity: 0.7;
}
.shadow {
@@ -1000,50 +1002,55 @@ h2 {
}
}
-.topology-options {
+.topology-option, .metric-selector {
+ color: @text-secondary-color;
+ margin: 6px 0;
- .topology-option {
- color: @text-secondary-color;
- margin: 6px 0;
-
- &:last-child {
- margin-bottom: 0;
- }
-
- &-wrapper {
- border-radius: @border-radius;
- border: 1px solid @background-darker-color;
- display: inline-block;
- }
-
- &-action {
- .btn-opacity;
- padding: 3px 12px;
- cursor: pointer;
- display: inline-block;
-
- &-selected, &:hover {
- color: @text-darker-color;
- background-color: @background-darker-color;
- }
-
- &-selected {
- cursor: default;
- }
-
- &:first-child {
- border-left: none;
- border-top-left-radius: @border-radius;
- border-bottom-left-radius: @border-radius;
- }
-
- &:last-child {
- border-top-right-radius: @border-radius;
- border-bottom-right-radius: @border-radius;
- }
- }
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ .fa {
+ margin-left: 4px;
+ color: darkred;
}
+ &-wrapper {
+ border-radius: @border-radius;
+ border: 1px solid @background-darker-color;
+ display: inline-block;
+ }
+
+ &-action {
+ .btn-opacity;
+ padding: 3px 12px;
+ cursor: pointer;
+ display: inline-block;
+
+ &-selected, &:hover {
+ color: @text-darker-color;
+ background-color: @background-darker-color;
+ }
+
+ &:first-child {
+ border-left: none;
+ border-top-left-radius: @border-radius;
+ border-bottom-left-radius: @border-radius;
+ }
+
+ &:last-child {
+ border-top-right-radius: @border-radius;
+ border-bottom-right-radius: @border-radius;
+ }
+ }
+}
+
+.topology-option {
+ &-action {
+ &-selected {
+ cursor: default;
+ }
+ }
}
.sidebar {