summaryrefslogtreecommitdiffstats
path: root/ogcp
diff options
context:
space:
mode:
authorJavier Sánchez Parra <jsanchez@soleta.eu>2021-07-28 17:14:51 +0200
committerOpenGnSys Support Team <soporte-og@soleta.eu>2021-07-28 17:19:51 +0200
commit5dd2b5c6dcd51438f71da05af399999ba512e0ca (patch)
treee9f10561cda118a143211dabc6a524c89e9fd446 /ogcp
parent4b4edf4aeeed5d6177c1d78812a497e7ad1a206d (diff)
Add full scheme partitioning support
The initial "Partition & Format" (aka setup) form only allows to modify one partition at a time. This commit updates it to allow to modify the whole disk partition schema in one go, without pop-ups and transitions. This is a remake of the previous form using FieldList de WTForms and javascript to duplicate / remove FieldList adapted to the attributes available in WTForms.
Diffstat (limited to 'ogcp')
-rw-r--r--ogcp/forms/action_forms.py32
-rw-r--r--ogcp/static/js/ogcp.js22
-rw-r--r--ogcp/templates/actions/setup.html122
-rw-r--r--ogcp/views.py133
4 files changed, 121 insertions, 188 deletions
diff --git a/ogcp/forms/action_forms.py b/ogcp/forms/action_forms.py
index 2286941..c5b4909 100644
--- a/ogcp/forms/action_forms.py
+++ b/ogcp/forms/action_forms.py
@@ -7,7 +7,7 @@
from wtforms import (
Form, SubmitField, HiddenField, SelectField, BooleanField, IntegerField,
- StringField, RadioField
+ StringField, RadioField, FormField, FieldList
)
from wtforms.validators import InputRequired
from flask_wtf import FlaskForm
@@ -21,9 +21,8 @@ class WOLForm(FlaskForm):
submit = SubmitField(label=_('Submit'))
class PartitionForm(FlaskForm):
- ips = HiddenField()
- disk = HiddenField()
- partition = HiddenField()
+ partition = SelectField(label=_('Partition'),
+ choices=range(1,10))
part_type = SelectField(label=_('Type'),
choices=[('LINUX', 'Linux'),
('NTFS', 'NTFS'),
@@ -31,28 +30,19 @@ class PartitionForm(FlaskForm):
fs = SelectField(label=_('Filesystem'),
choices=[('EXT4', 'EXT4'),
('NTFS', 'NTFS'),
- ('DISK', 'Disk'),
('EMPTY', 'Empty')])
size = IntegerField(label=_('Size (KB)'))
format_partition = BooleanField(label=_('Format'))
- modify = SubmitField(label=_('Modify'))
- delete = SubmitField(label=_('Delete'))
-
-class NewPartitionForm(FlaskForm):
+class SetupForm(FlaskForm):
ips = HiddenField()
- part_type = SelectField(label=_('Type'),
- choices=[('LINUX', 'Linux'),
- ('NTFS', 'NTFS'),
- ('EMPTY', 'Empty')])
- fs = SelectField(label=_('Filesystem'),
- choices=[('EXT4', 'EXT4'),
- ('NTFS', 'NTFS'),
- ('DISK', 'Disk'),
- ('EMPTY', 'Empty')])
- size = IntegerField(label=_('Size (KB)'))
- create = SubmitField(label=_('Create'))
-
+ disk = HiddenField()
+ disk_type = SelectField(label=_('Type'),
+ choices=[('MSDOS', 'MSDOS'),
+ ('GPT', 'GPT')])
+ partitions = FieldList(FormField(PartitionForm),
+ min_entries=1,
+ max_entries=10)
class HardwareForm(FlaskForm):
ips = HiddenField()
diff --git a/ogcp/static/js/ogcp.js b/ogcp/static/js/ogcp.js
index f9c8e56..00ab61c 100644
--- a/ogcp/static/js/ogcp.js
+++ b/ogcp/static/js/ogcp.js
@@ -53,3 +53,25 @@ function updateScopes(scopes) {
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(":input")[0].id;
+ const elem_prefix = elem_id.replace(/(.*)-(\d{1,4})/m, '$1')// max 4 digits for ids in list
+ const elem_num = parseInt(elem_id.replace(/(.*)-(\d{1,4})/m, '$2')) + 1;
+ // Increment WTForms unique identifiers
+ row.children(':input').each(function() {
+ const id = $(this).attr('id').replace(elem_prefix+'-' + (elem_num - 1),
+ elem_prefix+'-' + (elem_num));
+ $(this).attr('name', id).attr('id', id).val('').removeAttr("checked");
+ });
+ row.show();
+ oldrow.after(row);
+}
+
+function RemovePartition(evt) {
+ const target = $(evt).parent().parent();
+ target.remove();
+}
diff --git a/ogcp/templates/actions/setup.html b/ogcp/templates/actions/setup.html
index 6856ed1..c1633c6 100644
--- a/ogcp/templates/actions/setup.html
+++ b/ogcp/templates/actions/setup.html
@@ -4,79 +4,61 @@
<h1 class="m-5">{{_('Partition and Format')}}</h1>
-<table class="table">
- <thead class="text-center">
- <tr>
- <th>Type</th>
- <th>Filesytem</th>
- <th>Size (KB)</th>
- <th>Format?</th>
- <th colspan="2"></th>
- </tr>
- </thead>
+<form class="form-inline" method="POST" id="setupForm">
+ <table class="table">
+ <thead class="text-center">
+ <tr>
+ <th>Partition Table</th>
+ <th>Total Disk Size (KB)</th>
+ </tr>
+ </thead>
- <tbody>
- {% for form in forms %}
- <form class="form-inline" method="POST">
- <tr>
- {{ form.hidden_tag() }}
- <td>{{ form.part_type(class_="form-control") }}</td>
- <td>{{ form.fs(class_="form-control") }}</td>
- <td>{{ form.size(class_="form-control") }}</td>
- <td>{{ form.format_partition(class_="form-control") }}</td>
- <td>{{ form.modify(class_="form-control btn-primary") }}</td>
- <td>{{ form.delete(class_="form-control btn-danger") }}</td>
- </tr>
- </form>
- {% endfor %}
- </tbody>
-</table>
+ <tbody data-target="partitons-fieldset" id="setupTable" class="text-center">
+ <tr>
+ {{ 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>Filesytem</th>
+ <th>Size (KB)</th>
+ <th>Format?</th>
+ <th colspan="2"></th>
+ </tr>
+ </thead>
-<button class="btn btn-primary" data-toggle="modal" data-target="#newPartitionModal">
- {{ _('Add a new Partition') }}
-</button>
+ <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(class_="form-control") }}</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.format_partition(class_="form-control") }}</td>
+ <td>
+ <button class="btn btn-danger" onclick="RemovePartition(this)">
+ {{ _('Remove') }}
+ </button>
+ </td>
+ </tr>
+ {% endfor %}
+ </tbody>
+ </table>
+</form>
-<!-- Modal -->
-<div class="modal fade" id="newPartitionModal" tabindex="-1" aria-hidden="true">
- <div class="modal-dialog">
- <div class="modal-content">
- <form class="form" method="POST">
- <div class="modal-header">
- <h5 class="modal-title" id="exampleModalLabel">{{ _('Create a new partition') }}</h5>
- <button type="button" class="close" data-dismiss="modal" aria-label="Close">
- <span aria-hidden="true">&times;</span>
- </button>
- </div>
- <div class="modal-body">
- {{ new_partition_form.hidden_tag() }}
+<button class="btn btn-primary" data-target="#partitionsTable" onclick="AddPartition(this)">
+ {{ _('Add a new partition') }}
+</button>
- <div class="form-group">
- <label for="{{ new_partition_form.part_type.id }}">{{ new_partition_form.part_type.label }}</label>
- {{ new_partition_form.part_type(class_="form-control") }}
- </div>
- <div class="form-group">
- <label for="{{ new_partition_form.fs.id }}">{{ new_partition_form.fs.label }}</label>
- {{ new_partition_form.fs(class_="form-control") }}
- </div>
- <div class="form-group">
- <label for="{{ new_partition_form.size.id }}">{{ new_partition_form.size.label }}</label>
- {% if new_partition_form.size.errors %}
- {{ new_partition_form.size(class_="form-control is-invalid") }}
- {% else %}
- {{ new_partition_form.size(class_="form-control") }}
- {% endif %}
- {% for error in new_partition_form.size.errors %}
- <div class="invalid-feedback">{{ error }}</div>
- {% endfor %}
- </div>
- </div>
- <div class="modal-footer">
- {{ new_partition_form.create(class_="btn btn-primary") }}
- <button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
- </div>
- </div>
- </form>
- </div>
-</div>
+<button class="btn btn-success" form="setupForm">
+ {{ _('Accept') }}
+</button>
{% endblock %}
diff --git a/ogcp/views.py b/ogcp/views.py
index ad5a8cb..ba1fe6c 100644
--- a/ogcp/views.py
+++ b/ogcp/views.py
@@ -9,7 +9,7 @@ from flask import (
g, render_template, url_for, flash, redirect, request, jsonify, make_response
)
from ogcp.forms.action_forms import (
- WOLForm, PartitionForm, NewPartitionForm, ClientDetailsForm, HardwareForm,
+ WOLForm, SetupForm, ClientDetailsForm, HardwareForm,
SessionForm, ImageRestoreForm, ImageCreateForm, SoftwareForm, BootModeForm,
RoomForm, DeleteRoomForm, CenterForm
)
@@ -46,6 +46,7 @@ PART_TYPE_CODES = {
}
PART_SCHEME_CODES = {
+ 0: 'EMPTY',
1: 'MSDOS',
2: 'GPT'
}
@@ -232,85 +233,65 @@ def action_wol():
@app.route('/action/setup', methods=['GET'])
@login_required
-def action_setup_show(ips=None, new_partition_form=None):
+def action_setup_show(ips=None):
if not ips:
ips = parse_ips(request.args.to_dict())
db_partitions = get_client_setup(ips)
- forms = [PartitionForm() for _ in db_partitions]
- forms = list(forms)
+ form = SetupForm()
- if not new_partition_form:
- new_partition_form = NewPartitionForm()
- new_partition_form.ips.data = " ".join(ips)
- new_partition_form.create.render_kw = {"formaction": url_for('action_setup_create')}
+ form.ips.data = " ".join(ips)
+ form.disk.data = db_partitions[0]['disk']
+ # If partition table is empty, set MSDOS
+ form.disk_type.data = db_partitions[0]['code'] or 1
- for form, db_part in zip(forms, db_partitions):
- form.ips.data = " ".join(ips)
- form.disk.data = db_part['disk']
- form.partition.data = db_part['partition']
- # XXX: Should separate whole disk form from partition setup form
- if db_part['filesystem'] == "DISK":
- form.part_type.choices = list(PART_SCHEME_CODES.values())
- form.fs.render_kw = {'disabled': ''}
-
- form.part_type.data = db_part['code']
- form.fs.data = db_part['filesystem']
- form.size.data = db_part['size']
- form.modify.render_kw = {"formaction": url_for('action_setup_modify')}
- form.delete.render_kw = {"formaction": url_for('action_setup_delete')}
+ disk_size = db_partitions[0]['size']
+
+ # Make form.partition length equal to (db_partitions - 1) length
+ diff = len(db_partitions) - 1 - len(form.partitions)
+ [form.partitions.append_entry() for _ in range(diff)]
+
+ for partition, db_part in zip(form.partitions, db_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']
scopes, _clients = get_scopes(ips)
return render_template('actions/setup.html',
- forms=forms,
- new_partition_form=new_partition_form,
+ form=form,
+ disk_size=disk_size,
scopes=scopes)
-@app.route('/action/setup/create', methods=['POST'])
-@login_required
-def action_setup_create():
- form = NewPartitionForm(request.form)
- ips = form.ips.data.split(' ')
-
- if form.validate():
- # TODO: create the real partition
- flash(_('Partition created successfully'), category='info')
- return redirect(url_for('action_setup_show', ips=ips))
-
- flash(_('Invalid partition configuration'), category='error')
- # This return will maintain the new partition form state, but will break
- # the F5 behavior in the browser
- return action_setup_show(ips, form)
-
-@app.route('/action/setup/modify', methods=['POST'])
+@app.route('/action/setup', methods=['POST'])
@login_required
def action_setup_modify():
- form = PartitionForm(request.form)
+ form = SetupForm(request.form)
if form.validate():
ips = form.ips.data.split(' ')
db_partitions = get_client_setup(ips)
payload = {'clients': ips,
'disk': str(form.disk.data),
+ 'type': str(form.disk_type.data),
'cache': str(0),
'cache_size': str(0),
'partition_setup': []}
- for db_part in db_partitions:
- if db_part['partition'] == 0:
- # Set partition scheme
- payload['type'] = db_part['code']
- continue
- partition_setup = {'partition': str(db_part['partition']),
- 'code': db_part['code'],
- 'filesystem': db_part['filesystem'],
- 'size': str(db_part['size']),
- 'format': str(int(False))}
+ required_partitions = ["1", "2", "3", "4"]
+ for partition in form.partitions:
+ print(partition)
+ partition_setup = {'partition': str(partition.partition.data),
+ 'code': str(partition.part_type.data),
+ 'filesystem': str(partition.fs.data),
+ 'size': str(partition.size.data),
+ 'format': str(int(partition.format_partition.data))}
payload['partition_setup'].append(partition_setup)
+ if partition.partition.data in required_partitions:
+ required_partitions.remove(partition.partition.data)
- # Ensure a 4 partition setup
- for i in range(len(db_partitions), 5):
+ for partition in required_partitions:
empty_part = {
- 'partition': str(i),
+ 'partition': partition,
'code': 'EMPTY',
'filesystem': 'EMPTY',
'size': '0',
@@ -318,48 +299,6 @@ def action_setup_modify():
}
payload['partition_setup'].append(empty_part)
- modified_part = payload['partition_setup'][int(form.partition.data) - 1]
- modified_part['filesystem'] = str(form.fs.data)
- modified_part['code'] = str(form.part_type.data)
- modified_part['size'] = str(form.size.data)
- modified_part['format'] = str(int(form.format_partition.data))
-
- r = g.server.post('/setup', payload=payload)
- if r.status_code == requests.codes.ok:
- return redirect(url_for("scopes"))
- return make_response("400 Bad Request", 400)
-
-@app.route('/action/setup/delete', methods=['POST'])
-@login_required
-def action_setup_delete():
- form = PartitionForm(request.form)
- if form.validate():
- ips = form.ips.data.split(' ')
- db_partitions = get_client_setup(ips)
-
- payload = {'clients': ips,
- 'disk': str(form.disk.data),
- 'cache': str(0),
- 'cache_size': str(0),
- 'partition_setup': []}
-
- for db_part in db_partitions:
- if db_part['partition'] == 0:
- # Skip if this is disk setup.
- continue
- partition_setup = {'partition': str(db_part['partition']),
- 'code': db_part['code'],
- 'filesystem': db_part['filesystem'],
- 'size': str(db_part['size']),
- 'format': str(int(False))}
- payload['partition_setup'].append(partition_setup)
-
- modified_part = payload['partition_setup'][int(form.partition.data) - 1]
- modified_part['filesystem'] = FS_CODES[1]
- modified_part['code'] = PART_TYPE_CODES[0]
- modified_part['size'] = str(0)
- modified_part['format'] = str(int(True))
-
r = g.server.post('/setup', payload=payload)
if r.status_code == requests.codes.ok:
return redirect(url_for("scopes"))