Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Limit the plan generation of Hypervisor to the machines passed. #270

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions cou/steps/hypervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
UpgradePlan,
UpgradeStep,
)
from cou.utils.juju_utils import COUUnit
from cou.utils.juju_utils import COUMachine, COUUnit
from cou.utils.openstack import OpenStackRelease

logger = logging.getLogger(__name__)
Expand All @@ -53,7 +53,7 @@ def __eq__(self, other: Any) -> bool:
if not isinstance(other, HypervisorGroup):
return NotImplemented

return other.name == self.name
return other.name == self.name and other.app_units == self.app_units


class AZs(defaultdict):
Expand Down Expand Up @@ -95,15 +95,18 @@ class HypervisorUpgradePlanner:
This planner is meant to be used to upgrade machines contains the nova-compute application.
"""

def __init__(self, apps: list[OpenStackApplication]) -> None:
def __init__(self, apps: list[OpenStackApplication], machines: list[COUMachine]) -> None:
"""Initialize the Hypervisor class.

The application should be sorted by upgrade order.

:param apps: sorted list of OpenStack applications
:type apps: list[OpenStackApplication]
:param machines: Hypervisor machines to generate upgrade plan.
:type machines: list[COUMachine]
"""
self._apps = apps
self._machines = machines

@property
def apps(self) -> list[OpenStackApplication]:
Expand All @@ -114,6 +117,15 @@ def apps(self) -> list[OpenStackApplication]:
"""
return self._apps

@property
def machines(self) -> list[COUMachine]:
"""Return a list of hypervisor machines to upgrade.

:return: List of hypervisor machines.
:rtype: list[COUMachine]
"""
return self._machines

@property
def azs(self) -> AZs:
"""Returns a list of AZs defined in individual applications.
Expand Down Expand Up @@ -143,6 +155,10 @@ def azs(self) -> AZs:
azs = AZs()
for app in self.apps:
for unit in app.units.values():
if unit.machine not in self.machines:
logger.debug("skipping machine %s", unit.machine.machine_id)
continue

# NOTE(rgildein): If there is no AZ, we will use empty string and all units will
# belong to a single group.
az = unit.machine.az or ""
Expand Down
7 changes: 2 additions & 5 deletions cou/steps/plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,12 +419,9 @@ async def _generate_data_plane_plan(
# ceph-osd will not be handled by hypervisor planner
continue

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

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

Expand Down
143 changes: 128 additions & 15 deletions tests/unit/steps/test_hypervisor.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,12 @@ def test_generate_pre_upgrade_steps():
"""Test generating of pre-upgrade steps."""
target = OpenStackRelease("victoria")
units = ["1", "2", "3"]
machines = [COUMachine(f"{i}", (), f"az{i}") for i in range(3)]
apps = [_generate_app() for _ in range(3)]
planner = HypervisorUpgradePlanner(apps)
planner = HypervisorUpgradePlanner(apps, machines)
group = HypervisorGroup("test", {app.name.return_value: units for app in apps})

planner = HypervisorUpgradePlanner(apps)
planner = HypervisorUpgradePlanner(apps, machines)
steps = planner._generate_pre_upgrade_steps(target, group)

for step, app in zip(steps, apps):
Expand All @@ -53,10 +54,11 @@ def test_generate_post_upgrade_steps():
"""Test generating of post-upgrade steps."""
target = OpenStackRelease("victoria")
units = ["1", "2", "3"]
machines = [COUMachine(f"{i}", (), f"az{i}") for i in range(3)]
apps = [_generate_app() for _ in range(3)]
group = HypervisorGroup("test", {app.name.return_value: units for app in apps})

planner = HypervisorUpgradePlanner(apps)
planner = HypervisorUpgradePlanner(apps, machines)
steps = planner._generate_post_upgrade_steps(target, group)

for step, app in zip(steps, apps[::-1]): # using reversed order of applications
Expand All @@ -68,8 +70,10 @@ def test_hypervisor_group():
"""Test base logic of HypervisorGroup object."""
group1 = HypervisorGroup("test", {"app1": []})
group2 = HypervisorGroup("test", {"app2": []})
group3 = HypervisorGroup("test", {"app1": []})

assert group1 == group2
assert group1 != group2
assert group1 == group3
assert group1 is not None
assert group1 != "test"

Expand Down Expand Up @@ -140,19 +144,35 @@ def test_hypervisor_azs_grouping():
app1.units = {name: unit for name, unit in units.items() if name.startswith("app1")}
app2 = MagicMock(spec_set=COUApplication)()
app2.name = "app2"
app1.units = {name: unit for name, unit in units.items() if name.startswith("app2")}
app2.units = {name: unit for name, unit in units.items() if name.startswith("app2")}

exp_azs = AZs()
exp_azs["az0"].app_units["app1"] = [units["app1/0"], units["app1/1"]]
exp_azs["az0"].app_units["app2"] = [units["app2/0"]]
exp_azs["az1"].app_units["app1"] = [units["app1/2"], units["app1/3"]]
exp_azs["az1"].app_units["app2"] = [units["app2/1"]]
exp_azs["az2"].app_units["app1"] = [units["app1/4"], units["app1/5"]]
exp_azs["az2"].app_units["app2"] = [units["app2/2"]]
# passing all machines to the HypervisorUpgradePlanner
exp_azs_all = AZs()
exp_azs_all["az0"].app_units["app1"] = [units["app1/0"], units["app1/1"]]
exp_azs_all["az0"].app_units["app2"] = [units["app2/0"]]
exp_azs_all["az1"].app_units["app1"] = [units["app1/2"], units["app1/3"]]
exp_azs_all["az1"].app_units["app2"] = [units["app2/1"]]
exp_azs_all["az2"].app_units["app1"] = [units["app1/4"], units["app1/5"]]
exp_azs_all["az2"].app_units["app2"] = [units["app2/2"]]

hypervisor_planner = HypervisorUpgradePlanner([app1, app2])
hypervisor_planner_all = HypervisorUpgradePlanner([app1, app2], list(machines.values()))

assert dict(hypervisor_planner.azs) == exp_azs
assert dict(hypervisor_planner_all.azs) == exp_azs_all

# passing machine 0 to the HypervisorUpgradePlanner
exp_azs_0 = AZs()
exp_azs_0["az0"].app_units["app1"] = [units["app1/0"]]
exp_azs_0["az0"].app_units["app2"] = [units["app2/0"]]

hypervisor_planner_machine_0 = HypervisorUpgradePlanner([app1, app2], [machines["0"]])
assert dict(hypervisor_planner_machine_0.azs) == exp_azs_0

# passing machine 1 to the HypervisorUpgradePlanner
exp_azs_1 = AZs()
exp_azs_1["az0"].app_units["app1"] = [units["app1/1"]]

hypervisor_planner_machine_1 = HypervisorUpgradePlanner([app1, app2], [machines["1"]])
assert dict(hypervisor_planner_machine_1.azs) == exp_azs_1


def test_hypervisor_upgrade_plan(model):
Expand Down Expand Up @@ -273,7 +293,100 @@ def test_hypervisor_upgrade_plan(model):
workload_version="21.0.0",
)

planner = HypervisorUpgradePlanner([cinder, nova_compute])
planner = HypervisorUpgradePlanner([cinder, nova_compute], list(machines.values()))
plan = planner.generate_upgrade_plan(target, False)

assert str(plan) == exp_plan


def test_hypervisor_upgrade_plan_single_machine(model):
"""Testing generating hypervisors upgrade plan for just a single machine.

