diff --git a/ocaml/xenopsd/scripts/common.py b/ocaml/xenopsd/scripts/common.py index af8666ce62c..1b062e80ed4 100755 --- a/ocaml/xenopsd/scripts/common.py +++ b/ocaml/xenopsd/scripts/common.py @@ -159,10 +159,10 @@ def get_ethtool(self): for (k, v) in self.json["other_config"]: if k.startswith("ethtool-"): k = k[len("ethtool-"):] - if v == "true" or v == "on": - results.append(k, True) - elif v == "false" or v == "off": - results.append(k, False) + if v in ("true", "on"): + results.append((k, True)) + elif v in ("false", "off"): + results.append((k, False)) else: send_to_syslog("VIF %s/%d: ignoring ethtool argument %s=%s (use true/false)" % (self.vm_uuid, self.devid, k, v)) return results @@ -192,28 +192,33 @@ def get_external_ids(self): results["xs-network-uuid"] = self.json["extra_private_keys"]["network-uuid"] results["attached-mac"] = self.get_mac() return results + def get_locking_mode(self): - def get_words(value, separator): - if string.strip(value) == "": - return [] - else: - return string.split(value, separator) + """ + Get the locking mode configuration for the VIF. + + :returns dict: A dictionary containing the locking mode configuration with keys: + - mac: The MAC address + - locking_mode: The locking mode + - ipv4_allowed: List of IPv4 addresses allowed + - ipv6_allowed: List of IPv6 addresses allowed + """ results = { "mac": self.get_mac(), "locking_mode": "", "ipv4_allowed": [], - "ipv6_allowed": [] + "ipv6_allowed": [], } if "locking_mode" in self.json: - if type(self.json["locking_mode"]) is list: - # Must be type=locked here + if isinstance(self.json["locking_mode"], list): + # Must be type=locked and have keys for allowed ipv4 and ipv6 addresses results["locking_mode"] = self.json["locking_mode"][0].lower() - locked_params=self.json["locking_mode"][1] + locked_params = self.json["locking_mode"][1] results["ipv4_allowed"] = locked_params["ipv4"] results["ipv6_allowed"] = locked_params["ipv6"] else: results["locking_mode"] = self.json["locking_mode"].lower() - send_to_syslog("Got locking config: %s" % (repr(results))) + send_to_syslog("Got locking config: " + repr(results)) return results class Interface: diff --git a/ocaml/xenopsd/scripts/test_common_class_vif.py b/ocaml/xenopsd/scripts/test_common_class_vif.py new file mode 100644 index 00000000000..8ae264956f4 --- /dev/null +++ b/ocaml/xenopsd/scripts/test_common_class_vif.py @@ -0,0 +1,119 @@ +"""Test ocaml/xenopsd/scripts/common.VIF.get_locking_mode()""" + +from unittest.mock import patch # to check the arguments passed to send_to_syslog() + +import pytest # for pytest.parametrize to run the same test with different parameters + +import common # Tested module + + +# Mock class to simulate the object containing the get_locking_mode method +class VifMockSubclass(common.VIF): + """Mock class to simulate a VIF object containing the get_locking_mode method""" + + def __init__(self, json): # pylint: disable=super-init-not-called + """Do not call the parent constructor, it would open a file""" + self.json = json + + def get_mac(self): + return "00:11:22:33:44:55" # Expected MAC address + + +@pytest.mark.parametrize( + # Call the test case 3 times with two args: + # inp: input for VIF.get_locking_mode() + # expected_output: expected output of the get_locking_mode method + # Asserted with: + # assert expected_output == get_locking_mode(input) + "input_params, expected_output", + [ + # Happy path tests + ( + # locked + { # input + "locking_mode": [ + "locked", + {"ipv4": ["1.1.1.1"], "ipv6": ["fe80::1"]}, + ] + }, # expected output + { + "mac": "00:11:22:33:44:55", + "locking_mode": "locked", + "ipv4_allowed": ["1.1.1.1"], + "ipv6_allowed": ["fe80::1"], + }, + ), + ( + # unlocked + {"locking_mode": "unlocked"}, + { + "mac": "00:11:22:33:44:55", + "locking_mode": "unlocked", + "ipv4_allowed": [], + "ipv6_allowed": [], + }, + ), + ( + {}, # no locking_mode + { + "mac": "00:11:22:33:44:55", + "locking_mode": "", + "ipv4_allowed": [], + "ipv6_allowed": [], + }, + ), + ], +) +def test_get_locking_mode(input_params, expected_output): + """Test VIF.get_locking_mode() using the VIF class test parameters defined above.""" + + # Act: Get the locking mode configuration for the input params from the VIF object: + with patch("common.send_to_syslog") as send_to_syslog: + test_result = VifMockSubclass(input_params).get_locking_mode() + + # Assert the expected output and the expected call to send_to_syslog(): + assert test_result == expected_output + send_to_syslog.assert_called_once_with( + "Got locking config: " + repr(expected_output) + ) + + +@pytest.mark.parametrize( + "input_params, expected_results", + [ + pytest.param( + {"other_config": [("ethtool-speed", "true"), ("ethtool-duplex", "false")]}, + [("speed", True), ("duplex", False)], + ), + pytest.param( + {"other_config": [("ethtool-rx", "off"), ("ethtool-tso", "on")]}, + [("rx", False), ("tso", True)], + ), + pytest.param( + {"other_config": [("ethtool-rx", "Wrong")]}, + [], + ), + ], +) +def test_get_ethtool(input_params, expected_results): + """Test VIF.get_ethtool() using the VIF class test parameters defined above.""" + + # Arrange + class MockVIF(common.VIF): + """Mock class to simulate a VIF object containing the get_ethtool method""" + + def __init__(self, json_data): # pylint: disable=super-init-not-called + self.json = json_data + self.vm_uuid = "vm_uuid" + self.devid = 1 + + # Act: Get the locking mode configuration for the input params from the VIF object: + with patch("common.send_to_syslog") as send_to_syslog: + test_result = MockVIF(input_params).get_ethtool() + + # Assert the expected output and the expected call to send_to_syslog(): + assert test_result == expected_results + if not expected_results: + send_to_syslog.assert_called_once_with( + "VIF vm_uuid/1: ignoring ethtool argument rx=Wrong (use true/false)" + )