From 31766a3d07549e846c61ead624b4a45f88948fc0 Mon Sep 17 00:00:00 2001 From: Alejandro Sirgo Rica Date: Fri, 23 Aug 2024 12:17:41 +0200 Subject: ogcp: add support for multi-ip repositories Add support for the new API REST for repository management where the address is a list of ips instead of a single string. Add dynamic address creation in /action/repo/update and /action/repo/add forms through delete and add buttons in the form. Update /image/restore and /cache/fetch to use repository_id. Add additional repository form validations. --- ogcp/forms/action_forms.py | 5 +- ogcp/templates/actions/delete_repo.html | 29 +++++++++-- ogcp/templates/actions/repo_details.html | 20 +++++++- ogcp/templates/actions/repos_add.html | 7 +-- ogcp/templates/actions/repos_update.html | 7 +-- ogcp/templates/repo_inspector.html | 63 ++++++++++++++++++++++++ ogcp/views.py | 84 ++++++++++++++++++++++---------- 7 files changed, 173 insertions(+), 42 deletions(-) create mode 100644 ogcp/templates/repo_inspector.html diff --git a/ogcp/forms/action_forms.py b/ogcp/forms/action_forms.py index 496be56..5a2a4b1 100644 --- a/ogcp/forms/action_forms.py +++ b/ogcp/forms/action_forms.py @@ -122,7 +122,10 @@ class RepoForm(FlaskForm): server = HiddenField() repo_id = HiddenField() name = StringField(label=_l('Name')) - ip = StringField(label=_l('IP')) + addr = FieldList( + StringField(label=_l('Address')), + label=_l('Addresses'), + ) submit = SubmitField(label=_l('Submit')) class FolderForm(FlaskForm): diff --git a/ogcp/templates/actions/delete_repo.html b/ogcp/templates/actions/delete_repo.html index 17902dd..8c32a8c 100644 --- a/ogcp/templates/actions/delete_repo.html +++ b/ogcp/templates/actions/delete_repo.html @@ -9,9 +9,28 @@

{{_('Delete repo')}}

-{{ wtf.quick_form(form, - action=url_for('action_repo_delete'), - method='post', - button_map={'submit': 'danger'}, - extra_classes="mx-5") }} +
+ {{ form.hidden_tag() }} + + {{ form.server() }} + {{ form.repo_id() }} + +
+ {{ form.name.label }} + {{ form.name(class="form-control") }} +
+ +
+ {{ form.addr.label }} + {% for addr in form.addr %} + {{ addr(class="form-control mb-2") }} + {% endfor %} +
+ +
+ {{ form.submit(class="btn btn-primary") }} +
+ +
+ {% endblock %} diff --git a/ogcp/templates/actions/repo_details.html b/ogcp/templates/actions/repo_details.html index 674abc2..212a63b 100644 --- a/ogcp/templates/actions/repo_details.html +++ b/ogcp/templates/actions/repo_details.html @@ -9,5 +9,23 @@

{{_('Repo details')}}

-{{ wtf.quick_form(form, extra_classes="mx-5") }} +
+ {{ form.hidden_tag() }} + + {{ form.server() }} + {{ form.repo_id() }} + +
+ {{ form.name.label }} + {{ form.name(class="form-control") }} +
+ +
+ {{ form.addr.label }} + {% for addr in form.addr %} + {{ addr(class="form-control mb-2") }} + {% endfor %} +
+
+ {% endblock %} diff --git a/ogcp/templates/actions/repos_add.html b/ogcp/templates/actions/repos_add.html index 338f96c..19f02bf 100644 --- a/ogcp/templates/actions/repos_add.html +++ b/ogcp/templates/actions/repos_add.html @@ -9,9 +9,6 @@

{{_('Add repo')}}

-{{ wtf.quick_form(form, - action=url_for('action_repo_add'), - method='post', - button_map={'submit': 'primary'}, - extra_classes="mx-5") }} +{% include 'repo_inspector.html' %} + {% endblock %} diff --git a/ogcp/templates/actions/repos_update.html b/ogcp/templates/actions/repos_update.html index 15c6b64..1278648 100644 --- a/ogcp/templates/actions/repos_update.html +++ b/ogcp/templates/actions/repos_update.html @@ -9,9 +9,6 @@

{{_('Update repo')}}

