Refactor host configure commands to use a single playbook

Ansible failure handling is different when executing multiple top-level
playbooks (CLI arguments) vs. multiple plays within a top-level
playbook. If any hosts have failed or are unreachable at the end of a
top-level playbook, then ansible-playbook exits non-zero.

In contrast, execution will continue at the end of a mid-playbook play
if there are hosts that have not failed or become unreachable. This is
documented in [1].

Currently, Kayobe executes multiple top-level playbooks, most notably in
the host configure commands where there is a long list of them. This has
implications when working at scale, where failures are more common. If a
host fails at any point, then execution of the command will stop at the
end of the current playbook. This means that the command must be run
again for all hosts. Additionally, if any hosts are unreachable, then
the command is unable to progress at all without removing them from the
inventory.

This change refactors the host configure and host upgrade commands to
use a single top-level playbook.

[1] https://github.com/markgoddard/ansible-experiments/tree/master/14-error-handling

Story: 2009854
Task: 44482

Change-Id: Ia63d66097b10b6ddda30ad693636143f8b1a85e0
This commit is contained in:
Mark Goddard 2021-11-09 15:19:55 +00:00 committed by Pierre Riteau
parent f00a65ead9
commit cb48f7e5d2
10 changed files with 327 additions and 166 deletions

View File

@ -0,0 +1,24 @@
---
- import_playbook: "ssh-known-host.yml"
- import_playbook: "kayobe-ansible-user.yml"
- import_playbook: "proxy.yml"
- import_playbook: "apt.yml"
- import_playbook: "dnf.yml"
- import_playbook: "pip.yml"
- import_playbook: "kayobe-target-venv.yml"
- import_playbook: "wipe-disks.yml"
- import_playbook: "users.yml"
- import_playbook: "dev-tools.yml"
- import_playbook: "disable-selinux.yml"
- import_playbook: "network.yml"
- import_playbook: "firewall.yml"
- import_playbook: "tuned.yml"
- import_playbook: "sysctl.yml"
- import_playbook: "disable-glean.yml"
- import_playbook: "disable-cloud-init.yml"
- import_playbook: "time.yml"
- import_playbook: "mdadm.yml"
- import_playbook: "luks.yml"
- import_playbook: "lvm.yml"
- import_playbook: "docker-devicemapper.yml"
- import_playbook: "docker.yml"

View File

@ -0,0 +1,26 @@
---
- import_playbook: "ssh-known-host.yml"
- import_playbook: "kayobe-ansible-user.yml"
- import_playbook: "proxy.yml"
- import_playbook: "apt.yml"
- import_playbook: "dnf.yml"
- import_playbook: "pip.yml"
- import_playbook: "kayobe-target-venv.yml"
- import_playbook: "wipe-disks.yml"
- import_playbook: "users.yml"
- import_playbook: "dev-tools.yml"
- import_playbook: "disable-selinux.yml"
- import_playbook: "network.yml"
- import_playbook: "firewall.yml"
- import_playbook: "tuned.yml"
- import_playbook: "sysctl.yml"
- import_playbook: "disable-glean.yml"
- import_playbook: "disable-cloud-init.yml"
- import_playbook: "time.yml"
- import_playbook: "mdadm.yml"
- import_playbook: "luks.yml"
- import_playbook: "lvm.yml"
- import_playbook: "docker-devicemapper.yml"
- import_playbook: "kolla-ansible-user.yml"
- import_playbook: "kolla-pip.yml"
- import_playbook: "kolla-target-venv.yml"

View File

@ -0,0 +1,5 @@
---
- import_playbook: "kayobe-target-venv.yml"
- import_playbook: "kolla-target-venv.yml"
- import_playbook: "overcloud-docker-sdk-upgrade.yml"
- import_playbook: "overcloud-etc-hosts-fixup.yml"

View File

