diff options
| author | Doug Szumski <doug@stackhpc.com> | 2026-01-26 11:25:34 +0000 |
|---|---|---|
| committer | Doug Szumski <doug@stackhpc.com> | 2026-04-24 14:47:26 +0100 |
| commit | 0f4c8544d8b822bd0d97da75b47b1244d147bef3 (patch) | |
| tree | 174d9bef4550904d7bad6e89e25424de3daf90e0 /kolla_ansible | |
| parent | f0f9810b67afb7531e89cd683d44e49b9980d00f (diff) | |
| download | kolla-ansible-0f4c8544d8b822bd0d97da75b47b1244d147bef3.tar.gz kolla-ansible-0f4c8544d8b822bd0d97da75b47b1244d147bef3.zip | |
Support multiple Nova Compute Ironic instances
Adds support for deploying multiple instances of the Nova
Compute Ironic service on the same host. This is useful
in large baremetal deployments, where the sharding and/or
conductor group feature is used to scale out the service.
A further patch to support deploying multiple Ironic
conductor instances will follow.
Co-Authored-By: Bartosz Bezak <bartosz@stackhpc.com>
Co-Authored-By: Bertrand Lanson <bertrand.lanson@protonmail.com>
Change-Id: Ibbddfd87e831a3775c3ed66c80b18e5070cedc90
Signed-off-by: Doug Szumski <doug@stackhpc.com>
Diffstat (limited to 'kolla_ansible')
| -rw-r--r-- | kolla_ansible/nova_filters.py | 51 | ||||
| -rw-r--r-- | kolla_ansible/tests/unit/test_nova_filters.py | 86 |
2 files changed, 137 insertions, 0 deletions
diff --git a/kolla_ansible/nova_filters.py b/kolla_ansible/nova_filters.py index 4bb5cbdf6..f9be3eb98 100644 --- a/kolla_ansible/nova_filters.py +++ b/kolla_ansible/nova_filters.py @@ -15,6 +15,8 @@ import jinja2 import re +from kolla_ansible import exception + def extract_cell(list_cells_cli_output, cell_name): """Extract cell settings from nova_manage CLI @@ -85,8 +87,57 @@ def namespace_haproxy_for_cell(services, cell_name): return services +def get_expected_ironic_compute_services(ironic_compute_batch_info, + multi_compute_conf, + nova_cell_compute_ironic_group): + """Return a List of expected Ironic compute services + + :param ironic_compute_batch_info: A dict containing configuration + information about Ironic Compute hosts. + :param multi_compute_conf: Ironic multi-compute config + :param nova_cell_compute_ironic_group: Ansible group name for + nova-compute-ironic. Eg. 'nova-compute-ironic'. + :returns: A list of Nova Compute Ironic service hostnames. + """ + classic_info = ironic_compute_batch_info['classic'] + multi_compute_info = ironic_compute_batch_info['multi'] + if len(multi_compute_conf) == 0: + return [f'{host}-ironic' for host in classic_info] + + expected_services = [] + group_prefix = nova_cell_compute_ironic_group + '-' + for host in multi_compute_info: + iterate_vars = [ + int(group.split(group_prefix)[1]) + for group in host if group_prefix in group] + for i in iterate_vars: + try: + service_conf = multi_compute_conf[i - 1] + except IndexError: + raise exception.FilterError( + "Unable to look up multi-compute Ironic config for " + "{g}-{i} inventory group. Check that " + "nova_multi_compute_ironic_config has an entry for " + "each defined group.".format( + g=nova_cell_compute_ironic_group, + i=i)) + expected_services.append( + _get_expected_compute_service_name(service_conf)) + return expected_services + + +def _get_expected_compute_service_name(service_conf): + custom_host = service_conf.get('custom_host') + if custom_host: + return f"{custom_host}-ironic" + cg = service_conf.get('conductor_group', 'default') + sk = service_conf.get('shard_key', 'default') + return f"{cg}-{sk}-ironic" + + def get_filters(): return { "extract_cell": extract_cell, "namespace_haproxy_for_cell": namespace_haproxy_for_cell, + "get_expected_ironic_compute_services": get_expected_ironic_compute_services, # noqa } diff --git a/kolla_ansible/tests/unit/test_nova_filters.py b/kolla_ansible/tests/unit/test_nova_filters.py index e8d5c507a..d436b0ef1 100644 --- a/kolla_ansible/tests/unit/test_nova_filters.py +++ b/kolla_ansible/tests/unit/test_nova_filters.py @@ -73,6 +73,92 @@ class TestFilters(unittest.TestCase): self.assertRaisesRegex(jinja2.TemplateRuntimeError, 'duplicates', self._test_extract_cell, test_data, 'cell0001') + def test_get_expected_ironic_compute_services_multi_compute(self): + example_ironic_compute_conf = { + # We expect the classic config to be ignored + 'classic': ['custom-host-nova-compute'], + # We have a batch of 2 Ironic compute hosts which should + # run a total of 3 Nova compute Ironic instances + 'multi': [ + ['nova-compute-ironic-1', + 'nova-compute-ironic-2', + 'nova-compute-ironic', + 'something'], + ['nova-compute-ironic-3', + 'nova-compute-ironic', + 'something'], + ] + } + multi_conf = [ + {"custom_host": "some_custom_host"}, + {"shard_key": "shard_1", "conductor_group": "location_1"}, + {"shard_key": "shard_2", "conductor_group": "location_1"}, + {"conductor_group": "location_2"}, + {"shard_key": "shard_1"} + ] + actual = filters.get_expected_ironic_compute_services( + example_ironic_compute_conf, multi_conf, 'nova-compute-ironic') + expected = [ + 'some_custom_host-ironic', + 'location_1-shard_1-ironic', + 'location_1-shard_2-ironic', + ] + self.assertListEqual(actual, expected) + + def test_get_expected_ironic_compute_services_no_compute(self): + example_ironic_compute_conf = { + 'classic': [], + 'multi': [] + } + actual = filters.get_expected_ironic_compute_services( + example_ironic_compute_conf, [], 'nova-compute-ironic') + expected = [] + self.assertListEqual(actual, expected) + + def test_get_expected_ironic_compute_services_classic_compute(self): + example_ironic_compute_conf = { + 'classic': ['custom-foo'], + 'multi': [] + } + actual = filters.get_expected_ironic_compute_services( + example_ironic_compute_conf, [], 'nova-compute-ironic') + expected = ['custom-foo-ironic'] + self.assertListEqual(actual, expected) + + def test_get_expected_ironic_compute_services_classic_compute_multi(self): + # NOTE(dougszu): This isn't a recommended configuration + example_ironic_compute_conf = { + 'classic': ['custom-foo', 'ctrl02', 'ctrl03'], + 'multi': [] + } + actual = filters.get_expected_ironic_compute_services( + example_ironic_compute_conf, [], 'nova-compute-ironic') + expected = [ + 'custom-foo-ironic', + 'ctrl02-ironic', + 'ctrl03-ironic', + ] + self.assertListEqual(actual, expected) + + def test_get_expected_ironic_compute_services_bad_config(self): + example_ironic_compute_conf = { + 'classic': [], + 'multi': [ + ['my-custom-group-1', + 'my-custom-group-2', + 'my-custom-group', + 'something'], + ] + } + multi_conf = [ + {"shard_key": "shard_1", "conductor_group": "location_1"}, + ] + with self.assertRaisesRegex(Exception, + 'Unable to look up multi-compute Ironic ' + 'config for my-custom-group-2'): + filters.get_expected_ironic_compute_services( + example_ironic_compute_conf, multi_conf, 'my-custom-group') + def test_namespace_haproxy_for_cell_with_empty_name(self): example_services = { 'nova-novncproxy': { |