This test simulate the plan generation if the user uses cou plan hypervisors --machine 0
"""
target = OpenStackRelease("victoria")
exp_plan = dedent_plan(
"""
Upgrading all applications deployed on machines with hypervisor.
Upgrade plan for 'az-0' to victoria
Upgrade software packages of 'cinder' from the current APT repositories
Upgrade software packages on unit cinder/0
Refresh 'cinder' to the latest revision of 'ussuri/stable'
Upgrade software packages of 'nova-compute' from the current APT repositories
Upgrade software packages on unit nova-compute/0
Refresh 'nova-compute' to the latest revision of 'ussuri/stable'
Change charm config of 'cinder' 'action-managed-upgrade' to True
Upgrade 'cinder' to the new channel: 'victoria/stable'
Change charm config of 'cinder' 'openstack-origin' to 'cloud:focal-victoria'
Upgrade plan for units: cinder/0
Upgrade plan for unit: cinder/0
Pause the unit: 'cinder/0'.
Upgrade the unit: 'cinder/0'.
Resume the unit: 'cinder/0'.
Change charm config of 'nova-compute' 'action-managed-upgrade' to True
Upgrade 'nova-compute' to the new channel: 'victoria/stable'
Change charm config of 'nova-compute' 'source' to 'cloud:focal-victoria'
Upgrade plan for units: nova-compute/0
Upgrade plan for unit: nova-compute/0
Disable nova-compute scheduler from unit: 'nova-compute/0'.
Check if unit nova-compute/0 has no VMs running before upgrading.
├── Pause the unit: 'nova-compute/0'.
├── Upgrade the unit: 'nova-compute/0'.
├── Resume the unit: 'nova-compute/0'.
Enable nova-compute scheduler from unit: 'nova-compute/0'.
Wait 1800s for model test_model to reach the idle state.
Check if the workload of 'nova-compute' has been upgraded on units: nova-compute/0
Wait 300s for app cinder to reach the idle state.
Check if the workload of 'cinder' has been upgraded on units: cinder/0
"""
)
machines = {f"{i}": generate_cou_machine(f"{i}", f"az-{i}") for i in range(3)}
cinder = OpenStackApplication(
name="cinder",
can_upgrade_to="ussuri/stable",
charm="cinder",
channel="ussuri/stable",
config={
"openstack-origin": {"value": "distro"},
"action-managed-upgrade": {"value": False},
},
machines={"0": machines["0"]},
model=model,
origin="ch",
series="focal",
subordinate_to=[],
units={
"cinder/0": COUUnit(
name="cinder/0",
workload_version="16.4.2",
machine=machines["0"],
)
},
workload_version="16.4.2",
)
nova_compute = NovaCompute(
name="nova-compute",
can_upgrade_to="ussuri/stable",
charm="nova-compute",
channel="ussuri/stable",
config={"source": {"value": "distro"}, "action-managed-upgrade": {"value": False}},
machines=machines,
model=model,
origin="ch",
series="focal",
subordinate_to=[],
units={
f"nova-compute/{unit}": COUUnit(
name=f"nova-compute/{unit}",
workload_version="21.0.0",
machine=machines[f"{unit}"],
)
for unit in range(3)
},
workload_version="21.0.0",
)

planner = HypervisorUpgradePlanner([cinder, nova_compute], [machines["0"]])
plan = planner.generate_upgrade_plan(target, False)

assert str(plan) == exp_plan
Loading