ogcp: add disk restrictions in partition and format

Use common disk space across all the selected clients.

Show dynamic disk partition graph in the partition view.

Limit partition sizes dynamically in the form.

Move js code to handle the addition and removal of
partitions into the html file to debloat ogcp.js and keep
the functions local to the only file they manipulate.
master
Alejandro Sirgo Rica 2024-08-22 10:04:55 +02:00
parent aab70b0222
commit bcd18241c7
4 changed files with 188 additions and 62 deletions

View File

@ -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"]')

View File

@ -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 %}

View File

@ -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

View File

@ -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
form.disk_type.data = target_partitions[0]['code'] or 1
disk_size = filtered_partitions[0]['size'] // 1024
# 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']