mirror of https://git.48k.eu/ogcp
ogcp: show cache contents in client details
Show cache contents in client details for a more complete view of the client's state. Move the cache inspector code to its own template for reusability.master
parent
5af2b3738b
commit
314a173b6c
|
@ -54,151 +54,8 @@
|
|||
{{ _('Delete') }}
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<br>
|
||||
|
||||
<div class="card text-center">
|
||||
<div class="card-header">
|
||||
{{ _('Detailed cache usage') }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<label for="cacheSelect">Choose a client:</label>
|
||||
<select id="cacheSelect" onchange="onClientSelected()">
|
||||
{% for client_ip in ip_list %}
|
||||
<option value="{{ client_ip }}">{{ client_ip }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
|
||||
<ul class="list-group list-group-horizontal">
|
||||
<li class="list-group-item w-50">
|
||||
<canvas id="cacheChart" class="mb-2"></canvas>
|
||||
</li>
|
||||
<li class="list-group-item w-50">
|
||||
<p>{{ _('Images in cache:') }}</p>
|
||||
<div id="cacheList"></div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-group list-group-horizontal">
|
||||
<li class="list-group-item w-50">
|
||||
{{ _('Disk size') }}
|
||||
</li>
|
||||
<li class="list-group-item w-50">
|
||||
{{ _('used') }} (%)
|
||||
</li>
|
||||
<li class="list-group-item w-50">
|
||||
{{ _('available') }} (%)
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-group list-group-horizontal">
|
||||
<li id="totalCacheItem" class="list-group-item w-50"></li>
|
||||
<li id="usedCacheItem" class="list-group-item w-50"></li>
|
||||
<li id="freeCacheItem" class="list-group-item w-50"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="{{ url_for('static', filename='AdminLTE/plugins/jquery/jquery.min.js') }}"></script>
|
||||
<!-- ChartJS -->
|
||||
<script src="{{ url_for('static', filename='AdminLTE/plugins/chart.js/Chart.min.js') }}"></script>
|
||||
<script>
|
||||
var cacheChartConfig = {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Used', 'Available'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Disk usage',
|
||||
data: [
|
||||
0,
|
||||
1,
|
||||
],
|
||||
backgroundColor: [
|
||||
'rgb(255, 99, 132)',
|
||||
'rgb(54, 162, 235)',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Chart.js Doughnut Chart'
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
var cacheChart = new Chart(
|
||||
document.getElementById('cacheChart'),
|
||||
cacheChartConfig,
|
||||
);
|
||||
|
||||
var storageData = {{ storage_data|tojson|safe }};
|
||||
var imageData = {{ images_data|tojson|safe }};
|
||||
var clientImages = {{ client_images|tojson|safe }};
|
||||
|
||||
function onClientSelected() {
|
||||
var selectElement = document.getElementById("cacheSelect");
|
||||
var selectedOption = selectElement.options[selectElement.selectedIndex].text;
|
||||
updateChart(selectedOption);
|
||||
}
|
||||
|
||||
function toGiB(v, decimals) {
|
||||
return (v / Math.pow(2, 30)).toFixed(decimals);
|
||||
}
|
||||
|
||||
function updateChart(ip) {
|
||||
var totalCache = toGiB(storageData[ip].total, 3);
|
||||
var usedCache = toGiB(storageData[ip].used, 3);
|
||||
var freeCache = toGiB(storageData[ip].total - storageData[ip].used, 3)
|
||||
|
||||
cacheChart.data.datasets[0].data = [
|
||||
usedCache,
|
||||
freeCache,
|
||||
]
|
||||
cacheChart.update();
|
||||
|
||||
var totalCacheItem = document.getElementById("totalCacheItem");
|
||||
totalCacheItem.innerHTML = totalCache + " GiB";
|
||||
|
||||
var usedCacheItem = document.getElementById("usedCacheItem");
|
||||
usedCacheItem.innerHTML = usedCache + " GiB (" + Math.round((usedCache / totalCache) * 100) + "%)";
|
||||
|
||||
var freeCacheItem = document.getElementById("freeCacheItem");
|
||||
freeCacheItem.innerHTML = freeCache + " GiB (" + Math.round((freeCache / totalCache) * 100) + "%)";
|
||||
|
||||
var cacheList = document.getElementById("cacheList");
|
||||
cacheList.innerHTML = "";
|
||||
clientImages[ip].forEach(function(img) {
|
||||
cacheList.innerHTML += imageData[img]["name"] + " (" + (imageData[img]["size"] / Math.pow(2, 20)).toFixed(3) + " MiB)<br>";
|
||||
});
|
||||
}
|
||||
|
||||
updateChart("{{ ip_list[0] }}");
|
||||
|
||||
// Update pill data
|
||||
$('.badge-pill').each(function(index) {
|
||||
for (var ip in storageData) {
|
||||
if ($(this).html().includes(ip)) {
|
||||
var totalCache = storageData[ip].total;
|
||||
var usedCache = storageData[ip].used;
|
||||
var freeCache = toGiB(totalCache - usedCache, 1)
|
||||
$(this).html($(this).html() + '<br>free: ' + freeCache + ' GiB');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
{% else %}
|
||||
<div class="card text-center p-3">
|
||||
<b>{{ _('Cache is currently empty in the selected client(s)') }}</b>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% include 'cache_inspector.html' %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
{% block nav_client_add %}active{% endblock %}
|
||||
{% block content %}
|
||||
|
||||
{% set ip_list = [form.ip.data] %}
|
||||
|
||||
<h2 class="mx-5 subhead-heading">{{_('Client details')}}</h2>
|
||||
|
||||
<div class="container mt-5">
|
||||
|
@ -106,4 +108,6 @@
|
|||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% include 'cache_inspector.html' %}
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
{% if storage_data is defined and images_data is defined and client_images is defined and ip_list is defined %}
|
||||
|
||||
<br>
|
||||
{% if images_data|length > 0 %}
|
||||
<div class="card text-center">
|
||||
<div class="card-header">
|
||||
{{ _('Detailed cache usage') }}
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if ip_list|length > 1 %}
|
||||
<label for="cacheSelect">Choose a client:</label>
|
||||
<select id="cacheSelect" onchange="onClientSelected()">
|
||||
{% for client_ip in ip_list %}
|
||||
<option value="{{ client_ip }}">{{ client_ip }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
{% endif %}
|
||||
|
||||
<ul class="list-group list-group-horizontal">
|
||||
<li class="list-group-item w-50">
|
||||
<canvas id="cacheChart" class="mb-2"></canvas>
|
||||
</li>
|
||||
<li class="list-group-item w-50">
|
||||
<p>{{ _('Images in cache:') }}</p>
|
||||
<div id="cacheList"></div>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-group list-group-horizontal">
|
||||
<li class="list-group-item w-50">
|
||||
{{ _('Disk size') }}
|
||||
</li>
|
||||
<li class="list-group-item w-50">
|
||||
{{ _('used') }} (%)
|
||||
</li>
|
||||
<li class="list-group-item w-50">
|
||||
{{ _('available') }} (%)
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="list-group list-group-horizontal">
|
||||
<li id="totalCacheItem" class="list-group-item w-50"></li>
|
||||
<li id="usedCacheItem" class="list-group-item w-50"></li>
|
||||
<li id="freeCacheItem" class="list-group-item w-50"></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- jQuery -->
|
||||
<script src="{{ url_for('static', filename='AdminLTE/plugins/jquery/jquery.min.js') }}"></script>
|
||||
<!-- ChartJS -->
|
||||
<script src="{{ url_for('static', filename='AdminLTE/plugins/chart.js/Chart.min.js') }}"></script>
|
||||
<script>
|
||||
var cacheChartConfig = {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Used', 'Available'],
|
||||
datasets: [
|
||||
{
|
||||
label: 'Disk usage',
|
||||
data: [
|
||||
0,
|
||||
1,
|
||||
],
|
||||
backgroundColor: [
|
||||
'rgb(255, 99, 132)',
|
||||
'rgb(54, 162, 235)',
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
title: {
|
||||
display: true,
|
||||
text: 'Chart.js Doughnut Chart'
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
var cacheChart = new Chart(
|
||||
document.getElementById('cacheChart'),
|
||||
cacheChartConfig,
|
||||
);
|
||||
|
||||
var storageData = {{ storage_data|tojson|safe }};
|
||||
var imageData = {{ images_data|tojson|safe }};
|
||||
var clientImages = {{ client_images|tojson|safe }};
|
||||
|
||||
function onClientSelected() {
|
||||
var selectElement = document.getElementById("cacheSelect");
|
||||
var selectedOption = selectElement.options[selectElement.selectedIndex].text;
|
||||
updateChart(selectedOption);
|
||||
}
|
||||
|
||||
function toGiB(v, decimals) {
|
||||
return (v / Math.pow(2, 30)).toFixed(decimals);
|
||||
}
|
||||
|
||||
function updateChart(ip) {
|
||||
var totalCache = toGiB(storageData[ip].total, 3);
|
||||
var usedCache = toGiB(storageData[ip].used, 3);
|
||||
var freeCache = toGiB(storageData[ip].total - storageData[ip].used, 3)
|
||||
|
||||
cacheChart.data.datasets[0].data = [
|
||||
usedCache,
|
||||
freeCache,
|
||||
]
|
||||
cacheChart.update();
|
||||
|
||||
var totalCacheItem = document.getElementById("totalCacheItem");
|
||||
totalCacheItem.innerHTML = totalCache + " GiB";
|
||||
|
||||
var usedCacheItem = document.getElementById("usedCacheItem");
|
||||
usedCacheItem.innerHTML = usedCache + " GiB (" + Math.round((usedCache / totalCache) * 100) + "%)";
|
||||
|
||||
var freeCacheItem = document.getElementById("freeCacheItem");
|
||||
freeCacheItem.innerHTML = freeCache + " GiB (" + Math.round((freeCache / totalCache) * 100) + "%)";
|
||||
|
||||
var cacheList = document.getElementById("cacheList");
|
||||
cacheList.innerHTML = "";
|
||||
if (clientImages[ip]) {
|
||||
clientImages[ip].forEach(function(img) {
|
||||
cacheList.innerHTML += imageData[img]["name"] + " (" + (imageData[img]["size"] / Math.pow(2, 20)).toFixed(3) + " MiB)<br>";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateChart("{{ ip_list[0] }}");
|
||||
|
||||
// Update pill data
|
||||
$('.badge-pill').each(function(index) {
|
||||
for (var ip in storageData) {
|
||||
if ($(this).html().includes(ip)) {
|
||||
var totalCache = storageData[ip].total;
|
||||
var usedCache = storageData[ip].used;
|
||||
var freeCache = toGiB(totalCache - usedCache, 1)
|
||||
$(this).html($(this).html() + '<br>free: ' + freeCache + ' GiB');
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% else %}
|
||||
<div class="card text-center p-3">
|
||||
<b>{{ _('No cache contents') }}</b>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
|
@ -354,6 +354,34 @@ def hash_password(pwd):
|
|||
|
||||
return pwd_hash
|
||||
|
||||
def get_cache_info(clients_info, storage_data, images_data, client_images):
|
||||
for client_info in clients_info:
|
||||
ip = client_info['ip']
|
||||
cache_size = int(client_info['cache_size'])
|
||||
used_cache = 0
|
||||
for image_info in client_info['images']:
|
||||
image_name = image_info['name']
|
||||
checksum = image_info['checksum']
|
||||
used_cache += int(image_info['size'])
|
||||
img_identifier = f'{image_name}.{checksum}'
|
||||
|
||||
if ip in client_images:
|
||||
client_images[ip].append(img_identifier)
|
||||
else:
|
||||
client_images[ip] = [img_identifier]
|
||||
|
||||
if img_identifier in images_data:
|
||||
images_data[img_identifier]['clients'].append(ip)
|
||||
else:
|
||||
images_data[img_identifier] = {
|
||||
'clients': [ip],
|
||||
'size': int(image_info['size']),
|
||||
'name': image_name,
|
||||
'checksum': checksum
|
||||
}
|
||||
|
||||
storage_data[ip] = {'used': used_cache,
|
||||
'total': cache_size}
|
||||
|
||||
def authenticate_user(username, pwd):
|
||||
for user in app.config['USERS']:
|
||||
|
@ -1171,9 +1199,9 @@ def action_client_cache():
|
|||
if not validate_elements(ips):
|
||||
return redirect(url_for('commands'))
|
||||
|
||||
server = get_server_from_clients(ips_list)
|
||||
form.ips.data = ' '.join(ips_list)
|
||||
|
||||
server = get_server_from_clients(ips_list)
|
||||
r = server.get('/cache/list', payload={'clients': ips_list})
|
||||
if not r:
|
||||
return ogserver_down('commands')
|
||||
|
@ -1189,33 +1217,8 @@ def action_client_cache():
|
|||
storage_data = {}
|
||||
images_data = {}
|
||||
client_images = {}
|
||||
for client_info in clients_info:
|
||||
ip = client_info['ip']
|
||||
cache_size = int(client_info['cache_size'])
|
||||
used_cache = 0
|
||||
for image_info in client_info['images']:
|
||||
image_name = image_info['name']
|
||||
checksum = image_info['checksum']
|
||||
used_cache += int(image_info['size'])
|
||||
img_identifier = f'{image_name}.{checksum}'
|
||||
|
||||
if ip in client_images:
|
||||
client_images[ip].append(img_identifier)
|
||||
else:
|
||||
client_images[ip] = [img_identifier]
|
||||
|
||||
if img_identifier in images_data:
|
||||
images_data[img_identifier]['clients'].append(ip)
|
||||
else:
|
||||
images_data[img_identifier] = {
|
||||
'clients': [ip],
|
||||
'size': int(image_info['size']),
|
||||
'name': image_name,
|
||||
'checksum': checksum
|
||||
}
|
||||
|
||||
storage_data[ip] = {'used': used_cache,
|
||||
'total': cache_size}
|
||||
get_cache_info(clients_info, storage_data, images_data, client_images)
|
||||
|
||||
for img_identifier in images_data:
|
||||
image_data = images_data[img_identifier]
|
||||
|
@ -1321,10 +1324,31 @@ def action_client_info():
|
|||
else:
|
||||
entry['image'] = ""
|
||||
|
||||
r = server.get('/cache/list', payload={'clients': [ip]})
|
||||
if not r:
|
||||
return ogserver_down('commands')
|
||||
if r.status_code != requests.codes.ok:
|
||||
return ogserver_error('commands')
|
||||
|
||||
clients_info = r.json()['clients']
|
||||
|
||||
if not clients_info:
|
||||
flash(_('ogServer returned an empty client list'), category='error')
|
||||
return redirect(url_for('commands'))
|
||||
|
||||
storage_data = {}
|
||||
images_data = {}
|
||||
client_images = {}
|
||||
|
||||
get_cache_info(clients_info, storage_data, images_data, client_images)
|
||||
|
||||
scopes, clients = get_scopes(set(ips))
|
||||
|
||||
return render_template('actions/client_details.html', form=form,
|
||||
parent="commands.html", scopes=scopes, setup=setup)
|
||||
parent="commands.html", scopes=scopes, setup=setup,
|
||||
images_data=images_data,
|
||||
storage_data=storage_data,
|
||||
client_images=client_images)
|
||||
|
||||
@app.route('/action/client/update', methods=['GET', 'POST'])
|
||||
@login_required
|
||||
|
@ -1455,6 +1479,7 @@ def action_client_update():
|
|||
entry['image'] = ""
|
||||
|
||||
form.submit.render_kw = {"formaction": url_for('action_client_update')}
|
||||
|
||||
return render_template('actions/client_details.html', form=form,
|
||||
parent="scopes.html", scopes=scopes, setup=setup)
|
||||
|
||||
|
|
Loading…
Reference in New Issue