diff options
-rw-r--r-- | ogcp/static/js/ogcp.js | 44 | ||||
-rw-r--r-- | ogcp/templates/actions/setup.html | 159 | ||||
-rw-r--r-- | ogcp/templates/base.html | 2 | ||||
-rw-r--r-- | ogcp/views.py | 45 |
4 files changed, 188 insertions, 62 deletions
diff --git a/ogcp/static/js/ogcp.js b/ogcp/static/js/ogcp.js index b70b49d..9e6fd4f 100644 --- a/ogcp/static/js/ogcp.js +++ b/ogcp/static/js/ogcp.js @@ -341,50 +341,6 @@ function unfoldAll() { $('#scopes .collapse').collapse('show'); } -function AddPartition(evt) { - const target = $($(evt).data("target")); - const oldrow = target.find("[data-toggle=fieldset-entry]:last"); - const row = oldrow.clone(true, true); - const elem_id = row.find(".form-control")[0].id; - const elem_num = parseInt(elem_id.replace(/(.*)-(\d{1,4})/m, '$2')) + 1; - // Increment WTForms unique identifiers - row.find('.form-control').each(function() { - const id = $(this).attr('id').replace(/(.*)-(\d{1,4})-(.*)/, `$1-${elem_num}-$3`); - $(this).attr('name', id).attr('id', id).val('').removeAttr("checked"); - }); - let part_field = row.find('td').filter(':first')[0]; - part_field.innerText = elem_num + 1; - row.show(); - oldrow.after(row); -} - -function RemovePartition(evt) { - const target = $(evt).parent().parent(); - const table = target.parent(); - - if (table[0].children.length > 1) { - target.remove(); - // Reassign rows ids - table.find('tr').each(function(index) { - function update_references() { - const id = $(this).attr('id').replace(/(.*)-(\d{1,4})-(.*)/, `$1-${index}-$3`); - $(this).attr('name', id).attr('id', id); - } - - let part_field = $(this).find('td').filter(':first')[0]; - part_field.innerText = index + 1; - $(this).find('input').filter(':first').each(update_references); - $(this).find('.form-control').each(update_references); - }); - } else { - table.find('tr').each(function(index) { - $(this).find('.form-control').each(function() { - $(this).val('').removeAttr("checked"); - }); - }); - } -} - function checkImageServer() { const images = $('input:checkbox[form|="imagesForm"][name!="image-server"]') diff --git a/ogcp/templates/actions/setup.html b/ogcp/templates/actions/setup.html index fafcb87..934d72f 100644 --- a/ogcp/templates/actions/setup.html +++ b/ogcp/templates/actions/setup.html @@ -58,7 +58,7 @@ <td>{{ partition.partition.data }}</td> <td>{{ partition.part_type(class_="form-control") }}</td> <td>{{ partition.fs(class_="form-control") }}</td> - <td>{{ partition.size(class_="form-control") }}</td> + <td>{{ partition.size(class_="form-control", oninput="handleEdit(this)") }}</td> <td> <button class="btn btn-danger" type="button" onclick="RemovePartition(this)"> {{ _('Remove') }} @@ -78,4 +78,161 @@ {{ _('Accept') }} </button> +<div class="card text-center"> + <div class="card-header"> + {{ _('Partition graph') }} + </div> + <div class="card-body mx-auto col-7"> + <canvas id="partitionChart" class="mb-2"></canvas> + </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> + const usedColor = 'rgb(255, 99, 132)'; + const freeColor = 'rgb(54, 162, 235)'; + let diskSize = {{ disk_size }}; + + let chartConfig = { + type: 'doughnut', + data: { + labels: [], + datasets: [ + { + label: 'Partitions', + data: [], + backgroundColor: [], + }, + ], + }, + options: { + responsive: true, + plugins: { + legend: { + position: 'top', + }, + title: { + display: true, + text: 'Chart.js Doughnut Chart' + }, + }, + }, + }; + + let partitionChart = new Chart( + document.getElementById('partitionChart'), + chartConfig, + ); + + function addPartitionToChart(chart, label, value, bgColor) { + chart.data.labels.push(label) + chart.data.datasets[0].data.push(value); + chart.data.datasets[0].backgroundColor.push(bgColor); + } + + function resetChart() { + partitionChart.data.labels = []; + partitionChart.data.datasets[0].data = []; + partitionChart.data.datasets[0].backgroundColor = []; + } + + function updateChart() { + resetChart(); + + let freeSpace = diskSize; + let partNum = 1; + $('#partitionsTable tr').each(function() { + let partitionSize = parseInt($(this).find('td').eq(3).find('input').val().trim()); + if (isNaN(partitionSize)) { + partitionSize = 0; + } + freeSpace -= partitionSize; + addPartitionToChart(partitionChart, 'Partition ' + partNum, partitionSize, usedColor); + partNum++; + }); + + addPartitionToChart(partitionChart, 'Free', Math.max(freeSpace, 0), freeColor); + partitionChart.update(); + } + + $(document).ready(function() { + updateChart(); + }); + + function handleEdit(element) { + const numericValue = parseInt(element.value); + + if (isNaN(numericValue)) { + updateChart(); + return; + } + + let freeSpace = diskSize; + $('#partitionsTable tr').each(function() { + let partitionSize = parseInt($(this).find('td').eq(3).find('input').val().trim()); + if (isNaN(partitionSize)) { + partitionSize = 0; + } + freeSpace -= partitionSize; + }); + + if (freeSpace < 0) { + element.value = numericValue + freeSpace; + } + + updateChart(); + } + + function AddPartition(evt) { + const target = $($(evt).data("target")); + const oldrow = target.find("[data-toggle=fieldset-entry]:last"); + const row = oldrow.clone(true, true); + const elem_id = row.find(".form-control")[0].id; + const elem_num = parseInt(elem_id.replace(/(.*)-(\d{1,4})/m, '$2')) + 1; + // Increment WTForms unique identifiers + row.find('.form-control').each(function() { + const id = $(this).attr('id').replace(/(.*)-(\d{1,4})-(.*)/, `$1-${elem_num}-$3`); + $(this).attr('name', id).attr('id', id).val('').removeAttr("checked"); + }); + let part_field = row.find('td').filter(':first')[0]; + part_field.innerText = elem_num + 1; + row.show(); + oldrow.after(row); + + updateChart(); + } + + function RemovePartition(evt) { + const target = $(evt).parent().parent(); + const table = target.parent(); + + if (table[0].children.length > 1) { + target.remove(); + // Reassign rows ids + table.find('tr').each(function(index) { + function update_references() { + const id = $(this).attr('id').replace(/(.*)-(\d{1,4})-(.*)/, `$1-${index}-$3`); + $(this).attr('name', id).attr('id', id); + } + + let part_field = $(this).find('td').filter(':first')[0]; + part_field.innerText = index + 1; + $(this).find('input').filter(':first').each(update_references); + $(this).find('.form-control').each(update_references); + }); + } else { + table.find('tr').each(function(index) { + $(this).find('.form-control').each(function() { + $(this).val('').removeAttr("checked"); + }); + }); + } + updateChart(); + } + +</script> + {% endblock %} diff --git a/ogcp/templates/base.html b/ogcp/templates/base.html index 0099c27..cd030e8 100644 --- a/ogcp/templates/base.html +++ b/ogcp/templates/base.html @@ -108,7 +108,7 @@ <!-- ChartJS --> <script src="{{ url_for('static', filename='AdminLTE/plugins/chart.js/Chart.min.js') }}"></script> - <script src="{{ url_for('static', filename='js/ogcp.js') }}?v=15"></script> + <script src="{{ url_for('static', filename='js/ogcp.js') }}?v=16"></script> <script> // error messages diff --git a/ogcp/views.py b/ogcp/views.py index 8b544eb..007ca91 100644 --- a/ogcp/views.py +++ b/ogcp/views.py @@ -682,6 +682,7 @@ def action_setup_select(): @login_required def action_setup_show(): args = request.args.copy() + disk_size = None default_disk = 1 selected_disk = int(args.pop('disk', default_disk)) @@ -694,35 +695,47 @@ def action_setup_show(): ips = set(args['ips'].split(' ')) base_client = args['selected_client'] - try: - db_partitions = get_client_setup(base_client) - except ServerError: - return ogserver_down('commands') - except ServerErrorCode: - return ogserver_error('commands') - if not db_partitions: + for ip in ips: + try: + setup_data = get_client_setup(ip) + except ServerError: + return ogserver_down('commands') + except ServerErrorCode: + return ogserver_error('commands') + + filtered_partitions = [p for p in setup_data + if p.get('disk') == selected_disk] + if not filtered_partitions: + continue + + if ip == base_client: + target_partitions = filtered_partitions + + client_disk_size = filtered_partitions[0]['size'] // 1024 + if disk_size: + disk_size = min(disk_size, client_disk_size) + else: + disk_size = client_disk_size + + if not target_partitions: flash(_('Partition information is not available. Boot client in ogLive mode to obtain it'), category='error') return redirect(url_for('commands')) - filtered_partitions = [p for p in db_partitions - if p.get('disk') == selected_disk] disk_partition = 0 - disks = [d.get('disk') for d in db_partitions + disks = [d.get('disk') for d in target_partitions if d.get('partition') == disk_partition] form = SetupForm() form.ips.data = ips_str form.disk.data = selected_disk # If partition table is empty, set MSDOS - form.disk_type.data = filtered_partitions[0]['code'] or 1 - - disk_size = filtered_partitions[0]['size'] // 1024 + form.disk_type.data = target_partitions[0]['code'] or 1 - # Make form.partition length equal to (filtered_partitions - 1) length - diff = len(filtered_partitions) - 1 - len(form.partitions) + # Make form.partition length equal to (target_partitions - 1) length + diff = len(target_partitions) - 1 - len(form.partitions) [form.partitions.append_entry() for unused in range(diff)] - for partition, db_part in zip(form.partitions, filtered_partitions[1:]): + for partition, db_part in zip(form.partitions, target_partitions[1:]): partition.partition.data = str(db_part['partition']) partition.part_type.data = db_part['code'] partition.fs.data = db_part['filesystem'] |