Adding Charts
parent
55c34e9375
commit
4ac2d3d76f
10
README.md
10
README.md
|
@ -107,17 +107,11 @@ Configure the flask configuration according to your needs (port,host,...) in con
|
||||||
class flask_conf:
|
class flask_conf:
|
||||||
port = 8090
|
port = 8090
|
||||||
host = "0.0.0.0"
|
host = "0.0.0.0"
|
||||||
thread = False
|
thread = True
|
||||||
debug = False
|
debug = False
|
||||||
reloader = False
|
reloader = False
|
||||||
ssl = 'adhoc'
|
|
||||||
```
|
|
||||||
NB: the parameter ssl = 'adhoc' will activate the HTTPS with a self certificate.
|
|
||||||
If you get an error, you should add this package :
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pip3 install pyopenssl
|
|
||||||
```
|
```
|
||||||
|
NB: Let thread = True, else this will take a time to switch from node to another node
|
||||||
|
|
||||||
## Path configuration
|
## Path configuration
|
||||||
```bash
|
```bash
|
||||||
|
|
31
app.py
31
app.py
|
@ -1,11 +1,13 @@
|
||||||
#!/usr/bin/python3
|
#!/usr/bin/python3
|
||||||
import paramiko
|
import paramiko
|
||||||
from config import *
|
from config import *
|
||||||
from flask import Flask, redirect, render_template, request, url_for, Response
|
from flask import Flask, redirect, render_template, request, url_for, Response, stream_with_context
|
||||||
|
import time
|
||||||
|
from datetime import datetime
|
||||||
|
import json
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
|
|
||||||
|
|
||||||
def get_remote(hostname, port, username, password, local_script_path, remote_script_path):
|
def get_remote(hostname, port, username, password, local_script_path, remote_script_path):
|
||||||
ssh = paramiko.SSHClient()
|
ssh = paramiko.SSHClient()
|
||||||
try:
|
try:
|
||||||
|
@ -30,15 +32,34 @@ def get_full(node):
|
||||||
def get_info():
|
def get_info():
|
||||||
node = 'localhost'
|
node = 'localhost'
|
||||||
full = get_full(node)
|
full = get_full(node)
|
||||||
return render_template('index.html', full = full, client = client, titre = flask_conf.title)
|
return render_template('index.html', full = full, client = client, titre = flask_conf.title, node = node)
|
||||||
|
|
||||||
@app.route("/n/<node>", methods = ['POST', 'GET'])
|
@app.route("/n/<node>", methods = ['POST', 'GET'])
|
||||||
def get_node_info(node):
|
def get_node_info(node):
|
||||||
full = get_full(node)
|
full = get_full(node)
|
||||||
return render_template('index.html', full = full, client = client, titre = flask_conf.title)
|
return render_template('index.html', full = full, client = client, titre = flask_conf.title, node = node)
|
||||||
|
|
||||||
|
def get_ressources(nodename):
|
||||||
|
while True:
|
||||||
|
full = get_full(nodename)
|
||||||
|
json_data = json.dumps(
|
||||||
|
{
|
||||||
|
"time": datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
||||||
|
"cpu": full['cpu']['percent'],
|
||||||
|
"mem": full['mem']['percent'],
|
||||||
|
"swap": full['swap']['percent'],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
yield f"data:{json_data}\n\n"
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
@app.route("/chart-ressources/<nodename>")
|
||||||
|
def chart_data(nodename) -> Response:
|
||||||
|
response = Response(stream_with_context(get_ressources(nodename)), mimetype="text/event-stream")
|
||||||
|
response.headers["Cache-Control"] = "no-cache"
|
||||||
|
response.headers["X-Accel-Buffering"] = "no"
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
app.run(host=flask_conf.host, port=flask_conf.port, use_reloader=flask_conf.reloader, threaded=flask_conf.thread, debug=flask_conf.debug, ssl_context=flask_conf.ssl)
|
app.run(host=flask_conf.host, port=flask_conf.port, use_reloader=flask_conf.reloader, threaded=flask_conf.thread, debug=flask_conf.debug)
|
||||||
|
|
|
@ -13,10 +13,9 @@ class ssh_conf:
|
||||||
class flask_conf:
|
class flask_conf:
|
||||||
port = 8090
|
port = 8090
|
||||||
host = "0.0.0.0"
|
host = "0.0.0.0"
|
||||||
thread = False
|
thread = True
|
||||||
debug = False
|
debug = False
|
||||||
reloader = False
|
reloader = False
|
||||||
ssl = 'adhoc'
|
|
||||||
title = 'Pymonit'
|
title = 'Pymonit'
|
||||||
|
|
||||||
#Path configuration
|
#Path configuration
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
|
||||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js" integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js" integrity="sha384-oBqDVmMz9ATKxIep9tiCxS/Z9fNfEXiDAYTujMAeBAsjFuCZSmKbSSUnQlmh/jp3" crossorigin="anonymous"></script>
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.css" integrity="sha512-/zs32ZEJh+/EO2N1b0PEdoA10JkdC3zJ8L5FTiQu82LR9S/rOQNfQN7U59U9BC12swNeRAz3HSzIL2vpp4fv3w==" crossorigin="anonymous" referrerpolicy="no-referrer"/>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" integrity="sha512-894YE6QWD5I59HgZOGReFYm4dnWc1Qt5NtvYSaNcOP+u1T9qYdvdihz0PPSiiqn/+/3e7Jo4EaG7TubfWGUrMQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.9.4/Chart.min.js" integrity="sha512-d9xgZrVZpmmQlfonhQUvTR7lMPtO7NkZMkA0ABN3PHCbKA5nqylQ/yWlFAyY6hYgdF1Qh6nYiuADWwKB4C2WSw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
@ -15,6 +18,8 @@
|
||||||
<body>
|
<body>
|
||||||
<center>
|
<center>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<nav class="navbar navbar-expand-lg bg-body-tertiary shadow">
|
<nav class="navbar navbar-expand-lg bg-body-tertiary shadow">
|
||||||
<div class="container-fluid">
|
<div class="container-fluid">
|
||||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
@ -52,10 +57,23 @@ Environnement
|
||||||
<br> CPU : {{ full['cpu_number'] }}
|
<br> CPU : {{ full['cpu_number'] }}
|
||||||
<br> vCPU : {{ full['vcpu'] }}
|
<br> vCPU : {{ full['vcpu'] }}
|
||||||
<br> Memory : {{ full['mem_max']}}
|
<br> Memory : {{ full['mem_max']}}
|
||||||
|
<br> Node : {{ node }}
|
||||||
</div></div>
|
</div></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-8">
|
<div class="col-8">
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
Monitor
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<canvas id="canvas"></canvas>
|
||||||
|
<br>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<br>
|
||||||
|
<div class="card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
Usage
|
Usage
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,8 +104,6 @@ total : {{ full['swap']['total'] }} / used : {{ full['swap']['used'] }} / free :
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
@ -126,5 +142,90 @@ total : {{ full[key]['size_total'] }} / used : {{ full[key]['size_used'] }} / fr
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
$(document).ready(function () {
|
||||||
|
const config = {
|
||||||
|
type: 'line',
|
||||||
|
data: {
|
||||||
|
labels: Array(30).fill("0000-00-00 00:00:00"),
|
||||||
|
datasets: [{
|
||||||
|
label: "CPU %",
|
||||||
|
backgroundColor: 'rgb(0, 191, 255)',
|
||||||
|
borderColor: 'rgb(0, 191, 255)',
|
||||||
|
data: Array(30).fill(null),
|
||||||
|
fill: false,
|
||||||
|
},{
|
||||||
|
label: "Mem %",
|
||||||
|
backgroundColor: 'rgb(255,240,100)',
|
||||||
|
borderColor: 'rgb(255, 240, 100)',
|
||||||
|
data: Array(30).fill(null),
|
||||||
|
fill: false,
|
||||||
|
},{
|
||||||
|
label: "Swap %",
|
||||||
|
backgroundColor: 'rgb(255, 20, 100)',
|
||||||
|
borderColor: 'rgb(255, 20, 100)',
|
||||||
|
data: Array(30).fill(null),
|
||||||
|
fill: false,
|
||||||
|
}],
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
responsive: true,
|
||||||
|
title: {
|
||||||
|
display: false,
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
mode: 'index',
|
||||||
|
intersect: false,
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
mode: 'nearest',
|
||||||
|
intersect: true
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
xAxes: [{
|
||||||
|
display: false,
|
||||||
|
scaleLabel: {
|
||||||
|
display: true,
|
||||||
|
labelString: 'Time'
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
yAxes: [{
|
||||||
|
display: true,
|
||||||
|
scaleLabel: [{
|
||||||
|
display: true,
|
||||||
|
labelString: 'CPU'
|
||||||
|
},{
|
||||||
|
display: true,
|
||||||
|
labelString: 'Mem'
|
||||||
|
},{
|
||||||
|
display: true,
|
||||||
|
labelString: 'Swap'
|
||||||
|
}]
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const context = document.getElementById('canvas').getContext('2d');
|
||||||
|
const lineChart = new Chart(context, config);
|
||||||
|
const source = new EventSource("/chart-ressources/{{ node }}");
|
||||||
|
source.onmessage = function (event) {
|
||||||
|
const data = JSON.parse(event.data);
|
||||||
|
if (config.data.labels.length === 30) {
|
||||||
|
config.data.labels.shift();
|
||||||
|
config.data.datasets[0].data.shift();
|
||||||
|
config.data.datasets[1].data.shift();
|
||||||
|
config.data.datasets[2].data.shift();
|
||||||
|
}
|
||||||
|
config.data.labels.push(data.time);
|
||||||
|
config.data.datasets[0].data.push(data.cpu);
|
||||||
|
config.data.datasets[1].data.push(data.mem);
|
||||||
|
config.data.datasets[2].data.push(data.swap);
|
||||||
|
lineChart.update();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
Loading…
Reference in New Issue