Compare commits

..

13 Commits

Author SHA1 Message Date
Alejandro Sirgo Rica 4e0bb82f9f views: skip invalid partitions in software inventory
Skip the invalid partition types in the list of selectable
partitions in software inventory.
2025-02-14 13:41:48 +01:00
Alejandro Sirgo Rica c6adc0f29b views: remove outdated ogLive checks
Remove checks for a running ogLive based on an empty client setup
reponse. The check is dead code as the partition setup is cached in
the database so the payload always constains the information.
2025-02-14 13:41:48 +01:00
Alejandro Sirgo Rica 35269b31a7 views: validate client partitions in /action/software
Redirect the user when /action/software is accessed on a client
without valid partitions.
2025-02-14 13:41:48 +01:00
Alejandro Sirgo Rica 1cf6fbc49e views: remove rendundant partition checks in image/restore
Partition validation is already performed before the removed checks
image restore, additional checks are redundant.
Remove redundant partition validation.
2025-02-14 13:39:46 +01:00
Alejandro Sirgo Rica d9597e4e01 views: fix partition scheme default values
Assign EMPTY when no scheme code is found.

Select GPT by default when the partition scheme is EMPTY in
partition and format view.

Add missing EMPTY value to SetupForm.
2025-02-13 10:25:00 +01:00
Alejandro Sirgo Rica b3b47ac0cb views: accept only user credentials without whitespace
Check the presence of whitespace for new usernames or passwords.

Usernames with whitespace can't be deleted because whitespace
is the separator of each element selected in the form.
2025-02-13 09:04:35 +01:00
OpenGnSys Support Team 4c21e370a3 views: remove implicit refresh when getting client details
do not force a client refresh when showing client details, use current
information in database
2025-02-12 13:46:32 +01:00
Alejandro Sirgo Rica 4616e3dcf5 views: remove unused variable partition_text 2025-02-11 11:18:59 +01:00
Alejandro Sirgo Rica d840e4af37 views: fix partition checks in script/run
Skip check of disk data in multi-disk clients in check for
uniform setup across all the clients selected.

Disk data should not be considered for client configuration
comparison.
2025-02-11 11:18:02 +01:00
Alejandro Sirgo Rica 91465fc269 views: skip disk data entries in multi-disk clients
Skip partition id zero for the partition list in the software
inventory form.

Partitions of id zero contain disk data and that data is not
relevant to compare client configurations.
2025-02-11 11:17:06 +01:00
Alejandro Sirgo Rica c33fe3ca77 views: report no valid partitions in image create/restore/update
Inform the user when the selected clients don't have a valid
partition type for an image create/restore/update operation and
return to the commands view.
2025-02-07 12:24:51 +01:00
Alejandro Sirgo Rica 9bb40d267f templates: use m-5 CSS class for every form
Use m-5 class instead of mx-5 CSS class for every main view form.
m-5 adds padding to all the borders while mx-5 only adds padding
for the x axis causing elements to be too close to the top and
bottom of the form.
2025-02-07 12:24:51 +01:00
Alejandro Sirgo Rica 00be07352d templates: warn about image create/restore on disks other than 1
Show a warning when the user selects a partition from a disk >1
in the image update, create or restore views.
Show the complete list of valid partitions from all disk.
2025-02-07 12:24:47 +01:00
37 changed files with 123 additions and 65 deletions

View File

