diff --git a/src/config.yaml b/src/config.yaml index 05ca7fa..80f7ac4 100644 --- a/src/config.yaml +++ b/src/config.yaml @@ -220,3 +220,9 @@ options: for. Defaults to 20 minutes. If some deploys get a 401 response code when trying to download from the temporary URL, try raising this duration. + hardware-enablement-options: + default: + type: string + description: | + Options passed to the managed service to set configuration keys that + enable the use of specific hardware. diff --git a/src/lib/charm/openstack/ironic/ironic.py b/src/lib/charm/openstack/ironic/ironic.py index ffe4bd1..7cffae6 100644 --- a/src/lib/charm/openstack/ironic/ironic.py +++ b/src/lib/charm/openstack/ironic/ironic.py @@ -15,10 +15,13 @@ from charmhelpers.contrib.openstack.utils import ( CompareOpenStackReleases, os_release, ) +from charmhelpers.contrib.openstack import templating +from charmhelpers.core import host import charm.openstack.ironic.controller_utils as controller_utils import charms_openstack.adapters as adapters import charmhelpers.contrib.network.ip as ch_ip +import charmhelpers.contrib.openstack.utils as ch_utils import charms.leadership as leadership import charms.reactive as reactive @@ -41,6 +44,10 @@ PACKAGES = [ IRONIC_DIR = "/etc/ironic/" IRONIC_CONF = os.path.join(IRONIC_DIR, "ironic.conf") +IRONIC_CONF_D = os.path.join(IRONIC_DIR, "conf.d") +IRONIC_CONF_HW_ENABLEMENT = os.path.join(IRONIC_CONF_D, + "90-hardware-enablement.conf") +IRONIC_DEFAULT = "/etc/default/ironic-conductor" ROOTWRAP_CONF = os.path.join(IRONIC_DIR, "rootwrap.conf") FILTERS_DIR = os.path.join(IRONIC_DIR, "rootwrap.d") IRONIC_LIB_FILTERS = os.path.join( @@ -198,6 +205,8 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm): restart_map = { IRONIC_CONF: ['ironic-conductor', ], + IRONIC_DEFAULT: ['ironic-conductor', ], + IRONIC_CONF_HW_ENABLEMENT: ['ironic-conductor', ], IRONIC_UTILS_FILTERS: ['ironic-conductor', ], IRONIC_LIB_FILTERS: ['ironic-conductor', ], ROOTWRAP_CONF: ['ironic-conductor', ], @@ -317,6 +326,7 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm): def install(self): self.configure_source() super().install() + self._reconfigure_ironic_conductor() self.pxe_config._copy_resources() self.assess_status() @@ -443,6 +453,32 @@ class IronicConductorCharm(charms_openstack.charm.OpenStackCharm): return (None, None) + def upgrade_charm(self): + """Custom upgrade charm. + + Side effects: + - Create /etc/ironic/conf.d/ directory. + - Reconfigure ironic-conductor service to use the previously created + directory. + """ + self._reconfigure_ironic_conductor() + super().upgrade_charm() + + def _reconfigure_ironic_conductor(self): + """Reconfigure ironic-conductor daemon. + + Set /etc/default/ironic-conductor to pass --config-dir in DAEMON_ARGS. + """ + if not os.path.isdir(IRONIC_CONF_D): + host.mkdir(IRONIC_CONF_D) + + # reconfigure ironic-conductor to run it with --conf-dir + release = ch_utils.os_release('ironic-common') + configs = templating.OSConfigRenderer(templates_dir='templates/', + openstack_release=release) + configs.register(config_file=IRONIC_DEFAULT, contexts=[]) + configs.write_all() + class IronicConductorXenaCharm(IronicConductorCharm): diff --git a/src/templates/90-hardware-enablement.conf b/src/templates/90-hardware-enablement.conf new file mode 100644 index 0000000..e7ce3e5 --- /dev/null +++ b/src/templates/90-hardware-enablement.conf @@ -0,0 +1,7 @@ +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +############################################################################### +{%- if options.hardware_enablement_options %} +{{ options.hardware_enablement_options }} +{%- endif %} \ No newline at end of file diff --git a/src/templates/etc_default_ironic-conductor b/src/templates/etc_default_ironic-conductor new file mode 100644 index 0000000..fcf8b60 --- /dev/null +++ b/src/templates/etc_default_ironic-conductor @@ -0,0 +1,5 @@ +############################################################################### +# [ WARNING ] +# Configuration file maintained by Juju. Local changes may be overwritten. +############################################################################### +DAEMON_ARGS="--config-dir=/etc/ironic/conf.d/" diff --git a/src/tests/bundles/jammy-yoga.yaml b/src/tests/bundles/jammy-yoga.yaml index 84825ba..0ceaf51 100644 --- a/src/tests/bundles/jammy-yoga.yaml +++ b/src/tests/bundles/jammy-yoga.yaml @@ -290,6 +290,9 @@ services: disable-secure-erase: true use-ipxe: true enabled-network-interfaces: "flat, noop" + hardware-enablement-options: | + [ipmi] + debug = true neutron-ironic-agent: charm: ch:neutron-api-plugin-ironic num_units: 0 diff --git a/src/tests/bundles/jammy-zed.yaml b/src/tests/bundles/jammy-zed.yaml index 40f0707..2824875 100644 --- a/src/tests/bundles/jammy-zed.yaml +++ b/src/tests/bundles/jammy-zed.yaml @@ -290,6 +290,9 @@ services: disable-secure-erase: true use-ipxe: true enabled-network-interfaces: "flat, noop" + hardware-enablement-options: | + [ipmi] + debug = true neutron-ironic-agent: charm: ch:neutron-api-plugin-ironic num_units: 0 diff --git a/src/tests/bundles/kinetic-zed.yaml b/src/tests/bundles/kinetic-zed.yaml index 27adb7a..9de3fcc 100644 --- a/src/tests/bundles/kinetic-zed.yaml +++ b/src/tests/bundles/kinetic-zed.yaml @@ -290,6 +290,9 @@ services: disable-secure-erase: true use-ipxe: true enabled-network-interfaces: "flat, noop" + hardware-enablement-options: | + [ipmi] + debug = true neutron-ironic-agent: charm: ch:neutron-api-plugin-ironic num_units: 0 diff --git a/unit_tests/test_lib_charm_openstack_ironic.py b/unit_tests/test_lib_charm_openstack_ironic.py index 492a153..50d7fae 100644 --- a/unit_tests/test_lib_charm_openstack_ironic.py +++ b/unit_tests/test_lib_charm_openstack_ironic.py @@ -12,9 +12,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -import mock 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 @@ -522,3 +523,48 @@ class TestIronicCharm(test_utils.PatchHelper): ' (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()