Files
charm-ironic-conductor/unit_tests/test_lib_charm_openstack_ironic.py
James Page 197ba0092c tests: fix compatibility with revised patching
Directly set the release for each test charm target rather than
using the return value of CompareOpenStackReleases which is no
longer patched by charms.openstack.

Drop supported Ubuntu targets from charmcraft configuration.

Change-Id: If6b65341ec69a2e1fd9b481fea75912bea2c86c4
2024-08-02 19:46:03 +00:00

570 lines
23 KiB
Python

# Copyright 2020 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from copy import deepcopy
from unittest import mock
import charms_openstack.test_mocks
import charms_openstack.test_utils as test_utils
import charms.leadership as leadership
import charmhelpers.core.hookenv as hookenv
import charms.reactive as reactive
from charmhelpers.contrib.openstack.utils import (
os_release,
)
from charm.openstack.ironic import ironic
from charm.openstack.ironic import controller_utils as ctrl_util
class TestIronicCharmConfigProperties(test_utils.PatchHelper):
def setUp(self):
super().setUp()
self.patch_release(ironic.IronicConductorCharm.release)
def test_deployment_interface_ip(self):
cls = mock.MagicMock()
self.patch_object(ironic, 'ch_ip')
ironic.deployment_interface_ip(cls)
self.ch_ip.get_relation_ip.assert_called_with('deployment')
def test_internal_interface_ip(self):
cls = mock.MagicMock()
self.patch_object(ironic, 'ch_ip')
ironic.internal_interface_ip(cls)
self.ch_ip.get_relation_ip.assert_called_with('internal')
def test_temp_url_secret(self):
cls = mock.MagicMock()
leadership.leader_get.return_value = "fake"
self.assertEqual(ironic.temp_url_secret(cls), "fake")
leadership.leader_get.assert_called_with("temp_url_secret")
class TestIronicCharm(test_utils.PatchHelper):
def setUp(self):
super().setUp()
hookenv.config.return_value = {}
self.patch_release(ironic.IronicConductorCharm.release)
self.patch_object(ironic.controller_utils, 'get_pxe_config_class')
self.mocked_pxe_cfg = mock.MagicMock()
self.mocked_pxe_cfg.TFTP_ROOT = ctrl_util.PXEBootBase.TFTP_ROOT
self.mocked_pxe_cfg.HTTP_ROOT = ctrl_util.PXEBootBase.HTTP_ROOT
self.mocked_pxe_cfg.IRONIC_USER = ctrl_util.PXEBootBase.IRONIC_USER
self.mocked_pxe_cfg.IRONIC_GROUP = ctrl_util.PXEBootBase.IRONIC_GROUP
self.mocked_pxe_cfg.determine_packages.return_value = [
"fakepkg1", "fakepkg2"]
self.mocked_pxe_cfg.get_restart_map.return_value = {
"fake_config": [
"fake_svc", ]}
self.mocked_pxe_cfg.HTTPD_SERVICE_NAME = "fakehttpd"
self.get_pxe_config_class.return_value = self.mocked_pxe_cfg
def test_setup_power_adapter_config_train(self):
os_release.return_value = "train"
cfg_data = {
"enabled-hw-types": "ipmi, redfish, idrac",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._setup_power_adapter_config()
expected = {
"enabled_hardware_types": "idrac, intel-ipmi, ipmi, redfish",
"enabled_management_interfaces": ("idrac-redfish, intel-ipmitool, "
"ipmitool, redfish, noop"),
"enabled_inspect_interfaces": "idrac-redfish, redfish, no-inspect",
"enabled_power_interfaces": "idrac-redfish, ipmitool, redfish",
"enabled_console_interfaces": ("ipmitool-shellinabox, "
"ipmitool-socat, no-console"),
"enabled_raid_interfaces": "idrac-wsman, no-raid",
"enabled_vendor_interfaces": "idrac-wsman, ipmitool, no-vendor",
"enabled_boot_interfaces": "pxe",
"enabled_bios_interfaces": "no-bios"
}
self.assertEqual(
target.config["hardware_type_cfg"],
expected)
def test_setup_power_adapter_config_train_ipxe(self):
os_release.return_value = "train"
cfg_data = {
"enabled-hw-types": "ipmi, redfish, idrac",
"use-ipxe": True,
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._setup_power_adapter_config()
expected = {
"enabled_hardware_types": "idrac, intel-ipmi, ipmi, redfish",
"enabled_management_interfaces": ("idrac-redfish, intel-ipmitool, "
"ipmitool, redfish, noop"),
"enabled_inspect_interfaces": "idrac-redfish, redfish, no-inspect",
"enabled_power_interfaces": "idrac-redfish, ipmitool, redfish",
"enabled_console_interfaces": ("ipmitool-shellinabox, "
"ipmitool-socat, no-console"),
"enabled_raid_interfaces": "idrac-wsman, no-raid",
"enabled_vendor_interfaces": "idrac-wsman, ipmitool, no-vendor",
"enabled_boot_interfaces": "pxe, ipxe",
"enabled_bios_interfaces": "no-bios"
}
self.assertEqual(
target.config["hardware_type_cfg"],
expected)
def test_setup_power_adapter_config_unknown(self):
# test that it defaults to latest, in this case ussuri
os_release.return_value = "unknown"
cfg_data = {
"enabled-hw-types": "ipmi, redfish, idrac",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._setup_power_adapter_config()
expected = {
"enabled_hardware_types": "idrac, intel-ipmi, ipmi, redfish",
"enabled_management_interfaces": ("idrac-redfish, intel-ipmitool, "
"ipmitool, redfish, noop"),
"enabled_inspect_interfaces": "idrac-redfish, redfish, no-inspect",
"enabled_power_interfaces": "idrac-redfish, ipmitool, redfish",
"enabled_console_interfaces": ("ipmitool-shellinabox, "
"ipmitool-socat, no-console"),
"enabled_raid_interfaces": "idrac-wsman, no-raid",
"enabled_vendor_interfaces": "idrac-wsman, ipmitool, no-vendor",
"enabled_boot_interfaces": "pxe, redfish-virtual-media",
"enabled_bios_interfaces": "idrac-wsman, no-bios"
}
self.assertEqual(
target.config["hardware_type_cfg"],
expected)
def test_setup_power_adapter_config_ussuri(self):
os_release.return_value = "ussuri"
cfg_data = {
"enabled-hw-types": "ipmi, redfish, idrac",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._setup_power_adapter_config()
self.maxDiff = None
expected = {
"enabled_hardware_types": "idrac, intel-ipmi, ipmi, redfish",
"enabled_management_interfaces": ("idrac-redfish, intel-ipmitool, "
"ipmitool, redfish, noop"),
"enabled_inspect_interfaces": "idrac-redfish, redfish, no-inspect",
"enabled_power_interfaces": "idrac-redfish, ipmitool, redfish",
"enabled_console_interfaces": ("ipmitool-shellinabox, "
"ipmitool-socat, no-console"),
"enabled_raid_interfaces": "idrac-wsman, no-raid",
"enabled_vendor_interfaces": "idrac-wsman, ipmitool, no-vendor",
"enabled_boot_interfaces": "pxe, redfish-virtual-media",
"enabled_bios_interfaces": "idrac-wsman, no-bios"
}
self.assertEqual(
target.config["hardware_type_cfg"],
expected)
def test_get_amqp_credentials(self):
os_release.return_value = "yoga"
cfg_data = {
"rabbit-user": "ironic",
"rabbit-vhost": "openstack",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
self.get_pxe_config_class.assert_called_with(cfg_data, 'yoga')
result = target.get_amqp_credentials()
self.assertEqual(result, ('ironic', 'openstack'))
def test_get_database_setup(self):
os_release.return_value = "yoga"
cfg_data = {
"database-user": "ironic",
"database": "ironicdb",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
self.get_pxe_config_class.assert_called_with(cfg_data, 'yoga')
result = target.get_database_setup()
self.assertEqual(
result,
[{
"database": cfg_data["database"],
"username": cfg_data["database-user"]}])
def test_enabled_network_interfaces(self):
os_release.return_value = "yoga"
cfg_data = {
"enabled-network-interfaces": "fake, fake2"}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
self.get_pxe_config_class.assert_called_with(cfg_data, 'yoga')
self.assertEqual(
target.enabled_network_interfaces,
["fake", "fake2"])
def test_enabled_deploy_interfaces(self):
os_release.return_value = "yoga"
cfg_data = {
"enabled-deploy-interfaces": "fake, fake2"}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
self.get_pxe_config_class.assert_called_with(cfg_data, 'yoga')
self.assertEqual(
target.enabled_deploy_interfaces,
["fake", "fake2"])
def test_configure_defaults_no_cfg(self):
cfg_data = {
"default-network-interface": "",
"default-deploy-interface": ""}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
self.assertEqual(
target.config.get("default-network-interface"),
ironic.DEFAULT_NET_IFACE)
self.assertEqual(
target.config.get("default-deploy-interface"),
ironic.DEFAULT_DEPLOY_IFACE)
def test_configure_defaults_with_user_defined_val(self):
cfg_data = {
"default-network-interface": "fake_net",
"default-deploy-interface": "fake_deploy"}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._configure_defaults()
self.assertEqual(
target.config.get("default-network-interface"),
"fake_net")
self.assertEqual(
target.config.get("default-deploy-interface"),
"fake_deploy")
def test_configure_logging_with_local_path(self):
cfg_data = {
"deploy-logs-collect": "always",
"deploy-logs-storage-backend": "local",
"deploy-logs-local-path": "/var/log/ironic/nodeploy"}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._configure_defaults()
self.assertEqual(
target.config.get("deploy-logs-collect"),
"always")
self.assertEqual(
target.config.get("deploy-logs-storage-backend"),
"local")
self.assertEqual(
target.config.get("deploy-logs-local-path"),
"/var/log/ironic/nodeploy")
def test_configure_logging_with_swift_container(self):
cfg_data = {
"deploy-logs-collect": "always",
"deploy-logs-storage-backend": "swift",
"deploy-logs-swift-container": "no_container",
"deploy-logs-swift-days-to-expire": 15}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target._configure_defaults()
self.assertEqual(
target.config.get("deploy-logs-collect"),
"always")
self.assertEqual(
target.config.get("deploy-logs-storage-backend"),
"swift")
self.assertEqual(
target.config.get("deploy-logs-swift-container"),
"no_container"),
self.assertEqual(
target.config.get("deploy-logs-swift-days-to-expire"),
15),
def test_setup_pxe_config(self):
hookenv.config.return_value = {
"default-network-interface": "fake_net",
"default-deploy-interface": "fake_deploy"}
target = ironic.IronicConductorCharm()
target._setup_pxe_config(self.mocked_pxe_cfg)
expected_pkgs = deepcopy(ironic.PACKAGES) + ["fakepkg1", "fakepkg2"]
expected_cfg = {
'tftpboot': ctrl_util.PXEBootBase.TFTP_ROOT,
'httpboot': ctrl_util.PXEBootBase.HTTP_ROOT,
'ironic_user': ctrl_util.PXEBootBase.IRONIC_USER,
'ironic_group': ctrl_util.PXEBootBase.IRONIC_GROUP,
'hardware_type_cfg': {
'enabled_hardware_types': 'intel-ipmi, ipmi',
'enabled_management_interfaces': ('intel-ipmitool, ipmitool,'
' noop'),
'enabled_inspect_interfaces': 'no-inspect',
'enabled_power_interfaces': 'ipmitool',
'enabled_console_interfaces': ('ipmitool-shellinabox, '
'ipmitool-socat, no-console'),
'enabled_raid_interfaces': 'no-raid',
'enabled_vendor_interfaces': 'ipmitool, no-vendor',
'enabled_boot_interfaces': 'pxe',
'enabled_bios_interfaces': 'no-bios'},
'default-network-interface': 'fake_net',
'default-deploy-interface': 'fake_deploy'}
self.assertEqual(
sorted(target.packages),
sorted(expected_pkgs))
self.assertEqual(target.config, expected_cfg)
self.assertEqual(
target.restart_map.get("fake_config", []), ["fake_svc"])
self.assertTrue("fakehttpd" in target.services)
def test_packages_xena(self):
reactive.is_flag_set.side_effect = [False, False, False]
target = ironic.IronicConductorXenaCharm()
expected_pkgs = deepcopy(ironic.PACKAGES) + ["fakepkg1", "fakepkg2"]
expected_pkgs.remove("open-iscsi")
self.assertEqual(
sorted(target.packages),
sorted(expected_pkgs))
def test_validate_network_interfaces(self):
target = ironic.IronicConductorCharm()
with self.assertRaises(ValueError):
target._validate_network_interfaces(["bogus"])
self.assertIsNone(
target._validate_network_interfaces(["neutron"]))
def test_validate_deploy_interfaces(self):
target = ironic.IronicConductorCharm()
target.release = 'wallaby'
with self.assertRaises(ValueError) as err:
target._validate_deploy_interfaces(["bogus"])
expected_msg = (
'Deploy interface bogus is not valid.'
' Valid interfaces are: direct, iscsi')
self.assertIsNone(
target._validate_deploy_interfaces(["direct"]))
self.assertEqual(str(err.exception), expected_msg)
def test_validate_deploy_interfaces_xena(self):
reactive.is_flag_set.side_effect = [False, False, False]
target = ironic.IronicConductorXenaCharm()
target.release = 'xena'
with self.assertRaises(ValueError) as err:
target._validate_deploy_interfaces(["bogus"])
expected_msg = (
'Deploy interface bogus is not valid.'
' Valid interfaces are: direct')
self.assertIsNone(
target._validate_deploy_interfaces(["direct"]))
self.assertEqual(str(err.exception), expected_msg)
def test_validate_deploy_interfaces_tmp_secret(self):
# leadership.set.temp_url_secret is not set, and "direct"
# boot method is enabled. Validate will fail, until
# set-temp-url-secret action is run
reactive.is_flag_set.side_effect = [False, True]
target = ironic.IronicConductorCharm()
target.release = 'wallaby'
with self.assertRaises(ValueError) as err:
target._validate_deploy_interfaces(["direct"])
expected_msg = (
'run set-temp-url-secret action on '
'leader to enable direct deploy method')
self.assertEqual(str(err.exception), expected_msg)
def test_validate_default_net_interface(self):
hookenv.config.return_value = {
"default-network-interface": "flat",
"enabled-network-interfaces": "neutron, flat, noop"}
target = ironic.IronicConductorCharm()
self.assertIsNone(target._validate_default_net_interface())
def test_validate_default_net_interface_invalid_default(self):
hookenv.config.return_value = {
"default-network-interface": "bogus",
"enabled-network-interfaces": "neutron, flat, noop"}
target = ironic.IronicConductorCharm()
with self.assertRaises(ValueError) as err:
target._validate_default_net_interface()
expected_msg = (
"default-network-interface (bogus) is not enabled "
"in enabled-network-interfaces: neutron, flat, noop")
self.assertEqual(str(err.exception), expected_msg)
def test_validate_default_deploy_interface(self):
hookenv.config.return_value = {
"default-deploy-interface": "direct",
"enabled-deploy-interfaces": "direct, iscsi"}
target = ironic.IronicConductorCharm()
self.assertIsNone(target._validate_default_deploy_interface())
def test_validate_default_deploy_interface_invalid_default(self):
hookenv.config.return_value = {
"default-deploy-interface": "bogus",
"enabled-deploy-interfaces": "direct, iscsi"}
target = ironic.IronicConductorCharm()
with self.assertRaises(ValueError) as err:
target._validate_default_deploy_interface()
expected_msg = (
"default-deploy-interface (bogus) is not enabled "
"in enabled-deploy-interfaces: direct, iscsi")
self.assertEqual(str(err.exception), expected_msg)
def test_custom_assess_status_check_all_good(self):
hookenv.config.return_value = {
"default-deploy-interface": "direct",
"enabled-deploy-interfaces": "direct, iscsi",
"default-network-interface": "flat",
"enabled-network-interfaces": "neutron, flat, noop"}
target = ironic.IronicConductorCharm()
target.release = 'wallaby'
self.assertEqual(target.custom_assess_status_check(), (None, None))
def test_custom_assess_status_check_invalid_enabled_net_ifaces(self):
hookenv.config.return_value = {
"default-deploy-interface": "direct",
"enabled-deploy-interfaces": "direct, iscsi",
"default-network-interface": "flat",
"enabled-network-interfaces": "bogus, noop"}
target = ironic.IronicConductorCharm()
expected_status = (
'blocked',
'invalid enabled-network-interfaces config, Network interface '
'bogus is not valid. Valid interfaces are: neutron, flat, noop'
)
self.assertEqual(target.custom_assess_status_check(), expected_status)
def test_custom_assess_status_check_invalid_enabled_deploy_ifaces(self):
hookenv.config.return_value = {
"default-deploy-interface": "direct",
"enabled-deploy-interfaces": "bogus, iscsi",
"default-network-interface": "flat",
"enabled-network-interfaces": "neutron, flat, noop"}
target = ironic.IronicConductorCharm()
target.release = 'wallaby'
expected_status = (
'blocked',
'invalid enabled-deploy-interfaces config, Deploy interface '
'bogus is not valid. Valid interfaces are: direct, iscsi'
)
self.assertEqual(target.custom_assess_status_check(), expected_status)
def test_custom_assess_status_check_invalid_default_net_iface(self):
hookenv.config.return_value = {
"default-deploy-interface": "direct",
"enabled-deploy-interfaces": "direct, iscsi",
"default-network-interface": "bogus",
"enabled-network-interfaces": "neutron, flat, noop"}
target = ironic.IronicConductorCharm()
expected_status = (
'blocked',
'invalid default-network-interface config, '
'default-network-interface (bogus) is not enabled '
'in enabled-network-interfaces: neutron, flat, noop'
)
self.assertEqual(target.custom_assess_status_check(), expected_status)
def test_custom_assess_status_check_invalid_default_deploy_iface(self):
hookenv.config.return_value = {
"default-deploy-interface": "bogus",
"enabled-deploy-interfaces": "direct, iscsi",
"default-network-interface": "flat",
"enabled-network-interfaces": "neutron, flat, noop"}
target = ironic.IronicConductorCharm()
target.release = 'wallaby'
expected_status = (
'blocked',
'invalid default-deploy-interface config, default-deploy-interface'
' (bogus) is not enabled in enabled-deploy-interfaces: direct, '
'iscsi')
self.assertEqual(target.custom_assess_status_check(), expected_status)
@mock.patch('charms_openstack.charm.OpenStackCharm.upgrade_charm')
def test_upgrade_charm(self, upgrade_charm):
os_release.return_value = "ussuri"
cfg_data = {
"openstack-origin": "distro",
}
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target.upgrade_charm()
# check the parent's upgrade_charm was called
upgrade_charm.assert_called()
os_module = charms_openstack.test_mocks.charmhelpers.contrib.openstack
templating = os_module.templating
templating.OSConfigRenderer.assert_called_with(
templates_dir='templates/', openstack_release='ussuri')
configs = templating.OSConfigRenderer()
configs.register.assert_called_with(config_file=ironic.IRONIC_DEFAULT,
contexts=[])
configs.write_all.assert_called_with()
@mock.patch('charms_openstack.charm.OpenStackCharm.install')
def test_install(self, install):
os_release.return_value = "ussuri"
cfg_data = {
"openstack-origin": "distro",
}
charmhelpers = charms_openstack.test_mocks.charmhelpers
os_utils = charmhelpers.contrib.openstack.utils
os_utils.get_source_and_pgp_key.return_value = (None, None)
hookenv.config.return_value = cfg_data
target = ironic.IronicConductorCharm()
target.install()
install.assert_called_with()
os_module = charms_openstack.test_mocks.charmhelpers.contrib.openstack
templating = os_module.templating
templating.OSConfigRenderer.assert_called_with(
templates_dir='templates/', openstack_release='ussuri')
configs = templating.OSConfigRenderer(templates_dir='templates/',
openstack_release='ussuri')
configs.register.assert_called_with(config_file=ironic.IRONIC_DEFAULT,
contexts=[])
configs.write_all.assert_called_with()