@ -73,7 +73,8 @@ class SetupForm(FlaskForm):
ips = HiddenField()
disk = SelectField(label='Disk', validate_choice=False)
disk_type = SelectField(label=_l('Type'),
choices=[('MSDOS', 'MBR'),
choices=[('EMPTY', 'EMPTY'),
('MSDOS', 'MBR'),
('GPT', 'GPT')])
partitions = FieldList(FormField(PartitionForm),
min_entries=1,

View File

@ -14,7 +14,7 @@
action=url_for('action_center_add'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -14,7 +14,7 @@
action=url_for('action_room_add'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -12,6 +12,7 @@
{{ wtf.quick_form(form,
action=url_for('server_add_post'),
method='post',
button_map={'submit_btn':'primary'}) }}
button_map={'submit_btn':'primary'},
extra_classes="m-5") }}
{% endblock %}

View File

@ -13,6 +13,6 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -13,6 +13,6 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -14,6 +14,6 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -20,6 +20,6 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -45,7 +45,7 @@
action=url_for('action_center_delete'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -21,7 +21,7 @@
action=url_for('action_client_delete'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -17,7 +17,7 @@
action=url_for('action_image_delete'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -11,7 +11,7 @@
<h2 class="mx-5 subhead-heading">{{_('Delete repo')}}</h2>
<form class="form mx-5" method="POST">
<form class="form m-5" method="POST">
{{ form.hidden_tag() }}
{{ form.server() }}

View File

@ -48,7 +48,7 @@
action=url_for('action_room_delete'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -16,7 +16,7 @@
action=url_for('server_delete_post'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -13,7 +13,7 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -45,6 +45,6 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'danger'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -13,7 +13,7 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -16,7 +16,7 @@
action=url_for('action_hardware'),
method='post',
button_map={'refresh': 'primary'},
extra_classes='m-2')}}
extra_classes='m-5')}}
<table class="table table-striped">
<thead class="thead-dark">

View File

@ -8,7 +8,7 @@
<h2 class="mx-5 subhead-heading">{{_('Update image')}} {{ form.name.data }}</h2>
<form class="form mx-5" method="POST" action="{{ url_for('action_image_config') }}">
<form class="form m-5" method="POST" action="{{ url_for('action_image_config') }}">
{{ form.hidden_tag() }}
{{ form.image_id() }}

View File

@ -12,10 +12,13 @@
{{ macros.cmd_selected_clients(selected_clients) }}
{% set partition_field_id = 'os' %}
{% include 'partition_warning.html' %}
{{ wtf.quick_form(form,
action=url_for('action_image_create'),
method='post',
button_map={'create': 'primary'},
extra_classes='mx-5') }}
extra_classes='m-5') }}
{% endblock %}

View File

@ -8,7 +8,7 @@
<h2 class="mx-5 subhead-heading">{{_('Image details')}}</h2>
<div class="container mx-5">
<div class="container m-5">
<form class="form" method="POST">
{{ form.hidden_tag() }}

View File

@ -17,6 +17,9 @@
{{ macros.cmd_selected_clients(selected_clients) }}
{% set partition_field_id = 'partition' %}
{% include 'partition_warning.html' %}
{{ wtf.quick_form(form,
action=url_for('action_image_restore'),
method='post',

View File

@ -13,6 +13,9 @@
{{ macros.cmd_selected_clients(selected_clients) }}
{% set partition_field_id = 'os' %}
{% include 'partition_warning.html' %}
<form class="form mx-5" method="POST" action="{{ url_for('action_image_update') }}">
{{ form.hidden_tag() }}

View File

@ -14,6 +14,6 @@
action=url_for('action_clients_import_post'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -21,7 +21,7 @@
action=url_for('action_poweroff'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -21,7 +21,7 @@
action=url_for('action_reboot'),
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -11,7 +11,7 @@
<h2 class="mx-5 subhead-heading">{{_('Repo details')}}</h2>
<form class="form mx-5" method="POST">
<form class="form m-5" method="POST">
{{ form.hidden_tag() }}
{{ form.server() }}

View File

@ -13,6 +13,6 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -13,7 +13,7 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -20,6 +20,6 @@
{{ wtf.quick_form(form,
method='post',
button_map={'submit': 'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -9,7 +9,7 @@
<h2 class="mx-5 subhead-heading">{{_('Update server')}}</h2>
<form class="form mx-5" method="POST">
<form class="form m-5" method="POST">
{{ form.hidden_tag() }}
{{ form.server_addr() }}

View File

@ -18,13 +18,13 @@
{% if os_groups|length > 0 %}
<p>
<p class="mx-5">
{% if os_groups|length > 1 %}
The selected clients have different installed OS:
{% endif %}
</p>
<form class="form-inline" method="POST" id="sessionForm">
<form class="form-inline m-5" method="POST" id="sessionForm">
<table class="table table-hover">
<thead class="thead-light">
<tr>

View File

@ -14,6 +14,6 @@
action=url_for('action_software'),
method='post',
button_map={'view': 'primary', 'update': 'primary'},
extra_classes="mx-5")}}
extra_classes="m-5")}}
{% endblock %}

View File

@ -13,6 +13,6 @@
action=url_for('user_delete_post'),
method='post',
button_map={'submit_btn':'primary'},
extra_classes="mx-5") }}
extra_classes="m-5") }}
{% endblock %}

