summaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorJames Falcon <james.falcon@canonical.com>2021-10-27 09:43:34 -0500
committerGitHub <noreply@github.com>2021-10-27 08:43:34 -0600
commit1d01da5d9916d97ef463ba61a36b3f98f8911419 (patch)
tree1022c172c252082a02f89d0e54a42221dabc1851 /tests
parent75b26b0afbb14cf613fe3b08c8bed050dec0b874 (diff)
downloadvyos-cloud-init-1d01da5d9916d97ef463ba61a36b3f98f8911419.tar.gz
vyos-cloud-init-1d01da5d9916d97ef463ba61a36b3f98f8911419.zip
Add "install hotplug" module (SC-476) (#1069)
This commit removes automatically installing udev rules for hotplug and adds a module to install them instead. Automatically including the udev rules and checking if hotplug was enabled consumed too many resources in certain circumstances. Moving the rules to a module ensures we don't spend extra extra cycles on hotplug if hotplug functionality isn't desired. LP: #1946003
Diffstat (limited to 'tests')
-rw-r--r--tests/integration_tests/modules/test_hotplug.py13
-rw-r--r--tests/unittests/cmd/devel/test_hotplug_hook.py24
-rw-r--r--tests/unittests/test_handler/test_handler_install_hotplug.py104
-rw-r--r--tests/unittests/test_handler/test_schema.py3
4 files changed, 132 insertions, 12 deletions
diff --git a/tests/integration_tests/modules/test_hotplug.py b/tests/integration_tests/modules/test_hotplug.py
index 88cd8c16..f5abc86f 100644
--- a/tests/integration_tests/modules/test_hotplug.py
+++ b/tests/integration_tests/modules/test_hotplug.py
@@ -49,10 +49,13 @@ def test_hotplug_add_remove(client: IntegrationInstance):
ips_before = _get_ip_addr(client)
log = client.read_from_file('/var/log/cloud-init.log')
assert 'Exiting hotplug handler' not in log
+ assert client.execute(
+ 'test -f /etc/udev/rules.d/10-cloud-init-hook-hotplug.rules'
+ ).ok
# Add new NIC
added_ip = client.instance.add_network_interface()
- _wait_till_hotplug_complete(client, expected_runs=2)
+ _wait_till_hotplug_complete(client, expected_runs=1)
ips_after_add = _get_ip_addr(client)
new_addition = [ip for ip in ips_after_add if ip.ip4 == added_ip][0]
@@ -67,7 +70,7 @@ def test_hotplug_add_remove(client: IntegrationInstance):
# Remove new NIC
client.instance.remove_network_interface(added_ip)
- _wait_till_hotplug_complete(client, expected_runs=4)
+ _wait_till_hotplug_complete(client, expected_runs=2)
ips_after_remove = _get_ip_addr(client)
assert len(ips_after_remove) == len(ips_before)
assert added_ip not in [ip.ip4 for ip in ips_after_remove]
@@ -86,12 +89,14 @@ def test_no_hotplug_in_userdata(client: IntegrationInstance):
ips_before = _get_ip_addr(client)
log = client.read_from_file('/var/log/cloud-init.log')
assert 'Exiting hotplug handler' not in log
+ assert client.execute(
+ 'test -f /etc/udev/rules.d/10-cloud-init-hook-hotplug.rules'
+ ).failed
# Add new NIC
client.instance.add_network_interface()
- _wait_till_hotplug_complete(client)
log = client.read_from_file('/var/log/cloud-init.log')
- assert "Event Denied: scopes=['network'] EventType=hotplug" in log
+ assert 'hotplug-hook' not in log
ips_after_add = _get_ip_addr(client)
if len(ips_after_add) == len(ips_before) + 1:
diff --git a/tests/unittests/cmd/devel/test_hotplug_hook.py b/tests/unittests/cmd/devel/test_hotplug_hook.py
index 63d2490e..e1c64e2f 100644
--- a/tests/unittests/cmd/devel/test_hotplug_hook.py
+++ b/tests/unittests/cmd/devel/test_hotplug_hook.py
@@ -30,6 +30,11 @@ def mocks():
return_value=FAKE_MAC
)
+ update_event_enabled = mock.patch(
+ 'cloudinit.stages.update_event_enabled',
+ return_value=True,
+ )
+
m_network_state = mock.MagicMock(spec=NetworkState)
parse_net = mock.patch(
'cloudinit.cmd.devel.hotplug_hook.parse_net_config_data',
@@ -45,6 +50,7 @@ def mocks():
sleep = mock.patch('time.sleep')
read_sys_net.start()
+ update_event_enabled.start()
parse_net.start()
select_activator.start()
m_sleep = sleep.start()
@@ -57,6 +63,7 @@ def mocks():
)
read_sys_net.stop()
+ update_event_enabled.stop()
parse_net.stop()
select_activator.stop()
sleep.stop()
@@ -122,13 +129,16 @@ class TestHotplug:
def test_update_event_disabled(self, mocks, caplog):
init = mocks.m_init
- init.update_event_enabled.return_value = False
- handle_hotplug(
- hotplug_init=init,
- devpath='/dev/fake',
- udevaction='remove',
- subsystem='net'
- )
+ with mock.patch(
+ 'cloudinit.stages.update_event_enabled',
+ return_value=False
+ ):
+ handle_hotplug(
+ hotplug_init=init,
+ devpath='/dev/fake',
+ udevaction='remove',
+ subsystem='net'
+ )
assert 'hotplug not enabled for event of type' in caplog.text
init.datasource.update_metadata_if_supported.assert_not_called()
mocks.m_activator.bring_up_interface.assert_not_called()
diff --git a/tests/unittests/test_handler/test_handler_install_hotplug.py b/tests/unittests/test_handler/test_handler_install_hotplug.py
new file mode 100644
index 00000000..19b0cc41
--- /dev/null
+++ b/tests/unittests/test_handler/test_handler_install_hotplug.py
@@ -0,0 +1,104 @@
+# This file is part of cloud-init. See LICENSE file for license information.
+from collections import namedtuple
+from unittest import mock
+
+import pytest
+
+from cloudinit.config.cc_install_hotplug import (
+ handle,
+ HOTPLUG_UDEV_PATH,
+ HOTPLUG_UDEV_RULES,
+)
+from cloudinit.event import EventScope, EventType
+
+
+@pytest.yield_fixture()
+def mocks():
+ m_update_enabled = mock.patch('cloudinit.stages.update_event_enabled')
+ m_write = mock.patch('cloudinit.util.write_file', autospec=True)
+ m_del = mock.patch('cloudinit.util.del_file', autospec=True)
+ m_subp = mock.patch('cloudinit.subp.subp')
+ m_which = mock.patch('cloudinit.subp.which', return_value=None)
+ m_path_exists = mock.patch('os.path.exists', return_value=False)
+
+ yield namedtuple(
+ 'Mocks',
+ 'm_update_enabled m_write m_del m_subp m_which m_path_exists'
+ )(
+ m_update_enabled.start(), m_write.start(), m_del.start(),
+ m_subp.start(), m_which.start(), m_path_exists.start()
+ )
+
+ m_update_enabled.stop()
+ m_write.stop()
+ m_del.stop()
+ m_subp.stop()
+ m_which.stop()
+ m_path_exists.stop()
+
+
+class TestInstallHotplug:
+ def test_rules_installed_when_supported_and_enabled(self, mocks):
+ mocks.m_which.return_value = 'udevadm'
+ mocks.m_update_enabled.return_value = True
+ m_cloud = mock.MagicMock()
+ m_cloud.datasource.get_supported_events.return_value = {
+ EventScope.NETWORK: {EventType.HOTPLUG}
+ }
+
+ handle(None, {}, m_cloud, mock.Mock(), None)
+ mocks.m_write.assert_called_once_with(
+ filename=HOTPLUG_UDEV_PATH,
+ content=HOTPLUG_UDEV_RULES,
+ )
+ assert mocks.m_subp.call_args_list == [mock.call([
+ 'udevadm', 'control', '--reload-rules',
+ ])]
+ assert mocks.m_del.call_args_list == []
+
+ def test_rules_not_installed_when_unsupported(self, mocks):
+ mocks.m_update_enabled.return_value = True
+ m_cloud = mock.MagicMock()
+ m_cloud.datasource.get_supported_events.return_value = {}
+
+ handle(None, {}, m_cloud, mock.Mock(), None)
+ assert mocks.m_write.call_args_list == []
+ assert mocks.m_del.call_args_list == []
+ assert mocks.m_subp.call_args_list == []
+
+ def test_rules_not_installed_when_disabled(self, mocks):
+ mocks.m_update_enabled.return_value = False
+ m_cloud = mock.MagicMock()
+ m_cloud.datasource.get_supported_events.return_value = {
+ EventScope.NETWORK: {EventType.HOTPLUG}
+ }
+
+ handle(None, {}, m_cloud, mock.Mock(), None)
+ assert mocks.m_write.call_args_list == []
+ assert mocks.m_del.call_args_list == []
+ assert mocks.m_subp.call_args_list == []
+
+ def test_rules_uninstalled_when_disabled(self, mocks):
+ mocks.m_path_exists.return_value = True
+ mocks.m_update_enabled.return_value = False
+ m_cloud = mock.MagicMock()
+ m_cloud.datasource.get_supported_events.return_value = {}
+
+ handle(None, {}, m_cloud, mock.Mock(), None)
+ mocks.m_del.assert_called_with(HOTPLUG_UDEV_PATH)
+ assert mocks.m_subp.call_args_list == [mock.call([
+ 'udevadm', 'control', '--reload-rules',
+ ])]
+ assert mocks.m_write.call_args_list == []
+
+ def test_rules_not_installed_when_no_udevadm(self, mocks):
+ mocks.m_update_enabled.return_value = True
+ m_cloud = mock.MagicMock()
+ m_cloud.datasource.get_supported_events.return_value = {
+ EventScope.NETWORK: {EventType.HOTPLUG}
+ }
+
+ handle(None, {}, m_cloud, mock.Mock(), None)
+ assert mocks.m_del.call_args_list == []
+ assert mocks.m_write.call_args_list == []
+ assert mocks.m_subp.call_args_list == []
diff --git a/tests/unittests/test_handler/test_schema.py b/tests/unittests/test_handler/test_schema.py
index 59f58f7c..1dae223d 100644
--- a/tests/unittests/test_handler/test_schema.py
+++ b/tests/unittests/test_handler/test_schema.py
@@ -36,7 +36,8 @@ class GetSchemaTest(CiTestCase):
'cc_write_files',
'cc_write_files_deferred',
'cc_zypper_add_repo',
- 'cc_chef'
+ 'cc_chef',
+ 'cc_install_hotplug',
],
[subschema['id'] for subschema in schema['allOf']])
self.assertEqual('cloud-config-schema', schema['id'])