340 lines
10 KiB
Go
340 lines
10 KiB
Go
// Copyright 2014 Google Inc. All Rights Reserved.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package static
|
|
|
|
const containersJs = `
|
|
google.load("visualization", "1", {packages: ["corechart", "gauge"]});
|
|
|
|
// Draw a line chart.
|
|
function drawLineChart(seriesTitles, data, elementId, unit) {
|
|
// Convert the first column to a Date.
|
|
for (var i = 0; i < data.length; i++) {
|
|
if (data[i] != null) {
|
|
data[i][0] = new Date(data[i][0]);
|
|
}
|
|
}
|
|
|
|
// Add the definition of each column and the necessary data.
|
|
var dataTable = new google.visualization.DataTable();
|
|
dataTable.addColumn('datetime', seriesTitles[0]);
|
|
for (var i = 1; i < seriesTitles.length; i++) {
|
|
dataTable.addColumn('number', seriesTitles[i]);
|
|
}
|
|
dataTable.addRows(data);
|
|
|
|
// Create and draw the visualization.
|
|
var ac = null;
|
|
var opts = null;
|
|
// TODO(vmarmol): Remove this hack, it is to support the old charts and the new charts during the transition.
|
|
if (window.charts) {
|
|
if (!(elementId in window.charts)) {
|
|
ac = new google.visualization.LineChart(document.getElementById(elementId));
|
|
window.charts[elementId] = ac;
|
|
}
|
|
ac = window.charts[elementId];
|
|
opts = window.chartOptions;
|
|
} else {
|
|
ac = new google.visualization.LineChart(document.getElementById(elementId));
|
|
opts = {};
|
|
}
|
|
opts.vAxis = {title: unit};
|
|
opts.legend = {position: 'bottom'};
|
|
ac.draw(dataTable, window.chartOptions);
|
|
}
|
|
|
|
// Draw a gauge.
|
|
function drawGauge(elementId, cpuUsage, memoryUsage) {
|
|
var gauges = [['Label', 'Value']];
|
|
if (cpuUsage >= 0) {
|
|
gauges.push(['CPU', cpuUsage]);
|
|
}
|
|
if (memoryUsage >= 0) {
|
|
gauges.push(['Memory', memoryUsage]);
|
|
}
|
|
// Create and populate the data table.
|
|
var data = google.visualization.arrayToDataTable(gauges);
|
|
|
|
// Create and draw the visualization.
|
|
var options = {
|
|
width: 400, height: 120,
|
|
redFrom: 90, redTo: 100,
|
|
yellowFrom:75, yellowTo: 90,
|
|
minorTicks: 5,
|
|
animation: {
|
|
duration: 900,
|
|
easing: 'linear'
|
|
}
|
|
};
|
|
var chart = new google.visualization.Gauge(document.getElementById(elementId));
|
|
chart.draw(data, options);
|
|
}
|
|
|
|
// Get the machine info.
|
|
function getMachineInfo(callback) {
|
|
$.getJSON("/api/v1.0/machine", function(data) {
|
|
callback(data);
|
|
});
|
|
}
|
|
|
|
// Get the container stats for the specified container.
|
|
function getStats(containerName, callback) {
|
|
$.getJSON("/api/v1.0/containers" + containerName, function(data) {
|
|
callback(data);
|
|
});
|
|
}
|
|
|
|
// Draw the graph for CPU usage.
|
|
function drawCpuTotalUsage(elementId, machineInfo, stats) {
|
|
var titles = ["Time", "Total"];
|
|
var data = [];
|
|
for (var i = 1; i < stats.stats.length; i++) {
|
|
var cur = stats.stats[i];
|
|
var prev = stats.stats[i - 1];
|
|
|
|
// TODO(vmarmol): This assumes we sample every second, use the timestamps.
|
|
var elements = [];
|
|
elements.push(cur.timestamp);
|
|
elements.push((cur.cpu.usage.total - prev.cpu.usage.total) / 1000000000);
|
|
data.push(elements);
|
|
}
|
|
drawLineChart(titles, data, elementId, "Cores");
|
|
}
|
|
|
|
// Draw the graph for per-core CPU usage.
|
|
function drawCpuPerCoreUsage(elementId, machineInfo, stats) {
|
|
// Add a title for each core.
|
|
var titles = ["Time"];
|
|
for (var i = 0; i < machineInfo.num_cores; i++) {
|
|
titles.push("Core " + i);
|
|
}
|
|
var data = [];
|
|
for (var i = 1; i < stats.stats.length; i++) {
|
|
var cur = stats.stats[i];
|
|
var prev = stats.stats[i - 1];
|
|
|
|
var elements = [];
|
|
elements.push(cur.timestamp);
|
|
for (var j = 0; j < machineInfo.num_cores; j++) {
|
|
// TODO(vmarmol): This assumes we sample every second, use the timestamps.
|
|
elements.push((cur.cpu.usage.per_cpu_usage[j] - prev.cpu.usage.per_cpu_usage[j]) / 1000000000);
|
|
}
|
|
data.push(elements);
|
|
}
|
|
drawLineChart(titles, data, elementId, "Cores");
|
|
}
|
|
|
|
// Draw the graph for CPU usage breakdown.
|
|
function drawCpuUsageBreakdown(elementId, containerInfo) {
|
|
var titles = ["Time", "User", "Kernel"];
|
|
var data = [];
|
|
for (var i = 1; i < containerInfo.stats.length; i++) {
|
|
var cur = containerInfo.stats[i];
|
|
var prev = containerInfo.stats[i - 1];
|
|
|
|
// TODO(vmarmol): This assumes we sample every second, use the timestamps.
|
|
var elements = [];
|
|
elements.push(cur.timestamp);
|
|
elements.push((cur.cpu.usage.user - prev.cpu.usage.user) / 1000000000);
|
|
elements.push((cur.cpu.usage.system - prev.cpu.usage.system) / 1000000000);
|
|
data.push(elements);
|
|
}
|
|
drawLineChart(titles, data, elementId, "Cores");
|
|
}
|
|
|
|
// Draw the gauges for overall resource usage.
|
|
function drawOverallUsage(elementId, machineInfo, containerInfo) {
|
|
var cur = containerInfo.stats[containerInfo.stats.length - 1];
|
|
|
|
var cpuUsage = 0;
|
|
if (containerInfo.spec.cpu && containerInfo.stats.length >= 2) {
|
|
var prev = containerInfo.stats[containerInfo.stats.length - 2];
|
|
var rawUsage = cur.cpu.usage.total - prev.cpu.usage.total;
|
|
|
|
// Convert to millicores and take the percentage
|
|
cpuUsage = Math.round(((rawUsage / 1000000) / containerInfo.spec.cpu.limit) * 100);
|
|
if (cpuUsage > 100) {
|
|
cpuUsage = 100;
|
|
}
|
|
}
|
|
|
|
var memoryUsage = 0;
|
|
if (containerInfo.spec.memory) {
|
|
// Saturate to the machine size.
|
|
var limit = containerInfo.spec.memory.limit;
|
|
if (limit > machineInfo.memory_capacity) {
|
|
limit = machineInfo.memory_capacity;
|
|
}
|
|
|
|
memoryUsage = Math.round((cur.memory.usage / limit) * 100);
|
|
}
|
|
|
|
drawGauge(elementId, cpuUsage, memoryUsage);
|
|
}
|
|
|
|
var oneMegabyte = 1024 * 1024;
|
|
|
|
function drawMemoryUsage(elementId, containerInfo) {
|
|
var titles = ["Time", "Total"];
|
|
var data = [];
|
|
for (var i = 0; i < containerInfo.stats.length; i++) {
|
|
var cur = containerInfo.stats[i];
|
|
|
|
// TODO(vmarmol): This assumes we sample every second, use the timestamps.
|
|
var elements = [];
|
|
elements.push(cur.timestamp);
|
|
elements.push(cur.memory.usage / oneMegabyte);
|
|
data.push(elements);
|
|
}
|
|
drawLineChart(titles, data, elementId, "Megabytes");
|
|
}
|
|
|
|
function drawMemoryPageFaults(elementId, containerInfo) {
|
|
var titles = ["Time", "Faults", "Major Faults"];
|
|
var data = [];
|
|
for (var i = 1; i < containerInfo.stats.length; i++) {
|
|
var cur = containerInfo.stats[i];
|
|
var prev = containerInfo.stats[i - 1];
|
|
|
|
// TODO(vmarmol): This assumes we sample every second, use the timestamps.
|
|
var elements = [];
|
|
elements.push(cur.timestamp);
|
|
elements.push(cur.memory.hierarchical_data.pgfault - prev.memory.hierarchical_data.pgfault);
|
|
// TODO(vmarmol): Fix to expose this data.
|
|
//elements.push(cur.memory.hierarchical_data.pgmajfault - prev.memory.hierarchical_data.pgmajfault);
|
|
elements.push(0);
|
|
data.push(elements);
|
|
}
|
|
drawLineChart(titles, data, elementId, "Faults");
|
|
}
|
|
|
|
// Draw the graph for network tx/rx bytes.
|
|
function drawNetworkBytes(elementId, machineInfo, stats) {
|
|
var titles = ["Time", "Tx bytes", "Rx bytes"];
|
|
var data = [];
|
|
for (var i = 1; i < stats.stats.length; i++) {
|
|
var cur = stats.stats[i];
|
|
var prev = stats.stats[i - 1];
|
|
|
|
// TODO(vmarmol): This assumes we sample every second, use the timestamps.
|
|
var elements = [];
|
|
elements.push(cur.timestamp);
|
|
elements.push(cur.network.tx_bytes - prev.network.tx_bytes);
|
|
elements.push(cur.network.rx_bytes - prev.network.rx_bytes);
|
|
data.push(elements);
|
|
}
|
|
drawLineChart(titles, data, elementId, "Bytes per second");
|
|
}
|
|
|
|
// Draw the graph for network errors
|
|
function drawNetworkErrors(elementId, machineInfo, stats) {
|
|
var titles = ["Time", "Tx", "Rx"];
|
|
var data = [];
|
|
for (var i = 1; i < stats.stats.length; i++) {
|
|
var cur = stats.stats[i];
|
|
var prev = stats.stats[i - 1];
|
|
|
|
// TODO(vmarmol): This assumes we sample every second, use the timestamps.
|
|
var elements = [];
|
|
elements.push(cur.timestamp);
|
|
elements.push(cur.network.tx_errors - prev.network.tx_errors);
|
|
elements.push(cur.network.rx_errors - prev.network.rx_errors);
|
|
data.push(elements);
|
|
}
|
|
drawLineChart(titles, data, elementId, "Errors per second");
|
|
}
|
|
|
|
// Expects an array of closures to call. After each execution the JS runtime is given control back before continuing.
|
|
// This function returns asynchronously
|
|
function stepExecute(steps) {
|
|
// No steps, stop.
|
|
if (steps.length == 0) {
|
|
return;
|
|
}
|
|
|
|
// Get a step and execute it.
|
|
var step = steps.shift();
|
|
step();
|
|
|
|
// Schedule the next step.
|
|
setTimeout(function() {
|
|
stepExecute(steps);
|
|
}, 0);
|
|
}
|
|
|
|
// Draw all the charts on the page.
|
|
function drawCharts(machineInfo, containerInfo) {
|
|
var steps = [];
|
|
|
|
steps.push(function() {
|
|
drawOverallUsage("usage-gauge", machineInfo, containerInfo)
|
|
});
|
|
|
|
// CPU.
|
|
steps.push(function() {
|
|
drawCpuTotalUsage("cpu-total-usage-chart", machineInfo, containerInfo);
|
|
});
|
|
steps.push(function() {
|
|
drawCpuPerCoreUsage("cpu-per-core-usage-chart", machineInfo, containerInfo);
|
|
});
|
|
steps.push(function() {
|
|
drawCpuUsageBreakdown("cpu-usage-breakdown-chart", containerInfo);
|
|
});
|
|
|
|
// Memory.
|
|
steps.push(function() {
|
|
drawMemoryUsage("memory-usage-chart", containerInfo);
|
|
});
|
|
steps.push(function() {
|
|
drawMemoryPageFaults("memory-page-faults-chart", containerInfo);
|
|
});
|
|
|
|
// Network.
|
|
steps.push(function() {
|
|
drawNetworkBytes("network-bytes-chart", machineInfo, containerInfo);
|
|
});
|
|
steps.push(function() {
|
|
drawNetworkErrors("network-errors-chart", machineInfo, containerInfo);
|
|
});
|
|
|
|
stepExecute(steps);
|
|
}
|
|
|
|
// Executed when the page finishes loading.
|
|
function startPage(containerName, hasCpu, hasMemory) {
|
|
// Don't fetch data if we don't have any resource.
|
|
if (!hasCpu && !hasMemory) {
|
|
return;
|
|
}
|
|
|
|
// TODO(vmarmol): Look into changing the view window to get a smoother animation.
|
|
window.chartOptions = {
|
|
curveType: 'function',
|
|
height: 300,
|
|
legend:{position:"none"},
|
|
focusTarget: "category",
|
|
};
|
|
window.charts = {};
|
|
|
|
// Get machine info, then get the stats every 1s.
|
|
getMachineInfo(function(machineInfo) {
|
|
setInterval(function() {
|
|
getStats(containerName, function(stats){
|
|
drawCharts(machineInfo, stats);
|
|
});
|
|
}, 1000);
|
|
});
|
|
}
|
|
`
|