diff options
author | Alejandro Sirgo Rica <asirgo@soleta.eu> | 2024-07-16 11:41:41 +0200 |
---|---|---|
committer | Alejandro Sirgo Rica <asirgo@soleta.eu> | 2024-07-16 12:55:31 +0200 |
commit | 314a173b6cc3125190786e43158b7a60063f33bf (patch) | |
tree | 626c3d0e2820d4020e2bf4fb641320a0d5d6827d | |
parent | 5af2b3738be739796ae98ce68a3ea9162c7248bf (diff) |
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.
-rw-r--r-- | ogcp/templates/actions/cache.html | 147 | ||||
-rw-r--r-- | ogcp/templates/actions/client_details.html | 4 | ||||
-rw-r--r-- | ogcp/templates/cache_inspector.html | 152 | ||||
-rw-r--r-- | ogcp/views.py | 81 |
4 files changed, 211 insertions, 173 deletions
diff --git a/ogcp/templates/actions/cache.html b/ogcp/templates/actions/cache.html index 46a8699..d64aebe 100644 --- a/ogcp/templates/actions/cache.html +++ b/ogcp/templates/actions/cache.html @@ -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 %} diff --git a/ogcp/templates/actions/client_details.html b/ogcp/templates/actions/client_details.html index 0362d5b..070e4d6 100644 --- a/ogcp/templates/actions/client_details.html +++ b/ogcp/templates/actions/client_details.html @@ -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 %} diff --git a/ogcp/templates/cache_inspector.html b/ogcp/templates/cache_inspector.html new file mode 100644 index 0000000..42dd983 --- /dev/null +++ b/ogcp/templates/cache_inspector.html @@ -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 %}
\ No newline at end of file diff --git a/ogcp/views.py b/ogcp/views.py index 2a6c244..2c8f650 100644 --- a/ogcp/views.py +++ b/ogcp/views.py @@ -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) |