[BS5] add host statistics
This commit is contained in:
parent
9747995510
commit
a3c0737ba8
@ -8,6 +8,7 @@ RUN apk add --update --no-cache python3 \
|
||||
py3-pip \
|
||||
openssl \
|
||||
tzdata \
|
||||
py3-psutil \
|
||||
&& pip3 install --upgrade pip \
|
||||
docker \
|
||||
flask \
|
||||
|
@ -6,6 +6,7 @@ from flask import jsonify
|
||||
from flask import Response
|
||||
from flask import request
|
||||
from threading import Thread
|
||||
from datetime import datetime
|
||||
import docker
|
||||
import uuid
|
||||
import signal
|
||||
@ -17,6 +18,7 @@ import ssl
|
||||
import socket
|
||||
import subprocess
|
||||
import traceback
|
||||
import psutil
|
||||
|
||||
docker_client = docker.DockerClient(base_url='unix://var/run/docker.sock', version='auto')
|
||||
app = Flask(__name__)
|
||||
@ -326,6 +328,48 @@ class container_post(Resource):
|
||||
else:
|
||||
return jsonify(type='danger', msg='command did not complete')
|
||||
|
||||
class host_stats_get(Resource):
|
||||
def get(self):
|
||||
try:
|
||||
system_time = datetime.now()
|
||||
|
||||
disk_io_before = psutil.disk_io_counters(perdisk=False)
|
||||
net_io_before = psutil.net_io_counters(pernic=False)
|
||||
time.sleep(1)
|
||||
disk_io_after = psutil.disk_io_counters(perdisk=False)
|
||||
net_io_after = psutil.net_io_counters(pernic=False)
|
||||
|
||||
disks_read_per_sec = disk_io_after.read_bytes - disk_io_before.read_bytes
|
||||
disks_write_per_sec = disk_io_after.write_bytes - disk_io_before.write_bytes
|
||||
net_recv_per_sec = net_io_after.bytes_recv - net_io_before.bytes_recv
|
||||
net_sent_per_sec = net_io_after.bytes_sent - net_io_before.bytes_sent
|
||||
|
||||
|
||||
host_stats = {
|
||||
"cpu": {
|
||||
"cores": psutil.cpu_count(),
|
||||
"usage": psutil.cpu_percent()
|
||||
},
|
||||
"memory": {
|
||||
"total": psutil.virtual_memory().total,
|
||||
"usage": psutil.virtual_memory().percent,
|
||||
"swap": psutil.swap_memory()
|
||||
},
|
||||
"disk": {
|
||||
"read_bytes": disks_read_per_sec,
|
||||
"write_bytes": disks_write_per_sec
|
||||
},
|
||||
"network": {
|
||||
"bytes_recv": net_recv_per_sec,
|
||||
"bytes_sent": net_sent_per_sec
|
||||
},
|
||||
"uptime": time.time() - psutil.boot_time(),
|
||||
"system_time": system_time.strftime("%d.%m.%Y %H:%M:%S")
|
||||
}
|
||||
return host_stats
|
||||
except Exception as e:
|
||||
return jsonify(type='danger', msg=str(e))
|
||||
|
||||
def exec_cmd_container(container, cmd, user, timeout=2, shell_cmd="/bin/bash"):
|
||||
|
||||
def recv_socket_data(c_socket, timeout):
|
||||
@ -406,6 +450,7 @@ def startFlaskAPI():
|
||||
api.add_resource(containers_get, '/containers/json')
|
||||
api.add_resource(container_get, '/containers/<string:container_id>/json')
|
||||
api.add_resource(container_post, '/containers/<string:container_id>/<string:post_action>')
|
||||
api.add_resource(host_stats_get, '/host/stats')
|
||||
|
||||
if __name__ == '__main__':
|
||||
api_thread = Thread(target=startFlaskAPI)
|
||||
|
@ -44,15 +44,22 @@ foreach ($containers as $container => $container_info) {
|
||||
$containers[$container]['State']['StartedAtHR'] = $started;
|
||||
}
|
||||
|
||||
// get mailconf data
|
||||
$hostname = getenv('MAILCOW_HOSTNAME');
|
||||
$timezone = getenv('TZ');
|
||||
|
||||
$template = 'debug.twig';
|
||||
$template_data = [
|
||||
'log_lines' => getenv('LOG_LINES'),
|
||||
'vmail_df' => $vmail_df,
|
||||
'hostname' => $hostname,
|
||||
'timezone' => $timezone,
|
||||
'solr_status' => $solr_status,
|
||||
'solr_uptime' => round($solr_status['status']['dovecot-fts']['uptime'] / 1000 / 60 / 60),
|
||||
'clamd_status' => $clamd_status,
|
||||
'containers' => $containers,
|
||||
'lang_admin' => json_encode($lang['admin']),
|
||||
'lang_debug' => json_encode($lang['debug']),
|
||||
'lang_datatables' => json_encode($lang['datatables']),
|
||||
];
|
||||
|
||||
|
@ -32,6 +32,7 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
||||
}
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
case 'containers':
|
||||
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/containers/json');
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
@ -146,5 +147,23 @@ function docker($action, $service_name = null, $attr1 = null, $attr2 = null, $ex
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'host_stats':
|
||||
curl_setopt($curl, CURLOPT_URL, 'https://dockerapi:443/host/stats');
|
||||
curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
|
||||
curl_setopt($curl, CURLOPT_POST, 0);
|
||||
curl_setopt($curl, CURLOPT_TIMEOUT, $DOCKER_TIMEOUT);
|
||||
$response = curl_exec($curl);
|
||||
if ($response === false) {
|
||||
$err = curl_error($curl);
|
||||
curl_close($curl);
|
||||
return $err;
|
||||
}
|
||||
else {
|
||||
curl_close($curl);
|
||||
$stats = json_decode($response, true);
|
||||
if (!empty($stats)) return $stats;
|
||||
}
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -36,6 +36,15 @@ $(document).ready(function() {
|
||||
$(this).text(started_local_date);
|
||||
}
|
||||
});
|
||||
|
||||
// set default ChartJs Font Color
|
||||
Chart.defaults.color = '#999';
|
||||
// create net and disk charts
|
||||
createNetAndDiskChart();
|
||||
// check for new version
|
||||
check_update(mailcow_info.version_tag, mailcow_info.project_url);
|
||||
// update system stats
|
||||
update_stats();
|
||||
});
|
||||
jQuery(function($){
|
||||
if (localStorage.getItem("current_page") === null) {
|
||||
@ -998,3 +1007,233 @@ jQuery(function($){
|
||||
onVisible("[id^=rspamd_history]", () => draw_rspamd_history());
|
||||
onVisible("[id^=rspamd_donut]", () => rspamd_pie_graph());
|
||||
});
|
||||
|
||||
|
||||
// update system stats - every 5 seconds if system & container tab is active
|
||||
function update_stats(){
|
||||
if (!$('#tab-containers').hasClass('active')) {
|
||||
// tab not active - dont fetch stats - run again in n seconds
|
||||
setTimeout(update_stats, 5000);
|
||||
return;
|
||||
}
|
||||
|
||||
window.fetch("/api/v1/get/status/host", {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(data) {
|
||||
$("#host_date").text(data.system_time);
|
||||
$("#host_uptime").text(formatUptime(data.uptime));
|
||||
$("#host_cpu_cores").text(data.cpu.cores);
|
||||
$("#host_cpu_usage").text(parseInt(data.cpu.usage).toString() + "%");
|
||||
$("#host_memory_total").text((data.memory.total / (1024 ** 3)).toFixed(2).toString() + "GB");
|
||||
$("#host_memory_usage").text(parseInt(data.memory.usage).toString() + "%");
|
||||
|
||||
var net_io_chart = Chart.getChart("net_io_chart");
|
||||
var disk_io_chart = Chart.getChart("disk_io_chart");
|
||||
|
||||
net_io_chart.data.labels.push(data.system_time.split(" ")[1]);
|
||||
if (net_io_chart.data.labels.length > 20) {
|
||||
net_io_chart.data.labels.shift();
|
||||
}
|
||||
net_io_chart.data.datasets[0].data.push((data.network.bytes_recv / 1024).toFixed(4));
|
||||
net_io_chart.data.datasets[1].data.push((data.network.bytes_sent / 1024).toFixed(4));
|
||||
if (net_io_chart.data.datasets[0].data.length > 20) {
|
||||
net_io_chart.data.datasets[0].data.shift();
|
||||
}
|
||||
if (net_io_chart.data.datasets[1].data.length > 20) {
|
||||
net_io_chart.data.datasets[1].data.shift();
|
||||
}
|
||||
|
||||
disk_io_chart.data.labels.push(data.system_time.split(" ")[1]);
|
||||
if (disk_io_chart.data.labels.length > 20) {
|
||||
disk_io_chart.data.labels.shift();
|
||||
}
|
||||
disk_io_chart.data.datasets[0].data.push((data.disk.read_bytes / 1024).toFixed(4));
|
||||
disk_io_chart.data.datasets[1].data.push((data.disk.write_bytes / 1024).toFixed(4));
|
||||
if (disk_io_chart.data.datasets[0].data.length > 20) {
|
||||
disk_io_chart.data.datasets[0].data.shift();
|
||||
}
|
||||
if (disk_io_chart.data.datasets[1].data.length > 20) {
|
||||
disk_io_chart.data.datasets[1].data.shift();
|
||||
}
|
||||
|
||||
net_io_chart.update();
|
||||
disk_io_chart.update();
|
||||
|
||||
// run again in n seconds
|
||||
setTimeout(update_stats, 5000);
|
||||
});
|
||||
}
|
||||
// format hosts uptime seconds to readable string
|
||||
function formatUptime(seconds){
|
||||
seconds = Number(seconds);
|
||||
var d = Math.floor(seconds / (3600*24));
|
||||
var h = Math.floor(seconds % (3600*24) / 3600);
|
||||
var m = Math.floor(seconds % 3600 / 60);
|
||||
var s = Math.floor(seconds % 60);
|
||||
|
||||
var dFormat = d > 0 ? d + "D " : "";
|
||||
var hFormat = h > 0 ? h + "H " : "";
|
||||
var mFormat = m > 0 ? m + "M " : "";
|
||||
var sFormat = s > 0 ? s + "S" : "";
|
||||
return dFormat + hFormat + mFormat + sFormat;
|
||||
}
|
||||
// create network and disk chart
|
||||
function createNetAndDiskChart(){
|
||||
var net_io_ctx = document.getElementById("net_io_chart");
|
||||
var disk_io_ctx = document.getElementById("disk_io_chart");
|
||||
|
||||
var dataNet = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "Recieve",
|
||||
backgroundColor: "rgba(41, 187, 239, 0.3)",
|
||||
borderColor: "rgba(41, 187, 239, 0.6)",
|
||||
color: "#ff0000",
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.2,
|
||||
data: []
|
||||
}, {
|
||||
label: "Sent",
|
||||
backgroundColor: "rgba(239, 60, 41, 0.3)",
|
||||
borderColor: "rgba(239, 60, 41, 0.6)",
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.2,
|
||||
data: []
|
||||
}]
|
||||
};
|
||||
var optionsNet = {
|
||||
scales: {
|
||||
yAxis: {
|
||||
min: 0,
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
callback: function(i, index, ticks) {
|
||||
// b
|
||||
if (i < 1000) return i.toFixed(2).toString()+' B/s';
|
||||
// b to kb
|
||||
i = i / 1024;
|
||||
if (i < 1000) return i.toFixed(2).toString()+' KB/s';
|
||||
// kb to mb
|
||||
i = i / 1024;
|
||||
if (i < 1000) return i.toFixed(2).toString()+' MB/s';
|
||||
// final mb to gb
|
||||
return (i / 1024).toFixed(2).toString()+' GB/s';
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var dataDisk = {
|
||||
labels: [],
|
||||
datasets: [{
|
||||
label: "Read",
|
||||
backgroundColor: "rgba(41, 187, 239, 0.3)",
|
||||
borderColor: "rgba(41, 187, 239, 0.6)",
|
||||
color: "#ff0000",
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.2,
|
||||
data: []
|
||||
}, {
|
||||
label: "Write",
|
||||
backgroundColor: "rgba(239, 60, 41, 0.3)",
|
||||
borderColor: "rgba(239, 60, 41, 0.6)",
|
||||
borderWidth: 2,
|
||||
fill: true,
|
||||
tension: 0.2,
|
||||
data: []
|
||||
}]
|
||||
};
|
||||
var optionsDisk = {
|
||||
scales: {
|
||||
yAxis: {
|
||||
min: 0,
|
||||
grid: {
|
||||
display: false
|
||||
},
|
||||
ticks: {
|
||||
callback: function(i, index, ticks) {
|
||||
// b
|
||||
if (i < 1000) return i.toFixed(2).toString()+' B/s';
|
||||
// b to kb
|
||||
i = i / 1024;
|
||||
if (i < 1000) return i.toFixed(2).toString()+' KB/s';
|
||||
// kb to mb
|
||||
i = i / 1024;
|
||||
if (i < 1000) return i.toFixed(2).toString()+' MB/s';
|
||||
// final mb to gb
|
||||
return (i / 1024).toFixed(2).toString()+' GB/s';
|
||||
}
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
grid: {
|
||||
display: false
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var net_io_chart = new Chart(net_io_ctx, {
|
||||
type: 'line',
|
||||
data: dataNet,
|
||||
options: optionsNet
|
||||
});
|
||||
var disk_io_chart = new Chart(disk_io_ctx, {
|
||||
type: 'line',
|
||||
data: dataDisk,
|
||||
options: optionsDisk
|
||||
});
|
||||
}
|
||||
// check for mailcow updates
|
||||
function check_update(current_version, github_repo_url){
|
||||
var github_account = github_repo_url.split("/")[3];
|
||||
var github_repo_name = github_repo_url.split("/")[4];
|
||||
|
||||
// get details about latest release
|
||||
window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/latest", {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(latest_data) {
|
||||
// get details about current release
|
||||
window.fetch("https://api.github.com/repos/"+github_account+"/"+github_repo_name+"/releases/tags/"+current_version, {method:'GET',cache:'no-cache'}).then(function(response) {
|
||||
return response.json();
|
||||
}).then(function(current_data) {
|
||||
// compare releases
|
||||
var date_current = new Date(current_data.created_at);
|
||||
var date_latest = new Date(latest_data.created_at);
|
||||
if (date_latest.getTime() <= date_current.getTime()){
|
||||
// no update available
|
||||
$("#mailcow_update").removeClass("text-warning text-danger").addClass("text-success");
|
||||
$("#mailcow_update").html("<b>" + lang_debug.no_update_available + "</b>");
|
||||
} else {
|
||||
// update available
|
||||
$("#mailcow_update").removeClass("text-danger text-success").addClass("text-warning");
|
||||
$("#mailcow_update").html(
|
||||
`<b>` + lang_debug.update_available + `
|
||||
<a target="_blank" href="https://github.com/`+github_account+`/`+github_repo_name+`/releases/tag/`+latest_data.tag_name+`">`+latest_data.tag_name+`</a></b>`
|
||||
);
|
||||
}
|
||||
}).catch(err => {
|
||||
// err
|
||||
console.log(err);
|
||||
$("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
|
||||
$("#mailcow_update").html("<b>Could not check for an Update</b>");
|
||||
});
|
||||
}).catch(err => {
|
||||
// err
|
||||
console.log(err);
|
||||
$("#mailcow_update").removeClass("text-success text-warning").addClass("text-danger");
|
||||
$("#mailcow_update").html("<b>Could not check for an Update</b>");
|
||||
});
|
||||
}
|
||||
|
@ -1492,6 +1492,10 @@ if (isset($_GET['query'])) {
|
||||
'solr_documents' => $solr_documents
|
||||
));
|
||||
break;
|
||||
case "host":
|
||||
$stats = docker("host_stats");
|
||||
echo json_encode($stats);
|
||||
break;
|
||||
case "version":
|
||||
echo json_encode(array(
|
||||
'version' => $GLOBALS['MAILCOW_GIT_VERSION']
|
||||
|
@ -494,6 +494,7 @@
|
||||
"containers_info": "Container-Information",
|
||||
"container_running": "Läuft",
|
||||
"container_stopped": "Angehalten",
|
||||
"current_time": "Systemzeit",
|
||||
"disk_usage": "Festplattennutzung",
|
||||
"docs": "Dokumente",
|
||||
"external_logs": "Externe Logs",
|
||||
@ -504,6 +505,7 @@
|
||||
"log_info": "<p>mailcow <b>in-memory Logs</b> werden in Redis Listen gespeichert, die maximale Anzahl der Einträge pro Anwendung richtet sich nach LOG_LINES (%d).\r\n <br>In-memory Logs sind vergänglich und nicht zur ständigen Aufbewahrung bestimmt. Alle Anwendungen, die in-memory protokollieren, schreiben ebenso in den Docker Daemon.\r\n <br>Das in-memory Protokoll versteht sich als schnelle Übersicht zum Debugging eines Containers, für komplexere Protokolle sollte der Docker Daemon konsultiert werden.</p>\r\n <p><b>Externe Logs</b> werden via API externer Applikationen bezogen.</p>\r\n <p><b>Statische Logs</b> sind weitestgehend Aktivitätsprotokolle, die nicht in den Docker Daemon geschrieben werden, jedoch permanent verfügbar sein müssen (ausgeschlossen API Logs).</p>",
|
||||
"login_time": "Zeit",
|
||||
"logs": "Protokolle",
|
||||
"memory": "Arbeitsspeicher",
|
||||
"online_users": "Benutzer online",
|
||||
"restart_container": "Neustart",
|
||||
"service": "Dienst",
|
||||
@ -515,7 +517,10 @@
|
||||
"static_logs": "Statische Logs",
|
||||
"success": "Erfolg",
|
||||
"system_containers": "System & Container",
|
||||
"timezone": "Zeitzone",
|
||||
"uptime": "Uptime",
|
||||
"update_available": "Es ist ein Update verfügbar",
|
||||
"no_update_available": "Das System ist auf aktuellem Stand",
|
||||
"username": "Benutzername"
|
||||
},
|
||||
"diagnostics": {
|
||||
|
@ -494,6 +494,7 @@
|
||||
"containers_info": "Container information",
|
||||
"container_running": "Running",
|
||||
"container_stopped": "Stopped",
|
||||
"current_time": "System Time",
|
||||
"disk_usage": "Disk usage",
|
||||
"docs": "Docs",
|
||||
"external_logs": "External logs",
|
||||
@ -504,6 +505,7 @@
|
||||
"log_info": "<p>mailcow <b>in-memory logs</b> are collected in Redis lists and trimmed to LOG_LINES (%d) every minute to reduce hammering.\r\n <br>In-memory logs are not meant to be persistent. All applications that log in-memory, also log to the Docker daemon and therefore to the default logging driver.\r\n <br>The in-memory log type should be used for debugging minor issues with containers.</p>\r\n <p><b>External logs</b> are collected via API of the given application.</p>\r\n <p><b>Static logs</b> are mostly activity logs, that are not logged to the Dockerd but still need to be persistent (except for API logs).</p>",
|
||||
"login_time": "Time",
|
||||
"logs": "Logs",
|
||||
"memory": "Memory",
|
||||
"online_users": "Users online",
|
||||
"restart_container": "Restart",
|
||||
"service": "Service",
|
||||
@ -515,7 +517,10 @@
|
||||
"static_logs": "Static logs",
|
||||
"success": "Success",
|
||||
"system_containers": "System & Containers",
|
||||
"timezone": "Timezone",
|
||||
"uptime": "Uptime",
|
||||
"update_available": "There is an update available",
|
||||
"no_update_available": "The System is on the latest version",
|
||||
"username": "Username"
|
||||
},
|
||||
"diagnostics": {
|
||||
|
@ -89,39 +89,6 @@
|
||||
<br>
|
||||
</div>
|
||||
|
||||
<legend style="cursor:pointer;margin-top:40px" data-bs-target="#license" unselectable="on" data-bs-toggle="collapse">
|
||||
<i style="font-size:10pt;" class="bi bi-plus-square"></i> {{ lang.admin.guid_and_license }}
|
||||
</legend>
|
||||
<hr />
|
||||
<div id="license" class="collapse">
|
||||
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
||||
<div class="row">
|
||||
<label class="control-label col-sm-3" for="guid">{{ lang.admin.guid }}:</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="bi bi-suit-heart{% if gal.valid == true %}-fill text-danger{% endif %}"></i>
|
||||
</span>
|
||||
<input type="text" id="guid" class="form-control" value="{{ license_guid }}" readonly>
|
||||
</div>
|
||||
<p class="text-muted">
|
||||
{{ lang.admin.customer_id }}: {{ gal.c|default('?')|raw }} -
|
||||
{{ lang.admin.service_id }}: {{ gal.s|default('?')|raw }} -
|
||||
{{ lang.admin.sal_level }}: {{ gal.m|default('?')|raw }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="offset-sm-3 col-sm-9">
|
||||
<p class="text-muted">{{ lang.admin.license_info|raw }}</p>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm d-block d-sm-inline btn-success" name="license_validate_now" type="submit" href="#">{{ lang.admin.validate_license_now }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<legend style="cursor:pointer;margin-top:20px" data-bs-target="#admin_api" unselectable="on" data-bs-toggle="collapse">
|
||||
<i style="font-size:10pt;" class="bi bi-plus-square"></i> API
|
||||
</legend>
|
||||
|
@ -28,26 +28,125 @@
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="tab-content" style="padding-top:20px">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div role="tabpanel" class="tab-pane active" id="tab-containers">
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ lang.debug.disk_usage }}</h3>
|
||||
<h3 class="card-title">mailcow</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<p><i class="bi bi-hdd-fill"></i> {{ vmail_df[0] }}</p>
|
||||
<p>{{ vmail_df[2] }} / {{ vmail_df[1] }} ({{ vmail_df[4] }})</p>
|
||||
<div class="col-sm-12 col-md-4 d-flex flex-column">
|
||||
<img class="img-responsive" alt="mailcow-logo" src="{{ logo|default('/img/cow_mailcow.svg') }}" style="max-height: 200px;">
|
||||
<div style="margin-top: 60px;">
|
||||
<span class="d-block"><i class="bi bi-hdd-fill"></i> {{ vmail_df[0] }}</span>
|
||||
<span class="d-block">{{ vmail_df[2] }} / {{ vmail_df[1] }} ({{ vmail_df[4] }})</span>
|
||||
</div>
|
||||
<div class="col-sm-9">
|
||||
<div class="mt-2 mb-4">
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-info" role="progressbar" style="width:{{ vmail_df[4] }}"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-12 col-md-8">
|
||||
<div class="table-responsive" style="margin-top: 10px;">
|
||||
<table class="table table-striped table-condensed">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Hostname</td>
|
||||
<td><div>
|
||||
<p><b>{{ hostname }}</b></p>
|
||||
</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Version</td>
|
||||
<td><div>
|
||||
<p><b>{{ mailcow_info.version_tag }}</b></p>
|
||||
<p id="mailcow_update"></p>
|
||||
</div></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Changelog</td>
|
||||
<td><a href="{{ mailcow_info.project_url }}/releases/tag/{{ mailcow_info.version_tag }}" target="_blank">
|
||||
{{ mailcow_info.project_url }}/releases/tag/{{ mailcow_info.version_tag }}
|
||||
</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang.debug.current_time }}</td>
|
||||
<td id="host_date">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang.debug.timezone }}</td>
|
||||
<td>{{ timezone }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang.debug.uptime }}</td>
|
||||
<td id="host_uptime">-</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>CPU</td>
|
||||
<td>
|
||||
Cores <span id="host_cpu_cores">-</span><br />
|
||||
Usage <span id="host_cpu_usage"></span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{ lang.debug.memory }}</td>
|
||||
<td>
|
||||
Total <span id="host_memory_total">-</span><br />
|
||||
Usage <span id="host_memory_usage"></span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-6">
|
||||
<canvas id="net_io_chart" width="400" height="200"></canvas>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<canvas id="disk_io_chart" width="400" height="200"></canvas>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-12">
|
||||
<legend class="mt-4">
|
||||
{{ lang.admin.guid_and_license }}
|
||||
</legend>
|
||||
<hr />
|
||||
<div id="license">
|
||||
<form class="form-horizontal" autocapitalize="none" autocorrect="off" role="form" method="post">
|
||||
<div class="row">
|
||||
<label class="control-label col-sm-3" for="guid">{{ lang.admin.guid }}:</label>
|
||||
<div class="col-sm-9">
|
||||
<div class="input-group">
|
||||
<span class="input-group-text">
|
||||
<i class="bi bi-suit-heart{% if gal.valid == true %}-fill text-danger{% endif %}"></i>
|
||||
</span>
|
||||
<input type="text" id="guid" class="form-control" value="{{ license_guid }}" readonly>
|
||||
</div>
|
||||
<p class="text-muted">
|
||||
{{ lang.admin.customer_id }}: {{ gal.c|default('?')|raw }} -
|
||||
{{ lang.admin.service_id }}: {{ gal.s|default('?')|raw }} -
|
||||
{{ lang.admin.sal_level }}: {{ gal.m|default('?')|raw }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="offset-sm-3 col-sm-9">
|
||||
<p class="text-muted">{{ lang.admin.license_info|raw }}</p>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm d-block d-sm-inline btn-success" name="license_validate_now" type="submit" href="#">{{ lang.admin.validate_license_now }}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">{{ lang.debug.solr_status }}</h3>
|
||||
@ -124,6 +223,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-postfix-logs">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">Postfix
|
||||
<div class="btn-group ms-auto">
|
||||
@ -139,6 +239,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-ui">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex"> Mailcow UI
|
||||
<div class="btn-group ms-auto">
|
||||
@ -154,6 +255,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-sasl">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">SASL
|
||||
<div class="btn-group ms-auto">
|
||||
@ -169,6 +271,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-dovecot-logs">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">Dovecot
|
||||
<div class="btn-group ms-auto">
|
||||
@ -184,6 +287,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-sogo-logs">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">SOGo
|
||||
<div class="btn-group ms-auto">
|
||||
@ -199,6 +303,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-netfilter-logs">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">Netfilter
|
||||
<div class="btn-group ms-auto">
|
||||
@ -214,6 +319,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-rspamd-history">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">Rspamd history
|
||||
<div class="btn-group ms-auto">
|
||||
@ -234,6 +340,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-autodiscover-logs">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">Autodiscover
|
||||
<div class="btn-group ms-auto">
|
||||
@ -249,6 +356,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-watchdog-logs">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">Watchdog
|
||||
<div class="btn-group ms-auto">
|
||||
@ -264,6 +372,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-acme-logs">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">ACME
|
||||
<div class="btn-group ms-auto">
|
||||
@ -279,6 +388,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-api-logs">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">API
|
||||
<div class="btn-group ms-auto">
|
||||
@ -294,6 +404,7 @@
|
||||
</div>
|
||||
|
||||
<div role="tabpanel" class="tab-pane" id="tab-api-rl">
|
||||
<div class="debug-log-info">{{ lang.debug.log_info|format(log_lines+1)|raw }}</div>
|
||||
<div class="card panel-xs-lg">
|
||||
<div class="card-header d-flex">Ratelimits
|
||||
<div class="btn-group ms-auto">
|
||||
@ -315,6 +426,7 @@
|
||||
|
||||
<script type='text/javascript'>
|
||||
var lang = {{ lang_admin|raw }};
|
||||
var lang_debug = {{ lang_debug|raw }};
|
||||
var lang_datatables = {{ lang_datatables|raw }};
|
||||
var csrf_token = '{{ csrf_token }}';
|
||||
var log_pagination_size = '{{ log_pagination_size }}';
|
||||
|
@ -509,7 +509,7 @@ services:
|
||||
- watchdog
|
||||
|
||||
dockerapi-mailcow:
|
||||
image: mailcow/dockerapi:1.42
|
||||
image: mailcow/dockerapi:1.43
|
||||
security_opt:
|
||||
- label=disable
|
||||
restart: always
|
||||
|
Loading…
Reference in New Issue
Block a user