From a5905e5b34908d8511275fe7e76c8b22ec262e2c Mon Sep 17 00:00:00 2001 From: Benjamin Bertrand <benjamin.bertrand@esss.se> Date: Sat, 23 Dec 2017 00:18:26 +0100 Subject: [PATCH] Change interface mac select field to string field One should be able to enter a MAC when creating/editing an interface. --- app/helpers.py | 21 +++++++++++++++++++++ app/network/forms.py | 9 ++++++--- app/network/views.py | 21 +++++++++++++++++---- app/templates/network/create_host.html | 2 +- app/templates/network/create_interface.html | 2 +- app/templates/network/edit_interface.html | 2 +- app/validators.py | 2 +- 7 files changed, 48 insertions(+), 11 deletions(-) diff --git a/app/helpers.py b/app/helpers.py index 92db026..d68b378 100644 --- a/app/helpers.py +++ b/app/helpers.py @@ -9,7 +9,10 @@ This module implements helpers functions for the models. :license: BSD 2-Clause, see LICENSE for more details. """ +import sqlalchemy as sa from flask_wtf import FlaskForm +from .extensions import db +from . import models class CSEntryForm(FlaskForm): @@ -25,3 +28,21 @@ class CSEntryForm(FlaskForm): super().__init__(obj=obj, **kwargs) else: super().__init__(formdata=formdata, obj=obj, **kwargs) + + +def associate_mac_to_interface(address, interface): + """Associate the given address to an interface + + The Mac is retrieved if it exists or created otherwise + + :param address: Mac address string + :param interface: Interface instance + """ + if not address: + return + try: + mac = models.Mac.query.filter_by(address=address).one() + except sa.orm.exc.NoResultFound: + mac = models.Mac(address=address) + db.session.add(mac) + mac.interfaces.append(interface) diff --git a/app/network/forms.py b/app/network/forms.py index c19392b..145a8a9 100644 --- a/app/network/forms.py +++ b/app/network/forms.py @@ -13,7 +13,8 @@ from flask_login import current_user from wtforms import (SelectField, StringField, TextAreaField, IntegerField, SelectMultipleField, BooleanField, validators) from ..helpers import CSEntryForm -from ..validators import Unique, RegexpList, IPNetwork, HOST_NAME_RE, VLAN_NAME_RE +from ..validators import (Unique, RegexpList, IPNetwork, HOST_NAME_RE, + VLAN_NAME_RE, MAC_ADDRESS_RE) from .. import utils, models @@ -103,7 +104,10 @@ class InterfaceForm(CSEntryForm): validators.Regexp(HOST_NAME_RE), Unique(models.Interface)], filters=[utils.lowercase_field]) - mac_id = SelectField('MAC', coerce=utils.coerce_to_str_or_none) + mac_address = StringField( + 'MAC', + validators=[validators.Optional(), + validators.Regexp(MAC_ADDRESS_RE, message='Invalid MAC address')]) cnames_string = StringField( 'Cnames', description='space separated list of cnames (must be 2-20 characters long and contain only letters, numbers and dash)', @@ -121,7 +125,6 @@ class InterfaceForm(CSEntryForm): network_query = models.Network.query.filter(models.Network.admin_only.is_(False)) self.network_id.choices = utils.get_model_choices(models.Network, allow_none=False, attr='vlan_name', query=network_query) - self.mac_id.choices = utils.get_model_choices(models.Mac, allow_none=True, attr='address') self.tags.choices = utils.get_model_choices(models.Tag, allow_none=True, attr='name') diff --git a/app/network/views.py b/app/network/views.py index f17b43c..d7c09cb 100644 --- a/app/network/views.py +++ b/app/network/views.py @@ -18,7 +18,7 @@ from .forms import (HostForm, InterfaceForm, HostInterfaceForm, NetworkForm, NetworkScopeForm) from ..extensions import db from ..decorators import login_groups_accepted -from .. import models, utils +from .. import models, utils, helpers bp = Blueprint('network', __name__) @@ -58,9 +58,9 @@ def create_host(): interface = models.Interface(name=form.interface_name.data, ip=form.ip.data, network_id=network_id, - mac_id=form.mac_id.data, tags=tags) interface.cnames = [models.Cname(name=name) for name in form.cnames_string.data.split()] + helpers.associate_mac_to_interface(form.mac_address.data, interface) host.interfaces = [interface] current_app.logger.debug(f'Trying to create: {host!r}') db.session.add(host) @@ -123,9 +123,9 @@ def create_interface(hostname): name=form.interface_name.data, ip=form.ip.data, network_id=form.network_id.data, - mac_id=form.mac_id.data, tags=tags) interface.cnames = [models.Cname(name=name) for name in form.cnames_string.data.split()] + helpers.associate_mac_to_interface(form.mac_address.data, interface) current_app.logger.debug(f'Trying to create: {interface!r}') db.session.add(interface) try: @@ -145,8 +145,13 @@ def create_interface(hostname): def edit_interface(name): interface = models.Interface.query.filter_by(name=name).first_or_404() cnames_string = ' '.join([str(cname) for cname in interface.cnames]) + try: + mac_address = interface.mac.address + except AttributeError: + mac_address = '' form = InterfaceForm(request.form, obj=interface, interface_name=interface.name, + mac_address=mac_address, cnames_string=cnames_string) ips = [interface.ip] ips.extend([str(address) for address in interface.network.available_ips()]) @@ -155,7 +160,15 @@ def edit_interface(name): interface.name = form.interface_name.data interface.ip = form.ip.data interface.network_id = form.network_id.data - interface.mac_id = form.mac_id.data + if form.mac_address.data: + if form.mac_address.data != mac_address: + # The MAC changed - add the new one to the interface + # that will remove the association to the previous one + helpers.associate_mac_to_interface(form.mac_address.data, interface) + # else: nothing to do (address didn't change) + else: + # No MAC associated + interface.mac_id = None # Delete the cnames that have been removed new_cnames_string = form.cnames_string.data.split() for (index, cname) in enumerate(interface.cnames): diff --git a/app/templates/network/create_host.html b/app/templates/network/create_host.html index b2b0548..775a29b 100644 --- a/app/templates/network/create_host.html +++ b/app/templates/network/create_host.html @@ -13,7 +13,7 @@ {{ render_field(form.network_id) }} {{ render_field(form.ip) }} {{ render_field(form.interface_name, class_="text-lowercase") }} - {{ render_field(form.mac_id) }} + {{ render_field(form.mac_address) }} {{ render_field(form.cnames_string) }} {{ render_field(form.tags) }} <div class="form-group row"> diff --git a/app/templates/network/create_interface.html b/app/templates/network/create_interface.html index 01f4688..2da2cdb 100644 --- a/app/templates/network/create_interface.html +++ b/app/templates/network/create_interface.html @@ -23,7 +23,7 @@ {{ render_field(form.interface_name, class_="text-lowercase") }} {{ render_field(form.network_id) }} {{ render_field(form.ip) }} - {{ render_field(form.mac_id) }} + {{ render_field(form.mac_address) }} {{ render_field(form.cnames_string) }} {{ render_field(form.tags) }} <div class="form-group row"> diff --git a/app/templates/network/edit_interface.html b/app/templates/network/edit_interface.html index 621feb7..5ddaf85 100644 --- a/app/templates/network/edit_interface.html +++ b/app/templates/network/edit_interface.html @@ -26,7 +26,7 @@ {{ render_field(form.interface_name, class_="text-lowercase") }} {{ render_field(form.network_id) }} {{ render_field(form.ip) }} - {{ render_field(form.mac_id) }} + {{ render_field(form.mac_address) }} {{ render_field(form.cnames_string) }} {{ render_field(form.tags) }} <div class="form-group row"> diff --git a/app/validators.py b/app/validators.py index 004543e..6c8ae87 100644 --- a/app/validators.py +++ b/app/validators.py @@ -17,7 +17,7 @@ from wtforms import ValidationError ICS_ID_RE = re.compile('[A-Z]{3}[0-9]{3}') HOST_NAME_RE = re.compile('^[a-z0-9\-]{2,20}$') VLAN_NAME_RE = re.compile('^[A-Za-z0-9\-]{3,25}$') -MAC_ADDRESS_RE = re.compile('^(?:[0-9a-fA-F]{2}[:-]){5}[0-9a-fA-F]{2}$') +MAC_ADDRESS_RE = re.compile('^(?:[0-9a-fA-F]{2}[:-]?){5}[0-9a-fA-F]{2}$') class IPNetwork: -- GitLab