summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ogcp/forms/action_forms.py10
-rw-r--r--ogcp/templates/actions/client_details.html29
-rw-r--r--ogcp/templates/actions/select_client.html42
-rw-r--r--ogcp/templates/actions/setup.html226
-rw-r--r--ogcp/templates/disk_inspector.html306
-rw-r--r--ogcp/views.py192
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')