diff options
author | Javier Sánchez Parra <jsanchez@soleta.eu> | 2022-04-12 17:32:07 +0200 |
---|---|---|
committer | Javier Sánchez Parra <jsanchez@soleta.eu> | 2022-04-19 16:03:41 +0200 |
commit | 09884080c359b32576cf2c0a3128b481f5566932 (patch) | |
tree | 6e3ee5b8441e4977e09826c6747f3db25307af99 | |
parent | 87270dc8dfca246f54c5f0ccaa3f3d3e98020a2d (diff) |
Add import clients action
Add import clients form with required inputs: room and dhcpd.conf.
This permits users to rapidly add large amounts of clients to a room
using dhcpd.conf's syntax. Users can copy full dhcpd.conf files to the
text area and the parser only matches lines with the following format as
clients:
host dummy {hardware ethernet 12:34:56:78:90:ab; fixed-address 192.168.1.55; }
-rw-r--r-- | ogcp/forms/action_forms.py | 7 | ||||
-rw-r--r-- | ogcp/templates/actions/import_clients.html | 19 | ||||
-rw-r--r-- | ogcp/templates/scopes.html | 2 | ||||
-rw-r--r-- | ogcp/views.py | 63 |
4 files changed, 89 insertions, 2 deletions
diff --git a/ogcp/forms/action_forms.py b/ogcp/forms/action_forms.py index 5cabd54..a60115c 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, FormField, FieldList, DecimalField + StringField, RadioField, FormField, FieldList, DecimalField, TextAreaField ) from wtforms.validators import InputRequired from flask_wtf import FlaskForm @@ -119,6 +119,11 @@ class ClientDetailsForm(FlaskForm): boot = SelectField(label=_l('Boot Mode')) create = SubmitField(label=_l('Create')) +class ImportClientsForm(FlaskForm): + room = SelectField(label=_l('Room')) + dhcpd_conf = TextAreaField(label=_l('dhcpd configuration')) + import_btn = SubmitField(label=_l('Import')) + class BootModeForm(FlaskForm): ips = HiddenField() boot = SelectField(label=_l('Boot mode')) diff --git a/ogcp/templates/actions/import_clients.html b/ogcp/templates/actions/import_clients.html new file mode 100644 index 0000000..fc852d3 --- /dev/null +++ b/ogcp/templates/actions/import_clients.html @@ -0,0 +1,19 @@ +{% extends 'scopes.html' %} +{% import "bootstrap/wtf.html" as wtf %} + +{% set sidebar_state = 'disabled' %} +{% set btn_back = true %} + +{% block nav_client %}active{% endblock %} +{% block nav_clients_import %}active{% endblock %} +{% block content %} + +<h1 class="m-5">{{_('Import clients')}}</h1> + +{{ wtf.quick_form(form, + action=url_for('action_clients_import_post'), + method='post', + button_map={'submit': 'primary'}, + extra_classes="mx-5") }} + +{% endblock %} diff --git a/ogcp/templates/scopes.html b/ogcp/templates/scopes.html index 95d527a..751272d 100644 --- a/ogcp/templates/scopes.html +++ b/ogcp/templates/scopes.html @@ -23,6 +23,8 @@ <div class="dropdown-menu" aria-labelledby="dropdownMenuButton"> <input class="btn btn-light dropdown-item {% block nav_client_add %}{% endblock %}" type="submit" value="{{ _('Add client') }}" form="scopesForm" formaction="{{ url_for('action_client_add') }}" formmethod="get"> + <input class="btn btn-light dropdown-item {% block nav_clients_import %}{% endblock %}" type="submit" value="{{ _('Import clients') }}" + form="scopesForm" formaction="{{ url_for('action_clients_import_get') }}" formmethod="get"> <input class="btn btn-light dropdown-item {% block nav_client_delete %}{% endblock %}" type="submit" value="{{ _('Delete client') }}" form="scopesForm" formaction="{{ url_for('action_client_delete') }}" formmethod="get"> </div> diff --git a/ogcp/views.py b/ogcp/views.py index 938710d..a0d3e44 100644 --- a/ogcp/views.py +++ b/ogcp/views.py @@ -12,7 +12,7 @@ from ogcp.forms.action_forms import ( WOLForm, SetupForm, ClientDetailsForm, ImageDetailsForm, HardwareForm, SessionForm, ImageRestoreForm, ImageCreateForm, SoftwareForm, BootModeForm, RoomForm, DeleteRoomForm, CenterForm, DeleteCenterForm, OgliveForm, - GenericForm, SelectClientForm, ImageUpdateForm + GenericForm, SelectClientForm, ImageUpdateForm, ImportClientsForm ) from flask_login import ( current_user, LoginManager, @@ -30,6 +30,7 @@ from flask_babel import _ from ogcp import app import requests import datetime +import re FS_CODES = { 0: 'DISK', @@ -748,6 +749,66 @@ def action_client_add(): return render_template('actions/client_details.html', form=form, parent="scopes.html", scopes=scopes) +@app.route('/action/clients/import', methods=['GET']) +@login_required +def action_clients_import_get(): + form = ImportClientsForm() + r = g.server.get('/scopes') + rooms = parse_scopes_from_tree(r.json(), 'room') + rooms = [(room['id'], room['name'] + " (" + room['parent'] + ")") + for room in rooms] + form.room.choices = list(rooms) + scopes, _clients = get_scopes() + return render_template('actions/import_clients.html', form=form, + scopes=scopes) + + +OG_REGEX_DHCPD_CONF = (r'(?: *host *)' + r'([\w.-]*)' + r'(?: *{ *hardware *ethernet *)' + r'((?:[0-9A-Fa-f]{2}[:-]){5}(?:[0-9A-Fa-f]{2}))' + r'(?: *; *fixed-address *)' + r'(\d+\.\d+\.\d+\.\d+)' + r'(?: *; *})') +OG_CLIENT_DEFAULT_BOOT = "pxe" +OG_CLIENT_DEFAULT_LIVEDIR = "ogLive" +OG_CLIENT_DEFAULT_MAINTENANCE = False +OG_CLIENT_DEFAULT_NETDRIVER = "generic" +OG_CLIENT_DEFAULT_NETIFACE = "eth0" +OG_CLIENT_DEFAULT_NETMASK = "255.255.255.0" +OG_CLIENT_DEFAULT_REMOTE = False + + +@app.route('/action/clients/import', methods=['POST']) +@login_required +def action_clients_import_post(): + form = ImportClientsForm(request.form) + clients = re.findall(OG_REGEX_DHCPD_CONF, form.dhcpd_conf.data) + if not clients: + flash(_('No clients found. Check the dhcpd.conf file.'), + category='error') + return redirect(url_for('scopes')) + payload = {'boot': OG_CLIENT_DEFAULT_BOOT, + 'livedir': OG_CLIENT_DEFAULT_LIVEDIR, + 'maintenance': OG_CLIENT_DEFAULT_MAINTENANCE, + 'netdriver': OG_CLIENT_DEFAULT_NETDRIVER, + 'netiface': OG_CLIENT_DEFAULT_NETIFACE, + 'netmask': OG_CLIENT_DEFAULT_NETMASK, + 'remote': OG_CLIENT_DEFAULT_REMOTE, + 'room': int(form.room.data)} + for client in clients: + payload['name'] = client[0] + payload['mac'] = client[1].replace(':', '') + payload['ip'] = client[2] + resp = g.server.post('/client/add', payload) + if resp.status_code != requests.codes.ok: + flash(_('ogServer: error adding client {}').format(client[0]), + category='error') + return redirect(url_for('scopes')) + flash(_('Clients imported successfully'), category='info') + return redirect(url_for('scopes')) + + def get_selected_clients(scopes): selected_clients = dict() |