Skip to content

Commit

Permalink
Introducing the usage of HypervisorUpgradePlanner in planning stage. (
Browse files Browse the repository at this point in the history
#247)

Introduce base structure of the `cou.steps.plan.generate_plan`
generating the upgrade plan for data plane applications, right now it
will generate only plan using `HypervisorUpgradePlanner` for all
application deployed on same machine as Nova-compute.

---------

Co-authored-by: Gabriel Cocenza <[email protected]>
  • Loading branch information
rgildein and gabrielcocenza committed Feb 23, 2024
1 parent e71bdea commit 7e1e597
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 28 deletions.
4 changes: 3 additions & 1 deletion cou/steps/hypervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,13 +198,15 @@ def _generate_post_upgrade_plan(self) -> PostUpgradeStep:
raise NotImplementedError

# pylint: disable=unused-argument
def generate_upgrade_plan(self, target: OpenStackRelease) -> UpgradePlan:
def generate_upgrade_plan(self, target: OpenStackRelease, force: bool) -> UpgradePlan:
"""Generate full upgrade plan for all hypervisors.
This plan will be based on multiple HypervisorUpgradePlan.
:param target: OpenStack codename to upgrade.
:type target: OpenStackRelease
:param force: Whether the plan generation should be forced
:type force: bool
:return: Full upgrade plan
:rtype: UpgradePlan
"""
Expand Down
106 changes: 88 additions & 18 deletions cou/steps/plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
from cou.steps import PreUpgradeStep, UpgradePlan
from cou.steps.analyze import Analysis
from cou.steps.backup import backup
from cou.steps.hypervisor import HypervisorUpgradePlanner
from cou.utils.juju_utils import DEFAULT_TIMEOUT, COUMachine
from cou.utils.nova_compute import get_empty_hypervisors
from cou.utils.openstack import LTS_TO_OS_RELEASE, OpenStackRelease
Expand Down Expand Up @@ -293,6 +294,85 @@ def _get_os_release_and_series(analysis_result: Analysis) -> tuple[OpenStackRele
return current_os_release, current_series


def _generate_control_plane_plan(
target: OpenStackRelease, apps: list[OpenStackApplication], force: bool
) -> list[UpgradePlan]:
"""Generate upgrade plan for control plane.
:param target: Target OpenStack release.
:type target: OpenStackRelease
:param apps: List of control plane applications.
:type apps: list[OpenStackApplication]
:param force: Whether the plan generation should be forced
:type force: bool
:return: Principal and Subordiante upgrade plan.
:rtype: list[UpgradePlan]
"""
principal_upgrade_plan = create_upgrade_group(
apps=apps,
description="Control Plane principal(s) upgrade plan",
target=target,
force=force,
filter_function=lambda app: not isinstance(app, SubordinateBaseClass),
)

subordinate_upgrade_plan = create_upgrade_group(
apps=apps,
description="Control Plane subordinate(s) upgrade plan",
target=target,
force=force,
filter_function=lambda app: isinstance(app, SubordinateBaseClass),
)

logger.debug("Generation of the control plane upgrade plan complete")
return [principal_upgrade_plan, subordinate_upgrade_plan]


def _generate_data_plane_plan(
target: OpenStackRelease,
apps: list[OpenStackApplication],
hypervisors: list[COUMachine],
force: bool,
) -> list[UpgradePlan]: # pragma: no cover
"""Generate upgrade plan for data plane.
:param target: Target OpenStack release.
:type target: OpenStackRelease
:param apps: List of data plane applications.
:type apps: list[OpenStackApplication]
:param hypervisors: hypervisors filtered to generate plan and upgrade.
:type: list[COUMachine]
:param force: Whether the plan generation should be forced
:type force: bool
:return: Hypervisors plans and other data plane applications, e.g. ceph-osd
:rtype: list[UpgradePlan]
"""
nova_compute_machines = [
unit.machine.machine_id
for app in apps
for unit in app.units.values()
if app.charm == "nova-compute"
if unit.machine.machine_id not in hypervisors
]
hypervisor_apps = []
for app in apps:
if app.charm == "ceph-osd":
# ceph-osd will not be handled by hypervisor planner
continue

for machine in app.machines:
if machine in nova_compute_machines:
hypervisor_apps.append(app)
break # exiting machine for loop

hypervisor_planner = HypervisorUpgradePlanner(hypervisor_apps)
hypervisor_plans = hypervisor_planner.generate_upgrade_plan(target, force)
logger.debug("Generation of the hypervisors upgrade plan complete")

# generate the plan for the remaining data-plane apps
return [hypervisor_plans]


async def generate_plan(analysis_result: Analysis, args: CLIargs) -> UpgradePlan:
"""Generate plan for upgrade.
Expand Down Expand Up @@ -330,28 +410,18 @@ async def generate_plan(analysis_result: Analysis, args: CLIargs) -> UpgradePlan
plan.add_step(
PreUpgradeStep(
description="Backup mysql databases",
parallel=False,
coro=backup(analysis_result.model),
)
)

control_plane_principal_upgrade_plan = await create_upgrade_group(
apps=analysis_result.apps_control_plane,
description="Control Plane principal(s) upgrade plan",
target=target,
force=args.force,
filter_function=lambda app: not isinstance(app, SubordinateBaseClass),
)
plan.add_step(control_plane_principal_upgrade_plan)

control_plane_subordinate_upgrade_plan = await create_upgrade_group(
apps=analysis_result.apps_control_plane,
description="Control Plane subordinate(s) upgrade plan",
target=target,
force=args.force,
filter_function=lambda app: isinstance(app, SubordinateBaseClass),
plan.sub_steps.extend(
_generate_control_plane_plan(target, analysis_result.apps_control_plane, args.force)
)
plan.add_step(control_plane_subordinate_upgrade_plan)
# plan.sub_steps.extend(
# _generate_data_plane_plan(
# target, analysis_result.apps_data_plane, hypervisors, args.force
# )
# )

return plan

Expand Down Expand Up @@ -404,7 +474,7 @@ async def _get_upgradable_hypervisors_machines(
return await get_empty_hypervisors(nova_compute_units, analysis_result.model)


async def create_upgrade_group(
def create_upgrade_group(
apps: list[OpenStackApplication],
target: OpenStackRelease,
description: str,
Expand Down
15 changes: 6 additions & 9 deletions tests/unit/steps/test_steps_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,15 +453,14 @@ def test_determine_upgrade_target_out_support_range():


@pytest.mark.parametrize("force", [True, False])
@pytest.mark.asyncio
async def test_create_upgrade_plan(force):
def test_create_upgrade_plan(force):
"""Test create_upgrade_group."""
app: OpenStackApplication = MagicMock(spec_set=OpenStackApplication)
app.generate_upgrade_plan.return_value = MagicMock(spec_set=ApplicationUpgradePlan)
target = OpenStackRelease("victoria")
description = "test"

plan = await cou_plan.create_upgrade_group([app], target, description, force, lambda *_: True)
plan = cou_plan.create_upgrade_group([app], target, description, force, lambda *_: True)

assert plan.description == description
assert plan.parallel is False
Expand All @@ -472,31 +471,29 @@ async def test_create_upgrade_plan(force):


@pytest.mark.parametrize("force", [True, False])
@pytest.mark.asyncio
async def test_create_upgrade_plan_HaltUpgradePlanGeneration(force):
def test_create_upgrade_plan_HaltUpgradePlanGeneration(force):
"""Test create_upgrade_group."""
app: OpenStackApplication = MagicMock(spec=OpenStackApplication)
app.name = "test-app"
app.generate_upgrade_plan.side_effect = HaltUpgradePlanGeneration
target = OpenStackRelease("victoria")
description = "test"

plan = await cou_plan.create_upgrade_group([app], target, description, force, lambda *_: True)
plan = cou_plan.create_upgrade_group([app], target, description, force, lambda *_: True)

assert len(plan.sub_steps) == 0
app.generate_upgrade_plan.assert_called_once_with(target, force)


@pytest.mark.parametrize("force", [True, False])
@pytest.mark.asyncio
async def test_create_upgrade_plan_failed(force):
def test_create_upgrade_plan_failed(force):
"""Test create_upgrade_group."""
app: OpenStackApplication = MagicMock(spec=OpenStackApplication)
app.name = "test-app"
app.generate_upgrade_plan.side_effect = Exception("test")

with pytest.raises(Exception, match="test"):
await cou_plan.create_upgrade_group([app], "victoria", "test", force, lambda *_: True)
cou_plan.create_upgrade_group([app], "victoria", "test", force, lambda *_: True)


@patch("builtins.print")
Expand Down

0 comments on commit 7e1e597

Please sign in to comment.