mirror of https://git.48k.eu/ogcp
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.multi-ogserver
parent
4b4edf4aee
commit
5dd2b5c6dc
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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') }}
|
||||
<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>
|
||||
|
||||
<button class="btn btn-primary" data-target="#partitionsTable" onclick="AddPartition(this)">
|
||||
{{ _('Add a new partition') }}
|
||||
</button>
|
||||
|
||||
<!-- 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">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
{{ new_partition_form.hidden_tag() }}
|
||||
|
||||
<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 %}
|
||||
|
|
131
ogcp/views.py
131
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': ''}
|
||||
disk_size = db_partitions[0]['size']
|
||||
|
||||
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')}
|
||||
# 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"))
|
||||
|
|
Loading…
Reference in New Issue