summaryrefslogtreecommitdiffstats
path: root/ogcp/templates/actions
diff options
context:
space:
mode:
authorAlejandro Sirgo Rica <asirgo@soleta.eu>2024-05-16 12:38:28 +0200
committerAlejandro Sirgo Rica <asirgo@soleta.eu>2024-05-30 15:50:32 +0200
commit9cb3a952b4ceb3c33e8481250179023fdf5ceebb (patch)
tree6d590a2b18869a20370ae4513cbd4ad4559fb603 /ogcp/templates/actions
parentf896107375cc2a47a5c4eb3095c6c365d8eed98e (diff)
ogcp: add cache management page
Implement cache management through cache/list and cache/delete API REST methods. The view corresponds to the URL action/cache and contains three main parts: - Free cache available in the client's bubbles. - Image selection form to request the deletion and view the client with that image in cache. - Cache details per client to see total, used and available cache. The main usecase for the view is the removal of cache to make room for new images in clients with slow connections. Checking clients where available cache space is not enough to hold the new image and then requesting removal of specific images. The html template receives the following structures from the view: storage_data[ip] = {'used': 223452345, 'total': 2345234523452} client_images[ip] = [f'{image_name}.{image_checksum}', ...] image_data[f'{image_name}.{image_checksum}'] = { clients: ['192.168.0.1', ...], size: 34534530850, name: image_name, }
Diffstat (limited to 'ogcp/templates/actions')
-rw-r--r--ogcp/templates/actions/cache.html192
1 files changed, 192 insertions, 0 deletions
diff --git a/ogcp/templates/actions/cache.html b/ogcp/templates/actions/cache.html
new file mode 100644
index 0000000..07f5b16
--- /dev/null
+++ b/ogcp/templates/actions/cache.html
@@ -0,0 +1,192 @@
+{% extends 'commands.html' %}
+{% import "bootstrap/wtf.html" as wtf %}
+
+{% set sidebar_state = 'disabled' %}
+{% set btn_back = true %}
+
+{% block nav_client %} active{% endblock %}
+{% block nav_client_cache %} active{% endblock %}
+{% block content %}
+
+{% set ip_list = form.ips.data.split(' ') %}
+{% set ip_count = ip_list | length %}
+<h1 class="m-5">
+ {{ _('Manage Cache') }}
+</h1>
+
+<p>{{ _('Check free cache space in the client\'s bubbles:') }}</p>
+
+{{ macros.cmd_selected_clients(selected_clients) }}
+
+<p>{{ _('Select the images to be deleted:') }}</p>
+
+<form class="form-inline" method="POST" id="cacheForm">
+ <table class="table table-hover">
+ <thead class="thead-light">
+ <tr>
+ <th>{{ _('Image') }}</th>
+ <th>{{ _('Clients') }}</th>
+ </tr>
+ </thead>
+
+ <tbody data-target="cache-fieldset" id="cacheTable" class="text-left">
+ {{ form.hidden_tag() }}
+ {% for image in form.images %}
+ <tr data-toggle="fieldset-entry">
+ <td class="radio-container">
+ {{ image.selected(class_="form-control") }}
+ {{ image.image_name() }}
+ {{ image.clients() }}
+ <b>{{ image.image_name.data }} ({{ (images_data[image.selected.label.text]['size'] | int / 2**20)|round(3) }} MiB)</b>
+ </td>
+ <td>{{ ', '.join(images_data[image.selected.label.text]['clients']) }}</td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+ <button class="btn btn-danger" form="cacheForm">
+ {{ _('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>
+
+{% endblock %}