diff options
author | Alejandro Sirgo Rica <asirgo@soleta.eu> | 2024-08-29 11:55:52 +0200 |
---|---|---|
committer | Alejandro Sirgo Rica <asirgo@soleta.eu> | 2024-09-02 15:05:45 +0200 |
commit | d00e437b8d76bc32732643a92f0ea32fbe0b7901 (patch) | |
tree | cf8998b06022b7ca8e29eedb74c0a32b1ecea6b5 | |
parent | 78a26f947fba1af6b339567cb58e0f852d253137 (diff) |
ogcp: add support for multi-disk partition and format
Add support for selecting different disks in the disk inspector.
Add disk_inspector.html as a template to show the disk contents of
a client. The view can define the variable readonly_disk_inspector
to make the view non editable.
Use disk_inspector.html in the following views:
- client details
- partition and format
Update code to obtain the partitions of a client to better fit the
requirements of disk_inspector.html
Remove code to setup the SetupForm as the contents of the disks
are now dynamically loaded through javascript.
-rw-r--r-- | ogcp/forms/action_forms.py | 10 | ||||
-rw-r--r-- | ogcp/templates/actions/client_details.html | 29 | ||||
-rw-r--r-- | ogcp/templates/actions/select_client.html | 42 | ||||
-rw-r--r-- | ogcp/templates/actions/setup.html | 226 | ||||
-rw-r--r-- | ogcp/templates/disk_inspector.html | 306 | ||||
-rw-r--r-- | ogcp/views.py | 192 |
6 files changed, 463 insertions, 342 deletions
diff --git a/ogcp/forms/action_forms.py b/ogcp/forms/action_forms.py index 5a2a4b1..b97ff4d 100644 --- a/ogcp/forms/action_forms.py +++ b/ogcp/forms/action_forms.py @@ -35,6 +35,7 @@ class PartitionForm(FlaskForm): render_kw={'readonly': True}, default=1) part_type = SelectField(label=_l('Type'), + validators=[InputRequired()], choices=[('LINUX', 'Linux'), ('NTFS', 'NTFS'), ('CACHE', 'CACHE'), @@ -52,6 +53,7 @@ class PartitionForm(FlaskForm): ('EMPTY', 'Empty')], default='EMPTY') fs = SelectField(label=_l('Filesystem'), + validators=[InputRequired()], choices=[('EXT4', 'EXT4'), ('NTFS', 'NTFS'), ('CACHE', 'CACHE'), @@ -59,23 +61,23 @@ class PartitionForm(FlaskForm): ('FAT32', 'FAT32'), ('EMPTY', 'Empty')], default='EMPTY') - size = IntegerField(label=_l('Size (KB)')) + size = IntegerField(label=_l('Size (KB)'), validators=[InputRequired()],) class SelectClientForm(FlaskForm): ips = HiddenField() selected_client = SelectField(label=_l('Select one client as reference to ' 'define the partition scheme')) - ok = SubmitField(label=_l('Submit')) + submit = SubmitField(label=_l('Select')) class SetupForm(FlaskForm): ips = HiddenField() - disk = HiddenField() + disk = SelectField(label='Disk', validate_choice=False) disk_type = SelectField(label=_l('Type'), choices=[('MSDOS', 'MBR'), ('GPT', 'GPT')]) partitions = FieldList(FormField(PartitionForm), min_entries=1, - max_entries=10) + max_entries=16) class HardwareForm(FlaskForm): ips = HiddenField() diff --git a/ogcp/templates/actions/client_details.html b/ogcp/templates/actions/client_details.html index a718450..6bccace 100644 --- a/ogcp/templates/actions/client_details.html +++ b/ogcp/templates/actions/client_details.html @@ -37,31 +37,10 @@ </form> </div> -{% if setup|length > 0 %} -<table class="table"> - <tbody class="text-center"> - <tr> - <th>{{_('Partition')}}</th> - <th>{{_('Type')}}</th> - <th>{{_('Filesystem')}}</th> - <th>{{_('Size')}} (MiB)</th> - <th>{{_('Image')}}</th> - <th colspan="2"></th> - </tr> - </tbody> - <tbody class="text-center"> - {% for entry in setup %} - <tr> - <td>{{ entry.partition }}</td> - <td>{{ entry.code }}</td> - <td>{{ entry.filesystem }}</td> - <td>{{ entry.size // 1024}}</td> - <td>{{ entry.image }}</td> - </tr> - {% endfor %} - </tbody> -</table> -{% endif %} +{% set show_part_images = True %} +{% set readonly_disk_inspector = True %} +{% include 'disk_inspector.html' %} + {% include 'cache_inspector.html' %} diff --git a/ogcp/templates/actions/select_client.html b/ogcp/templates/actions/select_client.html index 0415621..a44c551 100644 --- a/ogcp/templates/actions/select_client.html +++ b/ogcp/templates/actions/select_client.html @@ -17,6 +17,48 @@ {{ macros.cmd_selected_clients(selected_clients) }} +{% if common_disk_data|length > 0 %} + +<div class="form-group mx-5"> + <label class="control-label">{{ _('Disk inventory') }}</label> + <table class="table table-bordered"> + <thead class="thead-light"> + <tr> + <th>{{ _('Disk') }}</th> + <th>{{ _('Size (MiB)') }}</th> + <th>{{ _('Clients') }}</th> + </tr> + </thead> + <tbody> + {% for disk, data in common_disk_data.items() %} + {% for size, ips in data.inventory.items() %} + <tr> + {% if loop.first %} + <td rowspan="{{ data.inventory | length }}">{{ disk }}</td> + {% endif %} + <td> + {% if size == data.common_size %} + <strong>{{ size // 1024 }}</strong> + {% else %} + {{ size // 1024 }} + {% endif %} + </td> + <td> + {% for ip in ips %} + <div class="card d-inline-block" style="padding: 5px;">{{ ip }}</div> + {% endfor %} + </td> + </tr> + {% endfor %} + + {% endfor %} + </tbody> + </table> + <p>{{ _('Note: the disk size in bold represents the selected common size for partitioning') }}</p> +</div> + +{% endif %} + {{ wtf.quick_form(form, action=url_for('action_setup_show'), method='get', diff --git a/ogcp/templates/actions/setup.html b/ogcp/templates/actions/setup.html index 934d72f..6ae7c15 100644 --- a/ogcp/templates/actions/setup.html +++ b/ogcp/templates/actions/setup.html @@ -9,230 +9,6 @@ <h2 class="mx-5 subhead-heading">{{_('Partition and Format')}}</h2> -<form method="GET" id="changeDiskForm"> - <input type="hidden" name="ips" value="{{ ips }}"/> - <input type="hidden" name="selected_client" value="{{ base_client }}"/> -</form> - -<form class="form-inline" method="POST" id="setupForm"> - <table class="table"> - <thead class="text-center"> - <tr> - <th>{{ _('Disk') }}</th> - <th>{{ _('Partition Table Type') }}</th> - <th>{{ _('Total Disk Size') }} (MiB)</th> - </tr> - </thead> - - <tbody data-target="partitons-fieldset" id="setupTable" class="text-center"> - <tr> - <td> - <select form="changeDiskForm" name="disk" onchange="this.form.submit()"> - {% for disk in disks %} - <option {% if disk == selected_disk %}selected{% endif %} - value="{{ disk }}">{{ disk }}</option> - {% endfor %} - </select> - </td> - {{ form.hidden_tag() }} - <td>{{ form.disk_type(class_="form-control") }}</td> - <td>{{ disk_size }}</td> - </tr> - </tbody> - </table> - <table class="table"> - <thead class="text-center"> - <tr> - <th>{{ _('Partition') }}</th> - <th>{{ _('Type') }}</th> - <th>{{ _('Filesystem') }}</th> - <th>{{ _('Size') }} (MiB)</th> - <th colspan="2"></th> - </tr> - </thead> - - <tbody data-target="partitons-fieldset" id="partitionsTable" class="text-center"> - {% for partition in form.partitions %} - <tr data-toggle="fieldset-entry"> - {{ partition.hidden_tag() }} - <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", oninput="handleEdit(this)") }}</td> - <td> - <button class="btn btn-danger" type="button" onclick="RemovePartition(this)"> - {{ _('Remove') }} - </button> - </td> - </tr> - {% endfor %} - </tbody> - </table> -</form> - -<button class="btn btn-primary" data-target="#partitionsTable" onclick="AddPartition(this)"> - {{ _('Add a new partition') }} -</button> - -<button class="btn btn-success" form="setupForm"> - {{ _('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> +{% include 'disk_inspector.html' %} {% endblock %} diff --git a/ogcp/templates/disk_inspector.html b/ogcp/templates/disk_inspector.html new file mode 100644 index 0000000..90a8996 --- /dev/null +++ b/ogcp/templates/disk_inspector.html @@ -0,0 +1,306 @@ +{% if selected_disk is defined and setup_data is defined %} + +<form class="form-inline" method="POST" id="setupForm"> + {{ disk_form.hidden_tag() }} + {{ disk_form.ips() }} + + <table class="table"> + <thead class="text-center"> + <tr> + <th>{{ _('Disk') }}</th> + <th>{{ _('Partition Table Type') }}</th> + <th>{{ _('Total Disk Size') }} (MiB)</th> + </tr> + </thead> + + <tbody data-target="partitons-fieldset" id="diskTable" class="text-center"> + <tr> + <td> + {{ disk_form.disk(class_="form-control", onchange="handleDiskChange(this)") }} + </td> + {{ disk_form.hidden_tag() }} + <td> + {% if readonly_disk_inspector is defined %} + {{ disk_form.disk_type(class_="form-control", id="diskType", readonly="readonly") }} + {% else %} + {{ disk_form.disk_type(class_="form-control", id="diskType") }} + {% endif %} + </td> + <td id="diskSize">{{ disk_size }}</td> + </tr> +</tbody> + </table> + <table class="table"> + <thead class="text-center"> + <tr> + <th>{{ _('Partition') }}</th> + <th>{{ _('Type') }}</th> + <th>{{ _('Filesystem') }}</th> + <th>{{ _('Size') }} (MiB)</th> + {% if show_part_images is defined %} + <th>{{ _('Image') }}</th> + {% endif %} + <th colspan="2"></th> + </tr> + </thead> + + <tbody data-target="partitons-fieldset" id="partitionsTable" class="text-center"> + {% for partition in disk_form.partitions %} + <tr data-toggle="fieldset-entry"> + {{ partition.hidden_tag() }} + <td>{{ partition.partition.data }}</td> + <td> + {% if readonly_disk_inspector is defined %} + {{ partition.part_type(class_="form-control", readonly="readonly") }} + {% else %} + {{ partition.part_type(class_="form-control") }} + {% endif %} + </td> + <td> + {% if readonly_disk_inspector is defined %} + {{ partition.fs(class_="form-control", readonly="readonly") }} + {% else %} + {{ partition.fs(class_="form-control") }} + {% endif %} + </td> + <td> + {% if readonly_disk_inspector is defined %} + {{ partition.size(class_="form-control", oninput="handleEdit(this)", readonly="readonly") }} + {% else %} + {{ partition.size(class_="form-control", oninput="handleEdit(this)") }} + {% endif %} + </td> + {% if show_part_images is defined %} + <td></td> + {% endif %} + <td> + <button class="btn btn-danger" type="button" onclick="RemovePartition(this, true)" + {% if readonly_disk_inspector is defined %}style="display: none;"{% endif %}> + {{ _('Remove') }} + </button> + </td> + </tr> + {% endfor %} + </tbody> + </table> +</form> + +<button class="btn btn-primary" data-target="#partitionsTable" id="addPartButton" onclick="AddPartition(this, true)" +{% if readonly_disk_inspector is defined %}style="display: none;"{% endif %}> + {{ _('Add a new partition') }} +</button> + +{% if not readonly_disk_inspector is defined %} + <button class="btn btn-success" form="setupForm"> + {{ _('Submit') }} + </button> +{% endif %} + +<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 selectedDisk = {{selected_disk}}; + let setupData = {{setup_data|tojson|safe}} + let diskSize = 0; + + 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 updatePartitionChart() { + 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(); + } + + function handleEdit(element) { + const numericValue = parseInt(element.value); + + if (isNaN(numericValue)) { + updatePartitionChart(); + 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; + } + + updatePartitionChart(); + } + + function AddPartition(evt, updateChart) { + 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); + + if (updateChart) { + updatePartitionChart(); + } + } + + function RemovePartition(evt, updateChart) { + 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"); + }); + }); + } + if (updateChart) { + updatePartitionChart(); + } + } + + function setDiskData(diskNumber) { + selectedDisk = diskNumber; + let partitions = setupData[selectedDisk]; + diskSize = Math.floor(partitions[0].size / 1024); + + $('#diskSize').text(diskSize); + $('#diskType').val(partitions[0].code); + + const partitionsTable = $('#partitionsTable'); + let partNumber = partitionsTable.find('tr').length; + const targetPartNumber = partitions.length - 1; + + while (partNumber < targetPartNumber) { + AddPartition($('#addPartButton'), false); + partNumber++; + } + + while (partNumber > targetPartNumber) { + RemovePartition(partitionsTable.find('tr:last').find('button'), false); + partNumber--; + } + + for (let i = 1; i < partitions.length; i++) { + let p = partitions[i]; + + let row = partitionsTable.find('tr').eq(i - 1); + + row.find('td').eq(0).text(p.partition); + row.find('td').eq(1).find('select').val(p.code); + row.find('td').eq(2).find('select').val(p.filesystem); + row.find('td').eq(3).find('input').val(Math.floor(p.size / 1024)); + {% if show_part_images is defined %} + row.find('td').eq(4).text(p.image); + {% endif %} + } + + updatePartitionChart(); + } + + function handleDiskChange(selectElement) { + setDiskData(selectElement.value); + } + + $(document).ready(function() { + setDiskData(selectedDisk); + }); + +</script> + +{% endif %} diff --git a/ogcp/views.py b/ogcp/views.py index 86006ba..579a43f 100644 --- a/ogcp/views.py +++ b/ogcp/views.py @@ -158,6 +158,26 @@ def parse_elements(checkboxes_dict): elements.update(elements_list.split(' ')) return elements +def client_setup_add_image_names(server, setup_data): + r = server.get('/images') + if not r: + raise ServerError + if r.status_code != requests.codes.ok: + raise ServerErrorCode + + images = r.json()['images'] + + for disk, partitions in setup_data.items(): + for p in partitions: + if images and p['image'] != 0: + image = next((img for img in images if img['id'] == p['image']), None) + if image: + p['image'] = image['name'] + else: + p['image'] = "" + else: + p['image'] = "" + def get_client_setup(ip): server = get_server_from_clients([ip]) @@ -175,6 +195,7 @@ def get_client_setup(ip): if r.status_code != requests.codes.ok: raise ServerErrorCode db_partitions = r.json()['partitions'] + res = {} for partition in db_partitions: if partition['partition'] == 0: partition['code'] = PART_SCHEME_CODES.get(partition['code'], 'MSDOS') @@ -183,7 +204,20 @@ def get_client_setup(ip): partition['filesystem'] = FS_CODES.get(partition['filesystem'], 'EMPTY') - return db_partitions + disk = partition.get('disk') + if disk in res: + res[disk].append(partition) + else: + res[disk] = [partition] + + return res + +@app.route('/client/setup', methods=['GET']) +@login_required +def get_client_setup_json(): + ip = parse_elements(request.args.to_dict()) + setup = get_client_setup(ip) + return jsonify(setup) def get_clients(state_filter=None): responses = multi_request('get', '/clients') @@ -642,6 +676,33 @@ def action_wol(): return redirect(url_for('commands')) +def get_common_disk_data(ips): + disk_data = {} + + for ip in ips: + setup_data = get_client_setup(ip) + + for disk, partitions in setup_data.items(): + for p in partitions: + if p.get('partition') != 0: + continue + if disk in disk_data: + disk_data[disk]['common_size'] = min(disk_data[disk]['common_size'], p.get('size')) + disk_data[disk]['inventory'].setdefault(p.get('size'), []).append(ip) + else: + disk_data[disk] = { + 'common_size': p.get('size'), + 'excluded': [], + 'inventory': {p.get('size'): [ip]} + } + break + + for disk_id in disk_data: + if disk_id not in setup_data: + disk_data[disk_id]['excluded'].append(ip) + + return disk_data + @app.route('/action/setup/select', methods=['GET']) @login_required def action_setup_select(): @@ -671,10 +732,13 @@ def action_setup_select(): client_choices.append((ip, f"{client_name} ({ip})")) form.selected_client.choices = client_choices + common_disk_data = get_common_disk_data(ips) + scopes, unused = get_scopes(ips) selected_clients = list(get_selected_clients(scopes['scope']).items()) return render_template('actions/select_client.html', + common_disk_data=common_disk_data, selected_clients=selected_clients, form=form, scopes=scopes) @@ -685,9 +749,6 @@ def action_setup_show(): args = request.args.copy() disk_size = None - default_disk = 1 - selected_disk = int(args.pop('disk', default_disk)) - if args.get('ip'): ips = {args['ip']} ips_str = base_client = args['ip'] @@ -696,59 +757,37 @@ def action_setup_show(): ips = set(args['ips'].split(' ')) base_client = args['selected_client'] - 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 + try: + setup_data = get_client_setup(base_client) + except ServerError: + return ogserver_down('commands') + except ServerErrorCode: + return ogserver_error('commands') - if not target_partitions: + if not setup_data: flash(_('Partition information is not available. Boot client in ogLive mode to obtain it'), category='error') return redirect(url_for('commands')) - disk_partition = 0 - disks = [d.get('disk') for d in target_partitions - if d.get('partition') == disk_partition] + selected_disk = 1 + common_disk_data = get_common_disk_data(ips) + + # Use common disk size + for disk in setup_data: + setup_data[disk][0]['size'] = common_disk_data[disk]['common_size'] form = SetupForm() form.ips.data = ips_str + + form.disk.choices = [(disk, disk) for disk in setup_data] form.disk.data = selected_disk # If partition table is empty, set MSDOS - form.disk_type.data = target_partitions[0]['code'] or 1 - - # 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)] + form.disk_type.data = setup_data[selected_disk][0]['code'] or 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'] - partition.size.data = db_part['size'] // 1024 scopes, _clients = get_scopes(ips) return render_template('actions/setup.html', selected_disk=selected_disk, - disks=disks, - form=form, - disk_size=disk_size, - ips=ips_str, - base_client=base_client, + setup_data=setup_data, + disk_form=form, scopes=scopes) @app.route('/action/setup', methods=['POST']) @@ -1461,34 +1500,19 @@ def action_client_info(): form.submit.render_kw = {"style": "visibility:hidden;"} - r = server.get('/images') - if not r: - return ogserver_down('commands') - if r.status_code != requests.codes.ok: - return ogserver_error('commands') - - images = r.json()['images'] - ip = list(ips)[0] try: - setup = get_client_setup(ip) + setup_data = get_client_setup(ip) + client_setup_add_image_names(server, setup_data) except ServerError: return ogserver_down('commands') except ServerErrorCode: return ogserver_error('commands') - if setup and setup[0].get('code') == 'MSDOS': - setup[0]['code'] = 'MBR' - - for entry in setup: - if images and entry['image'] != 0: - image = next((img for img in images if img['id'] == entry['image']), None) - if image: - entry['image'] = image['name'] - else: - entry['image'] = "" - else: - entry['image'] = "" + disk_form = SetupForm() + selected_disk = 1 + disk_form.disk.choices = [(disk, disk) for disk in setup_data] + disk_form.disk.data = selected_disk r = server.get('/cache/list', payload={'clients': [ip]}) if not r: @@ -1511,7 +1535,10 @@ def action_client_info(): 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_data=setup_data, + disk_form=disk_form, + selected_disk=selected_disk, images_data=images_data, storage_data=storage_data, client_images=client_images) @@ -1620,36 +1647,25 @@ def action_client_update(): if db_client['repo_id'] != repo["id"]]) ip = list(ips)[0] try: - setup = get_client_setup(ip) + setup_data = get_client_setup(ip) + client_setup_add_image_names(server, setup_data) except ServerError: return ogserver_down('scopes') except ServerErrorCode: return ogserver_error('scopes') - if setup and setup[0].get('code') == 'MSDOS': - setup[0]['code'] = 'MBR' - - r = server.get('/images') - if not r: - return ogserver_down('scopes') - if r.status_code != requests.codes.ok: - return ogserver_error('scopes') - - images = r.json()['images'] - for entry in setup: - if images and entry['image'] != 0: - image = next((img for img in images if img['id'] == entry['image']), None) - if image: - entry['image'] = image['name'] - else: - entry['image'] = "" - else: - entry['image'] = "" + disk_form = SetupForm() + selected_disk = 1 + disk_form.disk.choices = [(disk, disk) for disk in setup_data] + disk_form.disk.data = selected_disk 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) + setup_data=setup_data, + disk_form=disk_form, + selected_disk=selected_disk, + parent="scopes.html", scopes=scopes) def find_folder(folder_id, scopes): scopes = deque([scopes['scope']]) @@ -3048,7 +3064,7 @@ def repo_addr_is_valid(form): if invalid_ips: res = False flash(_(f'The following addresses are invalid: {" ".join(invalid_ips)}'), category='error') - + if ip_count > 16: res = False flash(_('More than 16 addresses is not supported'), category='error') |