diff --git a/app/models.py b/app/models.py index 0214a12f5e50c23787a9656c63456362edc3c7c8..41a0ed6729287b2cc4d2addf67b36a1fa00169f9 100644 --- a/app/models.py +++ b/app/models.py @@ -1134,13 +1134,18 @@ class Host(CreatedMixin, SearchableMixin, db.Model): ) ansible_vars = db.Column(postgresql.JSONB) - # Set cascade to all (to add delete) and delete-orphan to delete all interfaces + # 1. Set cascade to all (to add delete) and delete-orphan to delete all interfaces # when deleting a host + # 2. Return interfaces sorted by name so that the main one (the one starting with + # the same name as the host) is always the first one. + # As an interface name always has to start with the name of the host, the one + # matching the host name will always come first. interfaces = db.relationship( "Interface", backref=db.backref("host", lazy="joined"), cascade="all, delete-orphan", lazy="joined", + order_by="Interface.name", ) items = db.relationship( "Item", backref=db.backref("host", lazy="joined"), lazy="joined" @@ -1194,11 +1199,7 @@ class Host(CreatedMixin, SearchableMixin, db.Model): The main interface is the one that has the same name as the host or the first one found """ - for interface in self.interfaces: - if interface.name == self.name: - return interface - # No interface with the same name found... - # Return the first one + # As interfaces are sorted, the first one is always the main one try: return self.interfaces[0] except IndexError: diff --git a/app/network/views.py b/app/network/views.py index 756d35ccbbf3ee03d00681efdfbe76bd975687a2..f69ff4319c49d25f97e3a679f4dd58688a18b6c2 100644 --- a/app/network/views.py +++ b/app/network/views.py @@ -143,6 +143,14 @@ def delete_host(): @login_required def view_host(name): host = models.Host.query.filter_by(name=name).first_or_404() + if host.main_interface is None: + flash(f"Host {host.name} has no interface! Add one or delete it.", "warning") + elif host.main_interface.name != host.name: + flash( + f"The main interface '{host.main_interface.name}' shall have the same name as the host!" + f" Please rename it '{host.name}'.", + "warning", + ) if host.device_type.name == "Network": form = GenerateZTPConfigForm() elif host.device_type.name.startswith("Virtual"): diff --git a/tests/functional/test_api.py b/tests/functional/test_api.py index acecb7f57baf4f5bec5d84ad691fc0efa60ef232..1c2960714f106f8f68b706ee1b097c961427322a 100644 --- a/tests/functional/test_api.py +++ b/tests/functional/test_api.py @@ -1357,8 +1357,8 @@ def test_get_hosts_recursive(client, host_factory, interface_factory, readonly_t assert response.status_code == 200 assert len(response.get_json()) == 2 rhost1, rhost2 = response.get_json() - # We can't be sure in which order the interfaces are returned - assert set(rhost1["interfaces"]) == {interface11.name, interface12.name} + # Interfaces shall be sorted + assert rhost1["interfaces"] == sorted([interface11.name, interface12.name]) assert rhost2["interfaces"] == [interface21.name] # With recursive, interfaces are expanded response = get( diff --git a/tests/functional/test_models.py b/tests/functional/test_models.py index 4c25661f547091cd97fe29a17195bafd1fc7c3e1..a35d2633877fffaaa9f84134d19abbfc2c7e14c6 100644 --- a/tests/functional/test_models.py +++ b/tests/functional/test_models.py @@ -441,25 +441,29 @@ def test_interface_name_existing_cname( def test_interface_is_main(host_factory, interface_factory): # The interface with the same name as the host is the main one host1 = host_factory(name="myhost") - interface11 = interface_factory(name=host1.name, host=host1) - interface12 = interface_factory(name=host1.name + "-2", host=host1) + interface11 = interface_factory(name=host1.name + "-2", host=host1) + interface12 = interface_factory(name=host1.name, host=host1) interface13 = interface_factory(name=host1.name + "-3", host=host1) - assert interface11.is_main - assert not interface12.is_main + assert interface12.is_main + assert not interface11.is_main assert not interface13.is_main + # Interfaces are sorted by name + assert host1.interfaces == [interface12, interface11, interface13] host2 = host_factory(name="anotherhost") interface21 = interface_factory(name=host2.name + "-1", host=host2) # If no interface has the same name as the host, the first one is the main assert interface21.is_main interface22 = interface_factory(name=host2.name + "-2", host=host2) # The first interface in the list is the main one - assert host2.interfaces[0].is_main - assert not host2.interfaces[1].is_main + assert host2.interfaces == [interface21, interface22] + assert interface21.is_main + assert not interface22.is_main interface23 = interface_factory(name=host2.name, host=host2) # The new interface has the same name as the host, so this is the main one assert not interface21.is_main assert not interface22.is_main assert interface23.is_main + assert host2.interfaces == [interface23, interface21, interface22] def test_host_existing_interface(db, host_factory, interface):