View File

@ -1,6 +1,6 @@
{% if selected_disk is defined and setup_data is defined %}
<form class="form-inline mx-5" method="POST" id="setupForm">
<form class="form-inline m-5" method="POST" id="setupForm">
{{ disk_form.hidden_tag() }}
{{ disk_form.ips() }}

View File

@ -0,0 +1,30 @@
<div class="mx-5" id="partition-warning" style="display:none; color:red; font-weight: bold;">
{{ _('Warning: You have selected a partition from a disk other than 1. This will be considered for storage only.') }}
</div>
<script>
function checkPartition() {
var partitionSelect = document.getElementById('{{ partition_field_id }}');
var selectedValue = partitionSelect.value;
var warningDiv = document.getElementById('partition-warning');
// Extract the disk_id
var diskId = parseInt(selectedValue.split(' ')[0]);
if (diskId !== 1) {
warningDiv.style.display = 'block';
} else {
warningDiv.style.display = 'none';
}
}
document.addEventListener("DOMContentLoaded", function () {
var partitionSelect = document.getElementById('{{ partition_field_id }}');
if (partitionSelect) {
partitionSelect.addEventListener('change', checkPartition);
checkPartition();
} else {
console.error("Element with ID '{{ partition_field_id }}' not found.");
}
});
</script>

View File

