summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJavier Sánchez Parra <jsanchez@soleta.eu>2022-04-12 17:32:07 +0200
committerJavier Sánchez Parra <jsanchez@soleta.eu>2022-04-19 16:03:41 +0200
commit09884080c359b32576cf2c0a3128b481f5566932 (patch)
tree6e3ee5b8441e4977e09826c6747f3db25307af99
parent87270dc8dfca246f54c5f0ccaa3f3d3e98020a2d (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.py7
-rw-r--r--ogcp/templates/actions/import_clients.html19
-rw-r--r--ogcp/templates/scopes.html2
-rw-r--r--ogcp/views.py63
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()