@ -0,0 +1,27 @@
---
- import_playbook: "ssh-known-host.yml"
- import_playbook: "kayobe-ansible-user.yml"
- import_playbook: "proxy.yml"
- import_playbook: "apt.yml"
- import_playbook: "dnf.yml"
- import_playbook: "pip.yml"
- import_playbook: "kayobe-target-venv.yml"
- import_playbook: "wipe-disks.yml"
- import_playbook: "users.yml"
- import_playbook: "dev-tools.yml"
- import_playbook: "disable-selinux.yml"
- import_playbook: "network.yml"
- import_playbook: "firewall.yml"
- import_playbook: "tuned.yml"
- import_playbook: "sysctl.yml"
- import_playbook: "ip-routing.yml"
- import_playbook: "snat.yml"
- import_playbook: "disable-glean.yml"
- import_playbook: "time.yml"
- import_playbook: "mdadm.yml"
- import_playbook: "luks.yml"
- import_playbook: "lvm.yml"
- import_playbook: "docker-devicemapper.yml"
- import_playbook: "kolla-ansible-user.yml"
- import_playbook: "kolla-pip.yml"
- import_playbook: "kolla-target-venv.yml"

View File

@ -0,0 +1,3 @@
---
- import_playbook: "kayobe-target-venv.yml"
- import_playbook: "kolla-target-venv.yml"

View File

@ -0,0 +1,22 @@
---
- import_playbook: "ssh-known-host.yml"
- import_playbook: "kayobe-ansible-user.yml"
- import_playbook: "proxy.yml"
- import_playbook: "apt.yml"
- import_playbook: "dnf.yml"
- import_playbook: "pip.yml"
- import_playbook: "kayobe-target-venv.yml"
- import_playbook: "wipe-disks.yml"
- import_playbook: "users.yml"
- import_playbook: "dev-tools.yml"
- import_playbook: "network.yml"
- import_playbook: "firewall.yml"
- import_playbook: "tuned.yml"
- import_playbook: "sysctl.yml"
- import_playbook: "ip-routing.yml"
- import_playbook: "snat.yml"
- import_playbook: "time.yml"
- import_playbook: "mdadm.yml"
- import_playbook: "luks.yml"
- import_playbook: "lvm.yml"
- import_playbook: "seed-hypervisor-libvirt-host.yml"

View File

@ -11,8 +11,15 @@
hosts: seed-hypervisor:seed:overcloud:infra-vms
tags:
- wipe-disks
roles:
- role: stackhpc.luks
vars:
luks_action: teardown-unmounted
- role: wipe-disks
tasks:
- block:
- name: Tear down unmounted LUKS devices
include_role:
name: stackhpc.luks
vars:
luks_action: teardown-unmounted
- name: Wipe disks
include_role:
name: wipe-disks
when: wipe_disks | default(false) | bool

View File