-{{ wtf.quick_form(form, - action=url_for('action_repo_update'), - method='post', - button_map={'submit': 'primary'}, - extra_classes="mx-5") }} +{% include 'repo_inspector.html' %} + {% endblock %} diff --git a/ogcp/templates/repo_inspector.html b/ogcp/templates/repo_inspector.html new file mode 100644 index 0000000..ccedf5f --- /dev/null +++ b/ogcp/templates/repo_inspector.html @@ -0,0 +1,63 @@ +
+ {{ form.hidden_tag() }} + + {{ form.server() }} + {{ form.repo_id() }} + +
+ {{ form.name.label }} + {{ form.name(class="form-control", required=True) }} +
+ +
+ {{ form.addr.label }} +
+ {% for addr in form.addr %} +
+ {{ addr(class="form-control me-2", placeholder=_("Enter IP Address"), required=True) }} + +
+ {% endfor %} +
+ + + {{ form.submit(class="btn btn-success") }} +
+
+ + \ No newline at end of file diff --git a/ogcp/views.py b/ogcp/views.py index 007ca91..86006ba 100644 --- a/ogcp/views.py +++ b/ogcp/views.py @@ -30,6 +30,7 @@ from ogcp.og_server import OGServer, servers from flask_babel import lazy_gettext as _l from flask_babel import gettext, _ from ogcp import app, ogcp_cfg_path +from wtforms import StringField import unicodedata import ipaddress import requests @@ -952,17 +953,10 @@ def action_image_restore(): if requires_cache and not image_fits_in_cache(server, clients_info, image): return redirect(url_for('commands')) - try: - repository = get_repository(image['repo_id'], server) - except ServerError: - return ogserver_down('commands') - except ServerErrorCode: - return ogserver_error('commands') - payload = {'disk': disk, 'partition': partition, 'name': image['name'], - 'repository': repository['ip'], + 'repository_id': image['repo_id'], 'clients': ips, 'type': form.method.data, 'profile': str(image['software_id']), @@ -1347,15 +1341,8 @@ def action_image_fetch(): flash(_(f'Image to fetch was not found'), category='error') return redirect(url_for('commands')) - try: - repository = get_repository(image['repo_id'], server) - except ServerError: - return ogserver_down('commands') - except ServerErrorCode: - return ogserver_error('commands') - payload = {'clients': ips, - 'repository': repository['ip'], + 'repository_id': image['repo_id'], 'type': form.method.data, 'image': image['name']} @@ -3040,6 +3027,34 @@ def manage_repos(): responses = multi_request('get', '/repositories') return render_template('repos.html', repos_resp=responses) +def repo_addr_is_valid(form): + invalid_ips = [] + empty_ips = 0 + ip_count = 0 + for ip in form.addr: + ip_count += 1 + ip = ip.data.strip() + if not ip: + empty_ips += 1 + continue + if not is_valid_ip(ip): + invalid_ips.append('"' + ip + '"') + + res = True + if empty_ips > 0: + res = False + flash(_(f'{empty_ips} addresses are invalid'), category='error') + + 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') + + return res + @app.route('/action/repo/add', methods=['POST', 'GET']) @login_required def action_repo_add(): @@ -3048,8 +3063,14 @@ def action_repo_add(): if not form.validate(): flash(form.errors, category='error') return redirect(url_for('manage_repos')) + + if not repo_addr_is_valid(form): + return redirect(url_for('manage_repos')) + + addr = [ip.data.strip() for ip in form.addr] + payload = {"name": form.name.data, - "ip": form.ip.data, + "addr": addr, "center": 1} server = get_server_from_ip_port(form.server.data) r = server.post('/repository/add', payload) @@ -3068,6 +3089,7 @@ def action_repo_add(): return redirect(url_for('manage_repos')) form.server.data = params['repos-server'] + form.addr.append_entry('') responses = multi_request('get', '/repositories') return render_template('actions/repos_add.html', form=form, @@ -3079,9 +3101,15 @@ def action_repo_update(): form = RepoForm(request.form) if request.method == 'POST': server = get_server_from_ip_port(form.server.data) - payload = { 'repo_id': int(form.repo_id.data), + + if not repo_addr_is_valid(form): + return redirect(url_for('manage_repos')) + + addr = [ip.data.strip() for ip in form.addr] + + payload = { 'id': int(form.repo_id.data), 'name': form.name.data, - 'ip': form.ip.data, + 'addr': addr, 'center': 1} r = server.post('/repository/update', payload) if not r: @@ -3115,7 +3143,8 @@ def action_repo_update(): form.server.data = server_ip_port form.repo_id.data = repo_id form.name.data = repository['name'] - form.ip.data = repository['ip'] + for addr in repository['addr']: + form.addr.append_entry(addr) responses = multi_request('get', '/repositories') return render_template('actions/repos_update.html', form=form, @@ -3127,7 +3156,7 @@ def action_repo_delete(): form = RepoForm(request.form) if request.method == 'POST': server = get_server_from_ip_port(form.server.data) - payload = { 'id': form.repo_id.data } + payload = { 'id': int(form.repo_id.data) } r = server.post('/repository/delete', payload) if not r: return ogserver_down('manage_repos') @@ -3165,8 +3194,11 @@ def action_repo_delete(): form.repo_id.data = repo_id form.name.data = repository['name'] form.name.render_kw = {'readonly': True} - form.ip.data = repository['ip'] - form.ip.render_kw = {'readonly': True} + form.name.data = repository['name'] + for addr in repository['addr']: + form.addr.append_entry(addr) + for field in form.addr: + field.render_kw = {'readonly': True} responses = multi_request('get', '/repositories') return render_template('actions/delete_repo.html', form=form, repos_resp=responses) @@ -3199,8 +3231,10 @@ def action_repo_info(): return ogserver_error('manage_repos') form.name.data = repository['name'] form.name.render_kw = {'readonly': True} - form.ip.data = repository['ip'] - form.ip.render_kw = {'readonly': True} + for addr in repository['addr']: + form.addr.append_entry(addr) + for field in form.addr: + field.render_kw = {'readonly': True} form.submit.render_kw = {"style": "visibility:hidden;"} responses = multi_request('get', '/repositories') return render_template('actions/repo_details.html', form=form, -- cgit v1.2.3-18-g5258