Files
charm-ironic-conductor/unit_tests/test_lib_charm_openstack_ironic.py
Liam Young a3b68bd8df Stop managing grub.cfg in Yoga
From yoga onwards the ironic-conductor manges the grub.conf. This
change ensure that the charm stops managing it. This change also
ensures that the file permissions are set correctly prior to
the upgrade to Yoga.

While developing this change it became apparent that the charm
was missing the code to trigger an upgrade after a change in
openstack-origin so this change corrects that.

Closes-Bug: #1968871
Change-Id: Ie7ac1b0203d3a51cf3ff74ed8797be3f169cfd1c
2022-04-14 16:47:26 +00:00

481 lines
20 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.
import mock
from copy import deepcopy
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 (
CompareOpenStackReleases,
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_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()
CompareOpenStackReleases.return_value = '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()
CompareOpenStackReleases.return_value = '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]
CompareOpenStackReleases.return_value = 'wallaby'
target = ironic.IronicConductorCharm()
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"}
CompareOpenStackReleases.return_value = 'wallaby'
target = ironic.IronicConductorCharm()
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"}
CompareOpenStackReleases.return_value = 'wallaby'
target = ironic.IronicConductorCharm()
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"}
CompareOpenStackReleases.return_value = 'wallaby'
target = ironic.IronicConductorCharm()
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)