@ -442,17 +442,12 @@ class SeedHypervisorHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin,
self.run_kayobe_playbooks(parsed_args, playbooks,
limit="seed-hypervisor")
playbooks = _build_playbook_list(
"ssh-known-host", "kayobe-ansible-user", "proxy",
"apt", "dnf", "pip", "kayobe-target-venv")
kwargs = {}
if parsed_args.wipe_disks:
playbooks += _build_playbook_list("wipe-disks")
playbooks += _build_playbook_list(
"users", "dev-tools", "network", "firewall", "tuned", "sysctl",
"ip-routing", "snat", "time", "mdadm", "luks", "lvm",
"seed-hypervisor-libvirt-host")
kwargs["extra_vars"] = {"wipe_disks": True}
playbooks = _build_playbook_list("seed-hypervisor-host-configure")
self.run_kayobe_playbooks(parsed_args, playbooks,
limit="seed-hypervisor")
limit="seed-hypervisor", **kwargs)
class SeedHypervisorHostPackageUpdate(KayobeAnsibleMixin, VaultMixin, Command):
@ -600,17 +595,12 @@ class SeedHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed")
# Run kayobe playbooks.
playbooks = _build_playbook_list(
"ssh-known-host", "kayobe-ansible-user", "proxy",
"apt", "dnf", "pip", "kayobe-target-venv")
kwargs = {}
if parsed_args.wipe_disks:
playbooks += _build_playbook_list("wipe-disks")
playbooks += _build_playbook_list(
"users", "dev-tools", "disable-selinux", "network", "firewall",
"tuned", "sysctl", "ip-routing", "snat", "disable-glean", "time",
"mdadm", "luks", "lvm", "docker-devicemapper",
"kolla-ansible-user", "kolla-pip", "kolla-target-venv")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed")
kwargs["extra_vars"] = {"wipe_disks": True}
playbooks = _build_playbook_list("seed-host-configure")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed",
**kwargs)
self.generate_kolla_ansible_config(parsed_args, service_config=False)
@ -685,8 +675,7 @@ class SeedHostUpgrade(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
def take_action(self, parsed_args):
self.app.LOG.debug("Upgrading seed host services")
playbooks = _build_playbook_list(
"kayobe-target-venv", "kolla-target-venv")
playbooks = _build_playbook_list("seed-host-upgrade")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="seed")
@ -906,16 +895,12 @@ class InfraVMHostConfigure(KayobeAnsibleMixin, VaultMixin,
self.run_kayobe_playbooks(parsed_args, playbooks, limit="infra-vms")
# Kayobe playbooks.
playbooks = _build_playbook_list(
"ssh-known-host", "kayobe-ansible-user", "proxy",
"apt", "dnf", "pip", "kayobe-target-venv")
kwargs = {}
if parsed_args.wipe_disks:
playbooks += _build_playbook_list("wipe-disks")
playbooks += _build_playbook_list(
"users", "dev-tools", "disable-selinux", "network", "firewall",
"tuned", "sysctl", "disable-glean", "disable-cloud-init", "time",
"mdadm", "luks", "lvm", "docker-devicemapper", "docker")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="infra-vms")
kwargs["extra_vars"] = {"wipe_disks": True}
playbooks = _build_playbook_list("infra-vm-host-configure")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="infra-vms",
**kwargs)
class InfraVMHostPackageUpdate(KayobeAnsibleMixin, VaultMixin, Command):
@ -1159,17 +1144,12 @@ class OvercloudHostConfigure(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud")
# Kayobe playbooks.
playbooks = _build_playbook_list(
"ssh-known-host", "kayobe-ansible-user", "proxy",
"apt", "dnf", "pip", "kayobe-target-venv")
kwargs = {}
if parsed_args.wipe_disks:
playbooks += _build_playbook_list("wipe-disks")
playbooks += _build_playbook_list(
"users", "dev-tools", "disable-selinux", "network", "firewall",
"tuned", "sysctl", "disable-glean", "disable-cloud-init", "time",
"mdadm", "luks", "lvm", "docker-devicemapper",
"kolla-ansible-user", "kolla-pip", "kolla-target-venv")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud")
kwargs["extra_vars"] = {"wipe_disks": True}
playbooks = _build_playbook_list("overcloud-host-configure")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud",
**kwargs)
self.generate_kolla_ansible_config(parsed_args, service_config=False)
@ -1238,9 +1218,7 @@ class OvercloudHostUpgrade(KollaAnsibleMixin, KayobeAnsibleMixin, VaultMixin,
def take_action(self, parsed_args):
self.app.LOG.debug("Upgrading overcloud host services")
playbooks = _build_playbook_list(
"kayobe-target-venv", "kolla-target-venv",
"overcloud-docker-sdk-upgrade", "overcloud-etc-hosts-fixup")
playbooks = _build_playbook_list("overcloud-host-upgrade")
self.run_kayobe_playbooks(parsed_args, playbooks, limit="overcloud")

View File

@ -316,35 +316,42 @@ class TestCase(unittest.TestCase):
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible", "ssh-known-host.yml"),
utils.get_data_files_path(
"ansible", "kayobe-ansible-user.yml"),
utils.get_data_files_path("ansible", "proxy.yml"),
utils.get_data_files_path("ansible", "apt.yml"),
utils.get_data_files_path("ansible", "dnf.yml"),
utils.get_data_files_path("ansible", "pip.yml"),
utils.get_data_files_path(
"ansible", "kayobe-target-venv.yml"),
utils.get_data_files_path("ansible", "users.yml"),
utils.get_data_files_path("ansible", "dev-tools.yml"),
utils.get_data_files_path("ansible", "network.yml"),
utils.get_data_files_path("ansible", "firewall.yml"),
utils.get_data_files_path("ansible", "tuned.yml"),
utils.get_data_files_path("ansible", "sysctl.yml"),
utils.get_data_files_path("ansible", "ip-routing.yml"),
utils.get_data_files_path("ansible", "snat.yml"),
utils.get_data_files_path("ansible", "time.yml"),
utils.get_data_files_path("ansible", "mdadm.yml"),
utils.get_data_files_path("ansible", "luks.yml"),
utils.get_data_files_path("ansible", "lvm.yml"),
utils.get_data_files_path(
"ansible", "seed-hypervisor-libvirt-host.yml"),
"ansible", "seed-hypervisor-host-configure.yml"),
],
limit="seed-hypervisor",
),
]
self.assertEqual(expected_calls, mock_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_seed_hypervisor_host_configure_wipe_disks(self, mock_run):
command = commands.SeedHypervisorHostConfigure(TestApp(), [])
parser = command.get_parser("test")
parsed_args = parser.parse_args(["--wipe-disks"])
result = command.run(parsed_args)
self.assertEqual(0, result)
expected_calls = [
mock.call(
mock.ANY,
[utils.get_data_files_path("ansible", "ip-allocation.yml")],
limit="seed-hypervisor",
),
mock.call(
mock.ANY,
[
utils.get_data_files_path(
"ansible", "seed-hypervisor-host-configure.yml"),
],
limit="seed-hypervisor",
extra_vars={"wipe_disks": True},
),
]
self.assertEqual(expected_calls, mock_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_seed_hypervisor_host_command_run(self, mock_run):
@ -492,37 +499,8 @@ class TestCase(unittest.TestCase):
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible", "ssh-known-host.yml"),
utils.get_data_files_path(
"ansible", "kayobe-ansible-user.yml"),
utils.get_data_files_path("ansible", "proxy.yml"),
utils.get_data_files_path("ansible", "apt.yml"),
utils.get_data_files_path("ansible", "dnf.yml"),
utils.get_data_files_path("ansible", "pip.yml"),
utils.get_data_files_path(
"ansible", "kayobe-target-venv.yml"),
utils.get_data_files_path("ansible", "users.yml"),
utils.get_data_files_path("ansible", "dev-tools.yml"),
utils.get_data_files_path(
"ansible", "disable-selinux.yml"),
utils.get_data_files_path("ansible", "network.yml"),
utils.get_data_files_path("ansible", "firewall.yml"),
utils.get_data_files_path("ansible", "tuned.yml"),
utils.get_data_files_path("ansible", "sysctl.yml"),
utils.get_data_files_path("ansible", "ip-routing.yml"),
utils.get_data_files_path("ansible", "snat.yml"),
utils.get_data_files_path("ansible", "disable-glean.yml"),
utils.get_data_files_path("ansible", "time.yml"),
utils.get_data_files_path("ansible", "mdadm.yml"),
utils.get_data_files_path("ansible", "luks.yml"),
utils.get_data_files_path("ansible", "lvm.yml"),
utils.get_data_files_path("ansible",
"docker-devicemapper.yml"),
utils.get_data_files_path(
"ansible", "kolla-ansible-user.yml"),
utils.get_data_files_path("ansible", "kolla-pip.yml"),
utils.get_data_files_path(
"ansible", "kolla-target-venv.yml"),
"ansible", "seed-host-configure.yml"),
],
limit="seed",
),
@ -559,6 +537,68 @@ class TestCase(unittest.TestCase):
]
self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
@mock.patch.object(commands.KollaAnsibleMixin,
"run_kolla_ansible_seed")
def test_seed_host_configure_wipe_disks(self, mock_kolla_run, mock_run):
command = commands.SeedHostConfigure(TestApp(), [])
parser = command.get_parser("test")
parsed_args = parser.parse_args(["--wipe-disks"])
result = command.run(parsed_args)
self.assertEqual(0, result)
expected_calls = [
mock.call(
mock.ANY,
[utils.get_data_files_path("ansible", "ip-allocation.yml")],
limit="seed",
),
mock.call(
mock.ANY,
[
utils.get_data_files_path(
"ansible", "seed-host-configure.yml"),
],
limit="seed",
extra_vars={"wipe_disks": True},
),
mock.call(
mock.ANY,
[utils.get_data_files_path("ansible", "kolla-ansible.yml")],
tags="config",
ignore_limit=True,
),
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible", "docker.yml"),
],
limit="seed",
),
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible",
"docker-registry.yml"),
],
limit="seed",
extra_vars={'kayobe_action': 'deploy'},
),
]
print(expected_calls)
print(mock_run.call_args_list)
self.assertEqual(expected_calls, mock_run.call_args_list)
expected_calls = [
mock.call(
mock.ANY,
"bootstrap-servers",
),
]
self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_seed_host_command_run(self, mock_run):
@ -678,9 +718,7 @@ class TestCase(unittest.TestCase):
mock.ANY,
[
utils.get_data_files_path(
"ansible", "kayobe-target-venv.yml"),
utils.get_data_files_path(
"ansible", "kolla-target-venv.yml"),
"ansible", "seed-host-upgrade.yml"),
],
limit="seed",
),
@ -984,39 +1022,42 @@ class TestCase(unittest.TestCase):
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible", "ssh-known-host.yml"),
utils.get_data_files_path(
"ansible", "kayobe-ansible-user.yml"),
utils.get_data_files_path("ansible", "proxy.yml"),
utils.get_data_files_path("ansible", "apt.yml"),
utils.get_data_files_path("ansible", "dnf.yml"),
utils.get_data_files_path("ansible", "pip.yml"),
utils.get_data_files_path(
"ansible", "kayobe-target-venv.yml"),
utils.get_data_files_path("ansible", "users.yml"),
utils.get_data_files_path("ansible", "dev-tools.yml"),
utils.get_data_files_path(
"ansible", "disable-selinux.yml"),
utils.get_data_files_path("ansible", "network.yml"),
utils.get_data_files_path("ansible", "firewall.yml"),
utils.get_data_files_path("ansible", "tuned.yml"),
utils.get_data_files_path("ansible", "sysctl.yml"),
utils.get_data_files_path("ansible", "disable-glean.yml"),
utils.get_data_files_path(
"ansible", "disable-cloud-init.yml"),
utils.get_data_files_path("ansible", "time.yml"),
utils.get_data_files_path("ansible", "mdadm.yml"),
utils.get_data_files_path("ansible", "luks.yml"),
utils.get_data_files_path("ansible", "lvm.yml"),
utils.get_data_files_path("ansible",
"docker-devicemapper.yml"),
utils.get_data_files_path("ansible", "docker.yml"),
"ansible", "infra-vm-host-configure.yml"),
],
limit="infra-vms",
),
]
self.assertEqual(expected_calls, mock_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_infra_vm_host_configure_wipe_disks(self, mock_run):
command = commands.InfraVMHostConfigure(TestApp(), [])
parser = command.get_parser("test")
parsed_args = parser.parse_args(["--wipe-disks"])
result = command.run(parsed_args)
self.assertEqual(0, result)
expected_calls = [
mock.call(
mock.ANY,
[utils.get_data_files_path("ansible", "ip-allocation.yml")],
limit="infra-vms",
),
mock.call(
mock.ANY,
[
utils.get_data_files_path(
"ansible", "infra-vm-host-configure.yml"),
],
limit="infra-vms",
extra_vars={"wipe_disks": True},
),
]
self.assertEqual(expected_calls, mock_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_infra_vm_host_upgrade(self, mock_run):
@ -1264,37 +1305,8 @@ class TestCase(unittest.TestCase):
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible", "ssh-known-host.yml"),
utils.get_data_files_path(
"ansible", "kayobe-ansible-user.yml"),
utils.get_data_files_path("ansible", "proxy.yml"),
utils.get_data_files_path("ansible", "apt.yml"),
utils.get_data_files_path("ansible", "dnf.yml"),
utils.get_data_files_path("ansible", "pip.yml"),
utils.get_data_files_path(
"ansible", "kayobe-target-venv.yml"),
utils.get_data_files_path("ansible", "users.yml"),
utils.get_data_files_path("ansible", "dev-tools.yml"),
utils.get_data_files_path(
"ansible", "disable-selinux.yml"),
utils.get_data_files_path("ansible", "network.yml"),
utils.get_data_files_path("ansible", "firewall.yml"),
utils.get_data_files_path("ansible", "tuned.yml"),
utils.get_data_files_path("ansible", "sysctl.yml"),
utils.get_data_files_path("ansible", "disable-glean.yml"),
utils.get_data_files_path(
"ansible", "disable-cloud-init.yml"),
utils.get_data_files_path("ansible", "time.yml"),
utils.get_data_files_path("ansible", "mdadm.yml"),
utils.get_data_files_path("ansible", "luks.yml"),
utils.get_data_files_path("ansible", "lvm.yml"),
utils.get_data_files_path("ansible",
"docker-devicemapper.yml"),
utils.get_data_files_path(
"ansible", "kolla-ansible-user.yml"),
utils.get_data_files_path("ansible", "kolla-pip.yml"),
utils.get_data_files_path(
"ansible", "kolla-target-venv.yml"),
"ansible", "overcloud-host-configure.yml"),
],
limit="overcloud",
),
@ -1324,6 +1336,60 @@ class TestCase(unittest.TestCase):
]
self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
@mock.patch.object(commands.KollaAnsibleMixin,
"run_kolla_ansible_overcloud")
def test_overcloud_host_configure_wipe_disks(self, mock_kolla_run,
mock_run):
command = commands.OvercloudHostConfigure(TestApp(), [])
parser = command.get_parser("test")
parsed_args = parser.parse_args(["--wipe-disks"])
result = command.run(parsed_args)
self.assertEqual(0, result)
expected_calls = [
mock.call(
mock.ANY,
[utils.get_data_files_path("ansible", "ip-allocation.yml")],
limit="overcloud",
),
mock.call(
mock.ANY,
[
utils.get_data_files_path(
"ansible", "overcloud-host-configure.yml"),
],
limit="overcloud",
extra_vars={"wipe_disks": True},
),
mock.call(
mock.ANY,
[utils.get_data_files_path("ansible", "kolla-ansible.yml")],
tags="config",
ignore_limit=True,
),
mock.call(
mock.ANY,
[
utils.get_data_files_path("ansible", "docker.yml"),
utils.get_data_files_path(
"ansible", "swift-block-devices.yml"),
],
limit="overcloud",
),
]
self.assertEqual(expected_calls, mock_run.call_args_list)
expected_calls = [
mock.call(
mock.ANY,
"bootstrap-servers",
),
]
self.assertEqual(expected_calls, mock_kolla_run.call_args_list)
@mock.patch.object(commands.KayobeAnsibleMixin,
"run_kayobe_playbooks")
def test_overcloud_host_command_run(self, mock_run):
@ -1443,13 +1509,7 @@ class TestCase(unittest.TestCase):
mock.ANY,
[
utils.get_data_files_path(
"ansible", "kayobe-target-venv.yml"),
utils.get_data_files_path(
"ansible", "kolla-target-venv.yml"),
utils.get_data_files_path(
"ansible", "overcloud-docker-sdk-upgrade.yml"),
utils.get_data_files_path(
"ansible", "overcloud-etc-hosts-fixup.yml"),
"ansible", "overcloud-host-upgrade.yml"),
],
limit="overcloud",
),

View File

@ -0,0 +1,9 @@
---
features:
- |
Improves error handling by adding a top-level playbook for the ``kayobe *
host configure`` and ``kayobe * host upgrade`` commands. This ensures that
if a host fails during a host configuration command, other hosts are able
to continue to completion. This is useful at scale, where host failures
occur more frequently. See `story 2009854
<https://storyboard.openstack.org/#!/story/2009854>`__ for details.