@ -176,9 +176,6 @@ def client_setup_add_image_names(server, setup_data):
def get_client_setup(ip):
server = get_server_from_clients([ip])
payload = {'clients': [ip]}
r = server.post('/refresh', payload)
payload = {'client': [ip]}
r = server.get('/client/setup', payload)
@ -186,7 +183,7 @@ def get_client_setup(ip):
res = {}
for partition in db_partitions:
if partition['partition'] == 0:
partition['code'] = PART_SCHEME_CODES.get(partition['code'], 'MSDOS')
partition['code'] = PART_SCHEME_CODES.get(partition['code'], 'EMPTY')
else:
partition['code'] = PART_TYPE_CODES.get(partition['code'], 'EMPTY')
@ -777,10 +774,6 @@ def action_setup_show():
setup_data = get_client_setup(base_client)
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'))
selected_disk = 1
common_disk_data = get_common_disk_data(ips)
@ -797,8 +790,11 @@ def action_setup_show():
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 = setup_data[selected_disk][0]['code'] or 1
for disk in setup_data:
if setup_data[disk][0]['code'] == 'EMPTY':
setup_data[disk][0]['code'] = 'GPT'
form.disk_type.data = setup_data[selected_disk][0]['code']
scopes, _clients = get_scopes(ips)
return render_template('actions/setup.html',
@ -1084,8 +1080,6 @@ def action_image_restore():
part_id = partition['partition']
if part_id == 0: # This is the disk data, not a partition.
continue
if disk_id != 1:
continue
part_code = partition['code']
part_type = PART_TYPE_CODES.get(int(part_code), 'UNKNOWN')
@ -1114,10 +1108,6 @@ def action_image_restore():
reference_patitioning = part_collection.get_partition_setup(0)
if not reference_patitioning:
flash(_(f'No valid partition found'), category='error')
return redirect(url_for('commands'))
for disk_id, part_id, part_type, fs_type, part_size in reference_patitioning:
form.partition.choices.append(
(f"{disk_id} {part_id} {part_size} {has_cache}",
@ -1191,17 +1181,28 @@ def action_software():
server = get_server_from_clients(ips)
r = server.get('/client/setup', payload={'client': list(ips)})
if not r.json()['partitions']:
flash(_('Software inventory is not available. Boot client in ogLive mode to obtain it'), category='error')
return redirect(url_for('commands'))
invalid_part_types = get_invalid_image_partition_types()
for part in r.json()['partitions']:
part_id = part['partition']
if part_id == 0:
continue
part_type = PART_TYPE_CODES.get(int(part.get('code')), 'UNKNOWN')
if part_type in invalid_part_types:
continue
for part in r.json()['partitions'][1:]:
form.os.choices.append(
(f"{part.get('disk')} {part.get('partition')}",
f"Disk {part.get('disk')} | Partition {part.get('partition')} "
f"| {PART_TYPE_CODES.get(part.get('code'), 'UNKNOWN')} "
f"{FS_CODES.get(part.get('filesystem'), 'UNKNOWN')}")
)
if not form.os.choices:
flash(_(f'No valid partition available'), category='error')
return redirect(url_for('commands'))
return render_template('actions/software.html', form=form, scopes=scopes)
@app.route('/action/session', methods=['GET', 'POST'])
@ -2079,7 +2080,7 @@ def action_run_script():
for ip in ips:
r = server.get('/client/setup', payload={'client': [ip]})
partitions = r.json()['partitions'][1:]
partitions = [p for p in r.json()['partitions'] if p['partition'] != 0]
if not reference_patitioning:
reference_patitioning = partitions
elif reference_patitioning != partitions:
@ -2372,13 +2373,14 @@ def action_image_create():
invalid_part_types = get_invalid_image_partition_types()
for part in r.json()['partitions'][1:]:
for part in r.json()['partitions']:
part_type = PART_TYPE_CODES.get(int(part.get('code')), 'UNKNOWN')
if part_type in invalid_part_types:
part_id = part['partition']
if part_id == 0: # This is the disk data, not a partition.
continue
if part.get('disk') != 1:
if part_type in invalid_part_types:
continue
form.os.choices.append(
@ -2387,6 +2389,11 @@ def action_image_create():
f"| {PART_TYPE_CODES.get(part.get('code'), 'UNKNOWN')} "
f"{FS_CODES.get(part.get('filesystem'), 'UNKNOWN')}")
)
if not form.os.choices:
flash(_(f'No valid partition available'), category='error')
return redirect(url_for('commands'))
r = server.get('/client/info', payload={'client': list(ips)})
client_repo_id = r.json()['repo_id']
@ -2467,19 +2474,17 @@ def action_image_update():
invalid_part_types = get_invalid_image_partition_types()
part_content = {}
for part in r.json()['partitions'][1:]:
for part in r.json()['partitions']:
part_type = PART_TYPE_CODES.get(int(part.get('code')), 'UNKNOWN')
part_id = part['partition']
if part_id == 0: # This is the disk data, not a partition.
continue
if part_type in invalid_part_types:
continue
if part.get('disk') != 1:
continue
partition_value = f"{part.get('disk')} {part.get('partition')} {part.get('code')}"
partition_text = f"Disk {part.get('disk')} | Partition {part.get('partition')} "
f"| {PART_TYPE_CODES.get(part.get('code'), 'UNKNOWN')} "
f"{FS_CODES.get(part.get('filesystem'), 'UNKNOWN')}"
form.os.choices.append(
(partition_value,
@ -2494,6 +2499,10 @@ def action_image_update():
part_content[partition_value] = part['image']
break
if not form.os.choices:
flash(_(f'No valid partition available'), category='error')
return redirect(url_for('commands'))
scopes, _clients = get_scopes(set(ips))
selected_clients = list(get_selected_clients(scopes['scope']).items())
@ -3475,6 +3484,14 @@ def user_add_post():
flash(form.errors, category='error')
return redirect(url_for('users'))
if re.search(r'\s', form.username.data):
flash(_('The username cannot contain spaces'), category='error')
return redirect(url_for('users'))
if re.search(r'\s', form.pwd.data):
flash(_('The password cannot contain spaces'), category='error')
return redirect(url_for('users'))
if get_user(form.username.data):
flash(_('This username already exists'), category='error')
return redirect(url_for('users'))