File items_Fix_creation_of_default_NetworkInterface.patch of Package cobbler
From 689cb2371078196d31c739f20e3f7459d2cec3fa Mon Sep 17 00:00:00 2001
From: Bruno Travouillon <devel@travouillon.fr>
Date: Fri, 3 Jun 2022 23:20:54 -0400
Subject: [PATCH 1/3] Items: Fix creation of "default" NetworkInterface
Do not force creation of a network interface named `default`.
Closes: #2838
From 2d04011b4535fb4011189cfae68509c2870cd2b0 Mon Sep 17 00:00:00 2001
From: Bruno Travouillon <devel@travouillon.fr>
Date: Sat, 4 Jun 2022 11:27:41 -0400
Subject: [PATCH 2/3] Tests: Define default system interface explicitly
Do not assume the `default` interface is implicitly created with a
system.
From 7ad5981fec4c7f68b847b112708f6ca6de27a9fc Mon Sep 17 00:00:00 2001
From: Bruno Travouillon <devel@travouillon.fr>
Date: Tue, 7 Jun 2022 21:31:47 -0400
Subject: [PATCH 3/3] Tests: Create systems in xmlrpcapi
Ensure that:
- only the `default` network interface exists when no `--interface`.
- only one interface exists when interface name is provided.
- `--interface` is optional with several interfaces and `default`.
- `--interface` is mandatory with several interfaces and no `default`.
---
cobbler/items/system.py | 2
cobbler/remote.py | 18 +
tests/conftest.py | 4
tests/items/system_test.py | 2
tests/modules/managers/dnsmasq_test.py | 5
tests/modules/managers/ndjbdns_test.py | 3
tests/xmlrpcapi/miscellaneous_test.py | 299 +++++++++++++++++++++++++++++++++
7 files changed, 326 insertions(+), 7 deletions(-)
Index: cobbler-3.3.3/cobbler/items/system.py
===================================================================
--- cobbler-3.3.3.orig/cobbler/items/system.py
+++ cobbler-3.3.3/cobbler/items/system.py
@@ -743,7 +743,7 @@ class System(Item):
:param api: The Cobbler API
"""
super().__init__(api, *args, **kwargs)
- self._interfaces: Dict[str, NetworkInterface] = {"default": NetworkInterface(api)}
+ self._interfaces: Dict[str, NetworkInterface] = {}
self._ipv6_autoconfiguration = False
self._repos_enabled = False
self._autoinstall = enums.VALUE_INHERITED
Index: cobbler-3.3.3/cobbler/remote.py
===================================================================
--- cobbler-3.3.3.orig/cobbler/remote.py
+++ cobbler-3.3.3/cobbler/remote.py
@@ -2151,8 +2151,22 @@ class CobblerXMLRPCInterface:
system_to_edit = self.__get_object(handle)
if system_to_edit is None:
raise ValueError("No system found with the specified name (name given: \"%s\")!" % object_name)
- # If we don't have an explicit interface name use the default interface
- interface_name = attributes.get("interface", "default")
+
+ # If we don't have an explicit interface name use the default interface or require an explicit
+ # interface if default cannot be found.
+ if (
+ len(system_to_edit.interfaces) > 1
+ and attributes.get("interface") is None
+ ):
+ if "default" not in system_to_edit.interfaces.keys():
+ raise ValueError("Interface is required.")
+ interface_name = "default"
+ if len(system_to_edit.interfaces) == 1:
+ interface_name = attributes.get(
+ "interface", next(iter(system_to_edit.interfaces))
+ )
+ else:
+ interface_name = attributes.get("interface", "default")
self.logger.debug("Interface \"%s\" is being edited.", interface_name)
interface = system_to_edit.interfaces.get(interface_name)
if interface is None:
Index: cobbler-3.3.3/tests/conftest.py
===================================================================
--- cobbler-3.3.3.orig/tests/conftest.py
+++ cobbler-3.3.3/tests/conftest.py
@@ -8,8 +8,8 @@ import pytest
from cobbler.api import CobblerAPI
from cobbler.items.distro import Distro
from cobbler.items.profile import Profile
-from cobbler.items.system import System
from cobbler.items.image import Image
+from cobbler.items.system import NetworkInterface, System
@contextmanager
@@ -142,6 +142,7 @@ def create_system(request, cobbler_api):
test_system.profile = profile_name
if image_name != "":
test_system.image = image_name
+ test_system.interfaces = {"default": NetworkInterface(cobbler_api)}
cobbler_api.add_system(test_system)
return test_system
Index: cobbler-3.3.3/tests/items/system_test.py
===================================================================
--- cobbler-3.3.3.orig/tests/items/system_test.py
+++ cobbler-3.3.3/tests/items/system_test.py
@@ -644,6 +644,7 @@ def test_serial_baud_rate(cobbler_api, v
def test_from_dict_with_network_interface(cobbler_api):
# Arrange
system = System(cobbler_api)
+ system.interfaces = {"default": NetworkInterface(cobbler_api)}
sys_dict = system.to_dict()
# Act
@@ -663,6 +664,7 @@ def test_from_dict_with_network_interfac
def test_is_management_supported(cobbler_api, input_mac, input_ipv4, input_ipv6, expected_result):
# Arrange
system = System(cobbler_api)
+ system.interfaces = {"default": NetworkInterface(cobbler_api)}
system.interfaces["default"].mac_address = input_mac
system.interfaces["default"].ip_address = input_ipv4
system.interfaces["default"].ipv6_address = input_ipv6
Index: cobbler-3.3.3/tests/modules/managers/dnsmasq_test.py
===================================================================
--- cobbler-3.3.3.orig/tests/modules/managers/dnsmasq_test.py
+++ cobbler-3.3.3/tests/modules/managers/dnsmasq_test.py
@@ -2,7 +2,7 @@ import time
from unittest.mock import MagicMock
from cobbler.modules.managers import dnsmasq
-from cobbler.items.system import System
+from cobbler.items.system import NetworkInterface, System
from cobbler.items.distro import Distro
from cobbler.items.profile import Profile
from cobbler.templar import Templar
@@ -41,6 +41,7 @@ def test_manager_write_configs(mocker, c
mock_profile = Profile(cobbler_api)
mock_system = System(cobbler_api)
mock_system.name = "test_manager_regen_hosts_system"
+ mock_system.interfaces = {"default": NetworkInterface(cobbler_api)}
mock_system.interfaces["default"].dns_name = "host.example.org"
mock_system.interfaces["default"].mac_address = "aa:bb:cc:dd:ee:ff"
mock_system.interfaces["default"].ip_address = "192.168.1.2"
@@ -74,6 +75,7 @@ def test_manager_regen_ethers(mocker, co
mock_builtins_open = mocker.patch("builtins.open", mocker.mock_open())
mock_system = System(cobbler_api)
mock_system.name = "test_manager_regen_ethers_system"
+ mock_system.interfaces = {"default": NetworkInterface(cobbler_api)}
mock_system.interfaces["default"].dns_name = "host.example.org"
mock_system.interfaces["default"].mac_address = "aa:bb:cc:dd:ee:ff"
mock_system.interfaces["default"].ip_address = "192.168.1.2"
@@ -96,6 +98,7 @@ def test_manager_regen_hosts(mocker, cob
mock_builtins_open = mocker.patch("builtins.open", mocker.mock_open())
mock_system = System(cobbler_api)
mock_system.name = "test_manager_regen_hosts_system"
+ mock_system.interfaces = {"default": NetworkInterface(cobbler_api)}
mock_system.interfaces["default"].dns_name = "host.example.org"
mock_system.interfaces["default"].mac_address = "AA:BB:CC:DD:EE:FF"
mock_system.interfaces["default"].ip_address = "192.168.1.2"
Index: cobbler-3.3.3/tests/modules/managers/ndjbdns_test.py
===================================================================
--- cobbler-3.3.3.orig/tests/modules/managers/ndjbdns_test.py
+++ cobbler-3.3.3/tests/modules/managers/ndjbdns_test.py
@@ -2,7 +2,7 @@ import subprocess
from unittest.mock import MagicMock
from cobbler.modules.managers import ndjbdns
-from cobbler.items.system import System
+from cobbler.items.system import NetworkInterface, System
from cobbler.templar import Templar
@@ -36,6 +36,7 @@ def test_manager_write_configs(mocker, c
mock_subproc_popen.return_value.returncode = 0
mock_system = System(cobbler_api)
mock_system.name = "test_manager_regen_hosts_system"
+ mock_system.interfaces = {"default": NetworkInterface(cobbler_api)}
mock_system.interfaces["default"].dns_name = "host.example.org"
mock_system.interfaces["default"].mac_address = "aa:bb:cc:dd:ee:ff"
mock_system.interfaces["default"].ip_address = "192.168.1.2"
Index: cobbler-3.3.3/tests/xmlrpcapi/miscellaneous_test.py
===================================================================
--- cobbler-3.3.3.orig/tests/xmlrpcapi/miscellaneous_test.py
+++ cobbler-3.3.3/tests/xmlrpcapi/miscellaneous_test.py
@@ -720,6 +720,305 @@ class TestMiscellaneous:
# Assert
assert result
+ def test_xapi_system_edit(
+ self,
+ remote,
+ token,
+ create_kernel_initrd,
+ create_distro,
+ create_profile,
+ remove_system,
+ ):
+ # Arrange
+ name_distro = "testsystem_xapi_edit"
+ name_profile = "testsystem_xapi_edit"
+ name_system = "testsystem_xapi_edit"
+ fk_kernel = "vmlinuz1"
+ fk_initrd = "initrd1.img"
+ basepath = create_kernel_initrd(fk_kernel, fk_initrd)
+ path_kernel = os.path.join(basepath, fk_kernel)
+ path_initrd = os.path.join(basepath, fk_initrd)
+ create_distro(name_distro, "x86_64", "suse", path_kernel, path_initrd)
+ create_profile(name_profile, name_distro, "a=1 b=2 c=3 c=4 c=5 d e")
+
+ # Act
+ result = remote.xapi_object_edit(
+ "system",
+ name_system,
+ "add",
+ {
+ "name": name_system,
+ "profile": name_profile,
+ },
+ token,
+ )
+
+ # Assert
+ assert result
+ assert len(remote.get_system("testsystem_xapi_edit").get("interfaces", {})) == 1
+ assert "default" in remote.get_system("testsystem_xapi_edit").get(
+ "interfaces", {}
+ )
+
+ # Cleanup
+ remove_system(name_system)
+
+ def test_xapi_system_edit_interface_name(
+ self,
+ remote,
+ token,
+ create_kernel_initrd,
+ create_distro,
+ create_profile,
+ remove_system,
+ ):
+ # Arrange
+ name_distro = "testsystem_xapi_edit"
+ name_profile = "testsystem_xapi_edit"
+ name_system = "testsystem_xapi_edit"
+ fk_kernel = "vmlinuz1"
+ fk_initrd = "initrd1.img"
+ basepath = create_kernel_initrd(fk_kernel, fk_initrd)
+ path_kernel = os.path.join(basepath, fk_kernel)
+ path_initrd = os.path.join(basepath, fk_initrd)
+ create_distro(name_distro, "x86_64", "suse", path_kernel, path_initrd)
+ create_profile(name_profile, name_distro, "a=1 b=2 c=3 c=4 c=5 d e")
+
+ # Act
+ result = remote.xapi_object_edit(
+ "system",
+ name_system,
+ "add",
+ {
+ "name": name_system,
+ "profile": name_profile,
+ "interface": "eth1",
+ },
+ token,
+ )
+
+ # Assert
+ assert result
+ assert len(remote.get_system("testsystem_xapi_edit").get("interfaces", {})) == 1
+ assert "eth1" in remote.get_system("testsystem_xapi_edit").get("interfaces", {})
+
+ def test_xapi_system_edit_two_interfaces(
+ self,
+ remote,
+ token,
+ create_kernel_initrd,
+ create_distro,
+ create_profile,
+ ):
+ # Arrange
+ name_distro = "testsystem_xapi_edit"
+ name_profile = "testsystem_xapi_edit"
+ name_system = "testsystem_xapi_edit"
+ fk_kernel = "vmlinuz1"
+ fk_initrd = "initrd1.img"
+ basepath = create_kernel_initrd(fk_kernel, fk_initrd)
+ path_kernel = os.path.join(basepath, fk_kernel)
+ path_initrd = os.path.join(basepath, fk_initrd)
+ create_distro(name_distro, "x86_64", "suse", path_kernel, path_initrd)
+ create_profile(name_profile, name_distro, "a=1 b=2 c=3 c=4 c=5 d e")
+
+ # Act
+ result_add = remote.xapi_object_edit(
+ "system",
+ name_system,
+ "add",
+ {
+ "name": name_system,
+ "profile": name_profile,
+ },
+ token,
+ )
+ result_edit = remote.xapi_object_edit(
+ "system",
+ name_system,
+ "edit",
+ {
+ "name": name_system,
+ "interface": "eth1",
+ },
+ token,
+ )
+
+ # Assert
+ assert result_add
+ assert result_edit
+ assert len(remote.get_system("testsystem_xapi_edit").get("interfaces", {})) == 2
+ assert "default" in remote.get_system("testsystem_xapi_edit").get(
+ "interfaces", {}
+ )
+ assert "eth1" in remote.get_system("testsystem_xapi_edit").get("interfaces", {})
+
+ def test_xapi_system_edit_two_interfaces_no_default(
+ self,
+ remote,
+ token,
+ create_kernel_initrd,
+ create_distro,
+ create_profile,
+ ):
+ # Arrange
+ name_distro = "testsystem_xapi_edit"
+ name_profile = "testsystem_xapi_edit"
+ name_system = "testsystem_xapi_edit"
+ fk_kernel = "vmlinuz1"
+ fk_initrd = "initrd1.img"
+ basepath = create_kernel_initrd(fk_kernel, fk_initrd)
+ path_kernel = os.path.join(basepath, fk_kernel)
+ path_initrd = os.path.join(basepath, fk_initrd)
+ create_distro(name_distro, "x86_64", "suse", path_kernel, path_initrd)
+ create_profile(name_profile, name_distro, "a=1 b=2 c=3 c=4 c=5 d e")
+
+ # Act
+ result_add = remote.xapi_object_edit(
+ "system",
+ name_system,
+ "add",
+ {
+ "name": name_system,
+ "profile": name_profile,
+ "interface": "eth1",
+ },
+ token,
+ )
+ result_edit = remote.xapi_object_edit(
+ "system",
+ name_system,
+ "edit",
+ {
+ "name": name_system,
+ "interface": "eth2",
+ },
+ token,
+ )
+
+ # Assert
+ assert result_add
+ assert result_edit
+ assert len(remote.get_system("testsystem_xapi_edit").get("interfaces", {})) == 2
+ assert "eth1" in remote.get_system("testsystem_xapi_edit").get("interfaces", {})
+ assert "eth2" in remote.get_system("testsystem_xapi_edit").get("interfaces", {})
+
+ def test_xapi_system_edit_two_interfaces_default(
+ self,
+ remote,
+ token,
+ create_kernel_initrd,
+ create_distro,
+ create_profile,
+ ):
+ # Arrange
+ name_distro = "testsystem_xapi_edit"
+ name_profile = "testsystem_xapi_edit"
+ name_system = "testsystem_xapi_edit"
+ fk_kernel = "vmlinuz1"
+ fk_initrd = "initrd1.img"
+ basepath = create_kernel_initrd(fk_kernel, fk_initrd)
+ path_kernel = os.path.join(basepath, fk_kernel)
+ path_initrd = os.path.join(basepath, fk_initrd)
+ create_distro(name_distro, "x86_64", "suse", path_kernel, path_initrd)
+ create_profile(name_profile, name_distro, "a=1 b=2 c=3 c=4 c=5 d e")
+ remote.xapi_object_edit(
+ "system",
+ name_system,
+ "add",
+ {
+ "name": name_system,
+ "profile": name_profile,
+ },
+ token,
+ )
+ remote.xapi_object_edit(
+ "system",
+ name_system,
+ "edit",
+ {
+ "name": name_system,
+ "interface": "eth2",
+ },
+ token,
+ )
+
+ # Act
+ result = remote.xapi_object_edit(
+ "system",
+ name_system,
+ "edit",
+ {
+ "name": name_system,
+ "mac_address": "aa:bb:cc:dd:ee:ff",
+ },
+ token,
+ )
+
+ # Assert
+ assert result
+ assert (
+ remote.get_system(name_system)
+ .get("interfaces", {})
+ .get("default", {})
+ .get("mac_address")
+ == "aa:bb:cc:dd:ee:ff"
+ )
+
+ def test_xapi_system_edit_two_interfaces_no_default_negative(
+ self,
+ remote,
+ token,
+ create_kernel_initrd,
+ create_distro,
+ create_profile,
+ ):
+ # Arrange
+ name_distro = "testsystem_xapi_edit"
+ name_profile = "testsystem_xapi_edit"
+ name_system = "testsystem_xapi_edit"
+ fk_kernel = "vmlinuz1"
+ fk_initrd = "initrd1.img"
+ basepath = create_kernel_initrd(fk_kernel, fk_initrd)
+ path_kernel = os.path.join(basepath, fk_kernel)
+ path_initrd = os.path.join(basepath, fk_initrd)
+ create_distro(name_distro, "x86_64", "suse", path_kernel, path_initrd)
+ create_profile(name_profile, name_distro, "a=1 b=2 c=3 c=4 c=5 d e")
+ remote.xapi_object_edit(
+ "system",
+ name_system,
+ "add",
+ {
+ "name": name_system,
+ "profile": name_profile,
+ "interface": "eth1",
+ },
+ token,
+ )
+ remote.xapi_object_edit(
+ "system",
+ name_system,
+ "edit",
+ {
+ "name": name_system,
+ "interface": "eth2",
+ },
+ token,
+ )
+
+ # Act & Assert
+ with pytest.raises(ValueError):
+ remote.xapi_object_edit(
+ "system",
+ name_system,
+ "edit",
+ {
+ "name": name_system,
+ "mac_address": "aa:bb:cc:dd:ee:ff",
+ },
+ token,
+ )
+
@pytest.mark.usefixtures(
"create_testdistro",
"create_testmenu",