Files
kops-arm64/templates/index.html
Henning Jacobs 9b39874860 resource usage
2016-12-13 23:32:17 +01:00

262 lines
7.2 KiB
HTML

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Kubernetes Operational View</title>
<style>* {padding: 0; margin: 0}</style>
</head>
<body>
<script src="/static/pixi.min.js"></script>
<script>
//Create the renderer
const renderer = PIXI.autoDetectRenderer(256, 256);
renderer.view.style.position = "absolute";
renderer.view.style.display = "block";
renderer.autoResize = true;
renderer.resize(window.innerWidth, window.innerHeight);
//Add the canvas to the HTML document
document.body.appendChild(renderer.view);
//Create a container object called the `stage`
const stage = new PIXI.Container();
var graphics = new PIXI.Graphics();
stage.addChild(graphics);
var clusters = []
const FACTORS = {
'm': 1/1000,
'Ki': 1000,
'Mi': 1000 * 1000
}
function parseResource(v) {
const match = v.match(/^(\d*)(\D*)$/)
const factor = FACTORS[match[2]] || 1
return parseInt(match[1]) * factor
}
class Tooltip extends PIXI.Graphics {
constructor () {
super()
}
draw () {
var tooltip = this
tooltip.lineStyle(2, 0xaaaaff, 1)
tooltip.beginFill(0x999999, 0.5)
tooltip.drawRect(0, 0, 200, 400)
tooltip.endFill()
var text = new PIXI.Text('', {fontSize: 12, fill: 0xffffff})
text.x = 2
text.y = 2
tooltip.addChild(text)
tooltip.text = text
tooltip.visible = false
}
}
class Node extends PIXI.Graphics {
constructor (node, tooltip) {
super()
this.node = node
this.tooltip = tooltip
}
isMaster() {
return this.node.labels.master == 'true'
}
getResourceUsage() {
const resources = {}
for (let key of Object.keys(this.node.status.capacity)) {
resources[key] = {'capacity': parseResource(this.node.status.capacity[key]), 'used': 0}
}
for (let pod of this.node.pods) {
for (let container of pod.spec.containers) {
if (container.resources && container.resources.requests) {
for (let key of Object.keys(container.resources.requests)) {
resources[key].used += parseResource(container.resources.requests[key])
}
}
}
}
resources['pods'].used = this.node.pods.length
return resources
}
draw () {
var nodeBox = this
nodeBox.lineStyle(2, 0xaaaaff, 1);
nodeBox.beginFill(0x999999, 0.5)
nodeBox.drawRect(0, 0, 105, 100)
nodeBox.endFill()
nodeBox.lineStyle(2, 0xaaaaaa, 1);
nodeBox.interactive = true
nodeBox.on('mouseover', function() {
var s = this.node.name
for (let key of Object.keys(this.node.labels)) {
s += '\n' + key + ': ' + this.node.labels[key]
}
this.tooltip.text.text = s
this.tooltip.x = this.toGlobal(new PIXI.Point(0, 0)).x
this.tooltip.y = this.toGlobal(new PIXI.Point(0,0)).y
this.tooltip.visible = true
})
nodeBox.on('mouseout', function() {
this.tooltip.visible = false
})
const resources = this.getResourceUsage()
for (var i=0; i<resources.cpu.capacity; i++) {
nodeBox.drawRect(5, 90 - i * 5, 5, 5)
}
nodeBox.beginFill(0xaaffaa, 1)
nodeBox.drawRect(5, 95 - resources.cpu.used * 5, 5, resources.cpu.used * 5)
nodeBox.endFill()
const scale = 1000*1000*200
nodeBox.drawRect(14, 95 - resources.memory.capacity/scale, 5, resources.memory.capacity/scale)
nodeBox.lineStyle(2, 0xaaffaa, 1)
nodeBox.beginFill(0xaaffaa, 1)
nodeBox.drawRect(15, 95 - resources.memory.used/scale, 3, resources.memory.used/scale)
nodeBox.endFill()
var text = new PIXI.Text('', {fontSize: 10, fill: 0xffffff})
nodeBox.addChild(text)
var px = 24
var py = 5
for (let pod of this.node.pods) {
if (pod.metadata.namespace != 'kube-system') {
var podBox = new Pod(pod)
podBox.x = px
podBox.y = py
nodeBox.addChild(podBox.draw())
px += 13
if (px > 90) {
px = 24
py += 13
}
}
}
var px = 24
var py = 85
for (let pod of this.node.pods) {
if (pod.metadata.namespace == 'kube-system') {
var podBox = new Pod(pod)
podBox.x = px
podBox.y = py
nodeBox.addChild(podBox.draw())
px += 13
if (px > 90) {
px = 24
py -= 13
}
}
}
return nodeBox
}
}
class Pod extends PIXI.Graphics {
constructor (pod) {
super()
this.pod = pod
}
draw() {
var podBox = this
podBox.lineStyle(2, 0xaaaaaa, 1);
var i = 0
var w = 10 / this.pod.spec.containers.length
for (let container of this.pod.spec.containers) {
podBox.drawRect(0 + i * w, 0, w, 10)
i++
}
var ready = true
for (let containerStatus of this.pod.status.containerStatuses) {
if (!containerStatus.ready) {
ready = false
}
}
if (this.pod.status.phase == 'Running' && ready) {
podBox.lineStyle(2, 0xaaffaa, 1);
} else if (this.pod.status.phase == 'Pending') {
podBox.lineStyle(2, 0xffffaa, 1);
} else {
podBox.lineStyle(2, 0xff9999, 1);
}
podBox.beginFill(0x999999, 0.5)
podBox.drawRect(0, 0, 10, 10)
return this
}
}
var tooltip = new Tooltip()
tooltip.draw()
function update(clusters) {
graphics.removeChildren();
graphics.lineStyle(2, 0xaaaaff, 1);
var x = 50;
for (let cluster of clusters) {
var clusterBox = new PIXI.Graphics()
clusterBox.x = x
clusterBox.y = 50
graphics.addChild(clusterBox)
var rows = [10, 10]
for (let node of cluster.nodes) {
var nodeBox = new Node(node, tooltip)
nodeBox.draw()
if (nodeBox.isMaster()) {
nodeBox.x = rows[0]
rows[0] += nodeBox.width + 5
nodeBox.y = 10
} else {
nodeBox.x = rows[1]
rows[1] += nodeBox.width + 5
nodeBox.y = nodeBox.height + 15
}
clusterBox.addChild(nodeBox)
}
clusterBox.lineStyle(2, 0xaaaaff, 1);
clusterBox.drawRect(0, 0, Math.max(rows[0], rows[1]), nodeBox.height * 2 + 20);
x += 250;
}
graphics.addChild(tooltip)
}
function fetchData() {
fetch('/kubernetes-clusters')
.then(function(response) {
return response.json()
})
.then(function(json) {
clusters = json.kubernetes_clusters;
update(clusters)
});
window.setTimeout(fetchData, 5000)
}
fetchData()
function state() {
}
function mainLoop() {
requestAnimationFrame(mainLoop);
state();
//Tell the `renderer` to `render` the `stage`
renderer.render(stage);
}
mainLoop();
</script>
</body>
</html>