From 24e471eb1701fba5e2ca609a877b6a401ef03f6c Mon Sep 17 00:00:00 2001
From: Benjamin Bertrand <benjamin.bertrand@esss.se>
Date: Wed, 23 Oct 2019 18:52:54 +0200
Subject: [PATCH] Increase interface name max length

The interface name shall start by the host name. Its length must thus be
longer than the host.

JIRA INFRA-1324 #action In Progress
---
 app/models.py                   |  5 +++--
 app/network/forms.py            |  5 +++--
 app/validators.py               |  1 +
 tests/functional/test_models.py | 32 ++++++++++++++++++++++++++++++++
 4 files changed, 39 insertions(+), 4 deletions(-)

diff --git a/app/models.py b/app/models.py
index 0c32a0b..8408cc6 100644
--- a/app/models.py
+++ b/app/models.py
@@ -31,6 +31,7 @@ from .plugins import FlaskUserPlugin
 from .validators import (
     ICS_ID_RE,
     HOST_NAME_RE,
+    INTERFACE_NAME_RE,
     VLAN_NAME_RE,
     MAC_ADDRESS_RE,
     DEVICE_TYPE_RE,
@@ -1415,8 +1416,8 @@ class Interface(CreatedMixin, db.Model):
             return None
         # Force the string to lowercase
         lower_string = string.lower()
-        if HOST_NAME_RE.fullmatch(lower_string) is None:
-            raise ValidationError(r"Interface name shall match [a-z0-9\-]{2,20}")
+        if INTERFACE_NAME_RE.fullmatch(lower_string) is None:
+            raise ValidationError(r"Interface name shall match [a-z0-9\-]{2,25}")
         if self.host and not lower_string.startswith(self.host.name):
             raise ValidationError(
                 f"Interface name shall start with the host name '{self.host}'"
diff --git a/app/network/forms.py b/app/network/forms.py
index 5d0f9c5..30743fe 100644
--- a/app/network/forms.py
+++ b/app/network/forms.py
@@ -28,6 +28,7 @@ from ..validators import (
     RegexpList,
     IPNetwork,
     HOST_NAME_RE,
+    INTERFACE_NAME_RE,
     VLAN_NAME_RE,
     MAC_ADDRESS_RE,
     NoValidateSelectField,
@@ -183,10 +184,10 @@ class InterfaceForm(CSEntryForm):
     )
     interface_name = StringField(
         "Interface name",
-        description="name must be 2-20 characters long and contain only letters, numbers and dash",
+        description="name must be 2-25 characters long and contain only letters, numbers and dash",
         validators=[
             validators.InputRequired(),
-            validators.Regexp(HOST_NAME_RE),
+            validators.Regexp(INTERFACE_NAME_RE),
             Unique(models.Interface),
             starts_with_hostname,
             UniqueAccrossModels([models.Cname]),
diff --git a/app/validators.py b/app/validators.py
index 8684941..642da1d 100644
--- a/app/validators.py
+++ b/app/validators.py
@@ -16,6 +16,7 @@ from wtforms import ValidationError, SelectField
 
 ICS_ID_RE = re.compile(r"[A-Z]{3}[0-9]{3}")
 HOST_NAME_RE = re.compile(r"^[a-z0-9\-]{2,20}$")
+INTERFACE_NAME_RE = re.compile(r"^[a-z0-9\-]{2,25}$")
 VLAN_NAME_RE = re.compile(r"^[A-Za-z0-9\-]{3,25}$")
 MAC_ADDRESS_RE = re.compile(r"^(?:[0-9a-fA-F]{2}[:-]?){5}[0-9a-fA-F]{2}$")
 DEVICE_TYPE_RE = re.compile(r"^[A-Za-z0-9]{3,25}$")
diff --git a/tests/functional/test_models.py b/tests/functional/test_models.py
index 73d445b..c501486 100644
--- a/tests/functional/test_models.py
+++ b/tests/functional/test_models.py
@@ -767,3 +767,35 @@ def test_task_awx_job_url(db, task_factory):
     assert task4.awx_job_url is None
     task5 = task_factory(awx_resource="inventory_source", awx_job_id=12)
     assert task5.awx_job_url == "https://awx.example.org/#/jobs/inventory/12"
+
+
+@pytest.mark.parametrize("length", (1, 21, 50))
+def test_hostname_invalid_length(db, host_factory, length):
+    with pytest.raises(ValidationError) as excinfo:
+        host_factory(name="x" * length)
+    assert r"Host name shall match [a-z0-9\-]{2,20}" in str(excinfo.value)
+
+
+@pytest.mark.parametrize("name", ("my_host", "host@", "foo:bar", "U02.K02"))
+def test_hostname_invalid_characters(db, host_factory, name):
+    with pytest.raises(ValidationError) as excinfo:
+        host_factory(name=name)
+    assert r"Host name shall match [a-z0-9\-]{2,20}" in str(excinfo.value)
+
+
+@pytest.mark.parametrize("length", (1, 26, 50))
+def test_interface_name_invalid_length(db, interface_factory, length):
+    with pytest.raises(ValidationError) as excinfo:
+        interface_factory(name="x" * length)
+    assert r"Interface name shall match [a-z0-9\-]{2,25}" in str(excinfo.value)
+
+
+def test_interface_name_length(db, host_factory, interface_factory):
+    hostname = "x" * 20
+    interface_name = hostname + "-yyyy"
+    host1 = host_factory(name=hostname)
+    interface_factory(name=interface_name, host=host1)
+    assert host1.interfaces[0].name == interface_name
+    with pytest.raises(ValidationError) as excinfo:
+        interface_factory(name=interface_name + "y", host=host1)
+    assert r"Interface name shall match [a-z0-9\-]{2,25}" in str(excinfo.value)
-- 
GitLab