From 781adceeb89ab9e551c12d99515aec251a41ee94 Mon Sep 17 00:00:00 2001 From: Guillaume Boutry Date: Tue, 21 Jul 2026 11:06:14 +0200 Subject: [PATCH] feat: drop OVN-K8S provider support Make MicroOVN the only networking provider and remove the provider toggle. Assisted-By: Codex (gpt-5-6) Signed-off-by: Guillaume Boutry --- .github/assets/testing/edge.yml | 4 - .github/assets/testing/manifest.yml | 4 - cloud/etc/deploy-microovn/main.tf | 57 ++-- cloud/etc/deploy-microovn/variables.tf | 7 - cloud/etc/deploy-openstack-hypervisor/main.tf | 21 +- .../deploy-openstack-hypervisor/variables.tf | 7 - manifests/2026.1/beta.yml | 4 - manifests/2026.1/candidate.yml | 4 - manifests/2026.1/edge.yml | 4 - manifests/2026.1/stable.yml | 4 - sunbeam-python/sunbeam/core/ovn.py | 104 +------ .../sunbeam/core/role_assignments.py | 10 +- .../sunbeam/provider/local/commands.py | 70 +---- .../sunbeam/provider/local/steps.py | 14 - .../sunbeam/provider/maas/commands.py | 49 +--- sunbeam-python/sunbeam/provider/maas/steps.py | 11 - sunbeam-python/sunbeam/steps/configure.py | 9 - sunbeam-python/sunbeam/steps/hypervisor.py | 4 - sunbeam-python/sunbeam/steps/k8s.py | 5 - sunbeam-python/sunbeam/steps/microovn.py | 107 +------ sunbeam-python/sunbeam/steps/openstack.py | 40 +-- .../sunbeam/steps/role_distributor.py | 4 - .../sunbeam/steps/upgrades/inter_channel.py | 7 +- .../sunbeam/steps/upgrades/intra_channel.py | 4 +- sunbeam-python/sunbeam/versions.py | 6 - .../sunbeam/core/test_role_assignments.py | 26 +- .../unit/sunbeam/provider/local/test_steps.py | 262 +----------------- .../unit/sunbeam/provider/maas/test_maas.py | 21 +- .../unit/sunbeam/provider/maas/test_steps.py | 29 +- .../unit/sunbeam/steps/test_configure.py | 68 ----- .../tests/unit/sunbeam/steps/test_microovn.py | 157 +---------- .../unit/sunbeam/steps/test_openstack.py | 49 +--- .../sunbeam/steps/test_role_distributor.py | 38 --- 33 files changed, 96 insertions(+), 1114 deletions(-) diff --git a/.github/assets/testing/edge.yml b/.github/assets/testing/edge.yml index c77babeaa..2ae5172a6 100644 --- a/.github/assets/testing/edge.yml +++ b/.github/assets/testing/edge.yml @@ -28,10 +28,6 @@ core: channel: 2026.1/edge config: snap-channel: 2026.1/edge - ovn-central-k8s: - channel: 26.03/edge - ovn-relay-k8s: - channel: 26.03/edge placement-k8s: channel: 2026.1/edge sunbeam-clusterd: diff --git a/.github/assets/testing/manifest.yml b/.github/assets/testing/manifest.yml index 8ed31e7cd..7ffd1735a 100644 --- a/.github/assets/testing/manifest.yml +++ b/.github/assets/testing/manifest.yml @@ -62,10 +62,6 @@ core: channel: OS_CHARM watcher-k8s: channel: OS_CHARM - ovn-central-k8s: - channel: OVN_CHARM - ovn-relay-k8s: - channel: OVN_CHARM placement-k8s: channel: OS_CHARM sunbeam-clusterd: diff --git a/cloud/etc/deploy-microovn/main.tf b/cloud/etc/deploy-microovn/main.tf index d13c91365..16b381284 100644 --- a/cloud/etc/deploy-microovn/main.tf +++ b/cloud/etc/deploy-microovn/main.tf @@ -120,11 +120,14 @@ resource "juju_application" "microovn_arm64" { endpoint_bindings = var.endpoint_bindings } +moved { + from = juju_application.sunbeam-ovn-proxy[0] + to = juju_application.sunbeam-ovn-proxy +} + resource "juju_application" "sunbeam-ovn-proxy" { name = "sunbeam-ovn-proxy" model_uuid = data.juju_model.machine_model.uuid - # Only deploy when microovn is the SDN provider - count = var.ovn-relay-offer-url == null ? 1 : 0 # Deploy on same machine as token distributor machines = length(var.token_distributor_machine_ids) == 0 ? null : toset(var.token_distributor_machine_ids) units = length(var.token_distributor_machine_ids) == 0 ? 1 : null @@ -167,20 +170,6 @@ resource "juju_integration" "microovn-certs" { } } -resource "juju_integration" "microovn-ovsdb-cms" { - count = (var.ovn-relay-offer-url != null) ? 1 : 0 - model_uuid = data.juju_model.machine_model.uuid - - application { - name = juju_application.microovn.name - endpoint = "ovsdb-external" - } - - application { - offer_url = var.ovn-relay-offer-url - } -} - resource "juju_integration" "microovn-openstack-network-agents" { model_uuid = data.juju_model.machine_model.uuid @@ -272,25 +261,10 @@ resource "juju_integration" "microovn_arm64_certs" { } } -resource "juju_integration" "microovn_arm64_ovsdb_cms" { - count = (var.ovn-relay-offer-url != null && length(local.microovn_arm64_machine_ids) > 0) ? 1 : 0 - model_uuid = data.juju_model.machine_model.uuid - - application { - name = juju_application.microovn_arm64[0].name - endpoint = "ovsdb-external" - } - - application { - offer_url = var.ovn-relay-offer-url - } -} - resource "juju_integration" "microovn_arm64_to_ovn_proxy" { count = ( length(local.microovn_arm64_machine_ids) > 0 && length(local.microovn_machine_ids) == 0 - && length(juju_application.sunbeam-ovn-proxy.*.name) > 0 ) ? 1 : 0 model_uuid = data.juju_model.machine_model.uuid @@ -300,18 +274,15 @@ resource "juju_integration" "microovn_arm64_to_ovn_proxy" { } application { - name = juju_application.sunbeam-ovn-proxy[0].name + name = juju_application.sunbeam-ovn-proxy.name endpoint = "ovsdb" } } resource "juju_integration" "microovn-to-ovn-proxy" { count = ( - length(juju_application.sunbeam-ovn-proxy.*.name) > 0 - && ( - length(local.microovn_machine_ids) > 0 - || length(local.microovn_arm64_machine_ids) == 0 - ) + length(local.microovn_machine_ids) > 0 + || length(local.microovn_arm64_machine_ids) == 0 ) ? 1 : 0 model_uuid = data.juju_model.machine_model.uuid @@ -321,15 +292,19 @@ resource "juju_integration" "microovn-to-ovn-proxy" { } application { - name = juju_application.sunbeam-ovn-proxy[0].name + name = juju_application.sunbeam-ovn-proxy.name endpoint = "ovsdb" } } +moved { + from = juju_offer.ovsdb-cms[0] + to = juju_offer.ovsdb-cms +} + resource "juju_offer" "ovsdb-cms" { - count = length(juju_application.sunbeam-ovn-proxy.*.name) > 0 ? 1 : 0 model_uuid = data.juju_model.machine_model.uuid - application_name = juju_application.sunbeam-ovn-proxy[0].name + application_name = juju_application.sunbeam-ovn-proxy.name endpoints = ["ovsdb-cms"] } @@ -342,5 +317,5 @@ output "microovn-arm64-application-name" { } output "ovsdb-cms-offer" { - value = try(juju_offer.ovsdb-cms[0].url, null) + value = juju_offer.ovsdb-cms.url } diff --git a/cloud/etc/deploy-microovn/variables.tf b/cloud/etc/deploy-microovn/variables.tf index 408a59b41..ad35200a6 100644 --- a/cloud/etc/deploy-microovn/variables.tf +++ b/cloud/etc/deploy-microovn/variables.tf @@ -136,10 +136,3 @@ variable "ca-offer-url" { type = string default = null } - -# Mandatory relation, no defaults -variable "ovn-relay-offer-url" { - description = "Offer URL for ovn relay service" - type = string - default = null -} diff --git a/cloud/etc/deploy-openstack-hypervisor/main.tf b/cloud/etc/deploy-openstack-hypervisor/main.tf index c541de7e6..e6b73e04d 100644 --- a/cloud/etc/deploy-openstack-hypervisor/main.tf +++ b/cloud/etc/deploy-openstack-hypervisor/main.tf @@ -99,28 +99,11 @@ resource "juju_integration" "hypervisor-certs" { } moved { - from = juju_integration.hypervisor-ovn - to = juju_integration.hypervisor-ovn[0] -} - -resource "juju_integration" "hypervisor-ovn" { - # Should be deployed if ovn-relay-offer-url set - count = (var.ovn-relay-offer-url != null) ? 1 : 0 - model_uuid = data.juju_model.machine_model.uuid - - application { - name = juju_application.openstack-hypervisor.name - endpoint = "ovsdb-cms" - } - - application { - offer_url = var.ovn-relay-offer-url - } + from = juju_integration.hypervisor-ovn-proxy[0] + to = juju_integration.hypervisor-ovn-proxy } resource "juju_integration" "hypervisor-ovn-proxy" { - # Shouldn't be deployed if ovn-relay-offer-url is set - count = (var.ovn-relay-offer-url == null) ? 1 : 0 model_uuid = data.juju_model.machine_model.uuid application { name = juju_application.openstack-hypervisor.name diff --git a/cloud/etc/deploy-openstack-hypervisor/variables.tf b/cloud/etc/deploy-openstack-hypervisor/variables.tf index 8b33500c0..b94711320 100644 --- a/cloud/etc/deploy-openstack-hypervisor/variables.tf +++ b/cloud/etc/deploy-openstack-hypervisor/variables.tf @@ -66,13 +66,6 @@ variable "ca-offer-url" { default = null } -# Mandatory relation, no defaults -variable "ovn-relay-offer-url" { - description = "Offer URL for ovn relay service" - type = string - default = null -} - variable "ceilometer-offer-url" { description = "Offer URL for openstack ceilometer" type = string diff --git a/manifests/2026.1/beta.yml b/manifests/2026.1/beta.yml index 9af795bc8..4d7ebcd79 100644 --- a/manifests/2026.1/beta.yml +++ b/manifests/2026.1/beta.yml @@ -23,10 +23,6 @@ core: channel: 2026.1/beta config: snap-channel: 2026.1/beta - ovn-central-k8s: - channel: 26.03/beta - ovn-relay-k8s: - channel: 26.03/beta placement-k8s: channel: 2026.1/beta sunbeam-clusterd: diff --git a/manifests/2026.1/candidate.yml b/manifests/2026.1/candidate.yml index 8638f4b60..392b4de28 100644 --- a/manifests/2026.1/candidate.yml +++ b/manifests/2026.1/candidate.yml @@ -23,10 +23,6 @@ core: channel: 2026.1/candidate config: snap-channel: 2026.1/candidate - ovn-central-k8s: - channel: 26.03/candidate - ovn-relay-k8s: - channel: 26.03/candidate placement-k8s: channel: 2026.1/candidate sunbeam-clusterd: diff --git a/manifests/2026.1/edge.yml b/manifests/2026.1/edge.yml index 82d3f357d..051972457 100644 --- a/manifests/2026.1/edge.yml +++ b/manifests/2026.1/edge.yml @@ -23,10 +23,6 @@ core: channel: 2026.1/edge config: snap-channel: 2026.1/edge - ovn-central-k8s: - channel: 26.03/edge - ovn-relay-k8s: - channel: 26.03/edge placement-k8s: channel: 2026.1/edge sunbeam-clusterd: diff --git a/manifests/2026.1/stable.yml b/manifests/2026.1/stable.yml index dae410599..1fb54bc31 100644 --- a/manifests/2026.1/stable.yml +++ b/manifests/2026.1/stable.yml @@ -23,10 +23,6 @@ core: channel: 2026.1/stable config: snap-channel: 2026.1/stable - ovn-central-k8s: - channel: 26.03/stable - ovn-relay-k8s: - channel: 26.03/stable placement-k8s: channel: 2026.1/stable sunbeam-clusterd: diff --git a/sunbeam-python/sunbeam/core/ovn.py b/sunbeam-python/sunbeam/core/ovn.py index baeee31b6..b1e63f285 100644 --- a/sunbeam-python/sunbeam/core/ovn.py +++ b/sunbeam-python/sunbeam/core/ovn.py @@ -1,75 +1,27 @@ # SPDX-FileCopyrightText: 2025 - Canonical Ltd # SPDX-License-Identifier: Apache-2.0 -import enum from collections.abc import Iterable -import pydantic - from sunbeam.clusterd.client import Client from sunbeam.core.common import Role from sunbeam.core.deployment import Deployment from sunbeam.core.juju import JujuHelper -from sunbeam.core.questions import load_answers, write_answers - -CLUSTERD_CONFIG_KEY = "OvnConfig" -SNAP_PROVIDER_CONFIG_KEY = "ovn.provider" - - -class OvnProvider(enum.StrEnum): - OVN_K8S = "ovn-k8s" - MICROOVN = "microovn" - -class OvnConfig(pydantic.BaseModel): - provider: OvnProvider | None = None - - -DEFAULT_PROVIDER = OvnProvider.OVN_K8S DEFAULT_ARCHITECTURE = "amd64" ARM64_ARCHITECTURE = "arm64" -def load_provider_config(client: Client) -> OvnConfig: - """Load the OVN provider configuration from the cluster deployment answers. - - :param client: the Sunbeam client - :return: the OVN provider configuration - """ - answers = load_answers(client, CLUSTERD_CONFIG_KEY) - return OvnConfig.model_validate(answers) - - -def write_provider_config(client: Client, config: OvnConfig) -> None: - """Write the OVN provider configuration to the cluster deployment answers. - - :param client: the Sunbeam client - :param config: the OVN provider configuration - """ - write_answers(client, CLUSTERD_CONFIG_KEY, config.model_dump()) - - class OvnManager: def __init__(self, client: Client): self.client = client - def get_provider(self) -> OvnProvider: - """Get the OVN provider from the configuration.""" - config = load_provider_config(self.client) - if config.provider is None: - return DEFAULT_PROVIDER - return config.provider - def get_roles_for_microovn(self) -> set[Role]: """Get list of roles where microovn is necessary. :return: set of roles """ - provider = self.get_provider() - roles = {Role.NETWORK} - if provider == OvnProvider.MICROOVN: - roles |= {Role.COMPUTE, Role.CONTROL} - return roles + return {Role.CONTROL, Role.COMPUTE, Role.NETWORK} def is_microovn_necessary(self, roles: Iterable[Role]) -> bool: """Check if microovn is necessary for the given roles. @@ -79,19 +31,6 @@ def is_microovn_necessary(self, roles: Iterable[Role]) -> bool: """ return len(self.get_roles_for_microovn().intersection(roles)) > 0 - def is_network_agent_dataplane_node(self, roles: Iterable[Role]) -> bool: - """Check whether the node is a network agent dataplane node. - - :param roles: iterable of roles - :return: True if the role is managed by openstack-network-agents, - False otherwise - """ - provider = self.get_provider() - dataplane_roles = {Role.NETWORK} - if provider == OvnProvider.MICROOVN: - dataplane_roles.add(Role.COMPUTE) - return len(dataplane_roles.intersection(roles)) > 0 - def is_microovn_necessary_maas( self, nb_network: int, nb_compute: int, nb_control: int ) -> bool: @@ -102,30 +41,19 @@ def is_microovn_necessary_maas( :param nb_control: number of control nodes :return: True if microovn is necessary, False otherwise """ - provider = self.get_provider() - if provider == OvnProvider.MICROOVN: - return (nb_network + nb_compute + nb_control) > 0 - else: - return nb_network > 0 + return (nb_network + nb_compute + nb_control) > 0 def _list_microovn_nodes(self) -> list[dict]: """Collect cluster nodes that should run MicroOVN.""" - nodes = self.client.cluster.list_nodes_by_role("network") - if self.get_provider() == OvnProvider.MICROOVN: - nodes += self.client.cluster.list_nodes_by_role("compute") - nodes += self.client.cluster.list_nodes_by_role("control") - return nodes - - def get_token_distributor_machines( - self, provider: OvnProvider | None = None - ) -> list[str]: - """Get machine IDs for MicroOVN helper applications.""" - provider = provider or self.get_provider() - roles = [Role.NETWORK] - if provider == OvnProvider.MICROOVN: - roles = [Role.CONTROL, Role.COMPUTE, Role.NETWORK] + return ( + self.client.cluster.list_nodes_by_role("control") + + self.client.cluster.list_nodes_by_role("compute") + + self.client.cluster.list_nodes_by_role("network") + ) - for role in roles: + def get_token_distributor_machines(self) -> list[str]: + """Get machine IDs for MicroOVN helper applications.""" + for role in (Role.CONTROL, Role.COMPUTE, Role.NETWORK): machine_ids: set[str] = set() for node in self.client.cluster.list_nodes_by_role(role.name.lower()): machineid = node.get("machineid") @@ -176,11 +104,7 @@ def get_control_plane_tfvars( :return: dict of Terraform variables """ - provider = self.get_provider() - tfvars = {} - if provider == OvnProvider.MICROOVN: - model_name = jhelper.get_model_name_with_owner( - deployment.openstack_machines_model - ) - tfvars["external-ovsdb-cms-offer-url"] = model_name + ".sunbeam-ovn-proxy" - return tfvars + model_name = jhelper.get_model_name_with_owner( + deployment.openstack_machines_model + ) + return {"external-ovsdb-cms-offer-url": model_name + ".sunbeam-ovn-proxy"} diff --git a/sunbeam-python/sunbeam/core/role_assignments.py b/sunbeam-python/sunbeam/core/role_assignments.py index eb3ed2057..8ef23154b 100644 --- a/sunbeam-python/sunbeam/core/role_assignments.py +++ b/sunbeam-python/sunbeam/core/role_assignments.py @@ -19,8 +19,6 @@ def build_microovn_role_mapping( client: Client, model_name: str, machine_ids: Iterable[str], - *, - assign_central_roles: bool, ) -> RoleMapping: """Build the role-distributor mapping for MicroOVN.""" machine_roles: dict[str, list[str]] = {} @@ -35,10 +33,7 @@ def build_microovn_role_mapping( if machine_id_str not in microovn_machine_ids: continue node_roles = set(node.get("role", [])) - machine_roles[machine_id_str] = _microovn_roles_for_node( - node_roles, - assign_central_roles, - ) + machine_roles[machine_id_str] = _microovn_roles_for_node(node_roles) return _build_mapping( model_name, @@ -71,12 +66,11 @@ def _build_mapping( def _microovn_roles_for_node( node_roles: Iterable[str], - assign_central_roles: bool, ) -> list[str]: role_set = set(node_roles) roles = ["chassis"] - if assign_central_roles and "control" in role_set: + if "control" in role_set: roles.append("central") if "network" in role_set: roles.append("gateway") diff --git a/sunbeam-python/sunbeam/provider/local/commands.py b/sunbeam-python/sunbeam/provider/local/commands.py index 3655fea7f..6377e079b 100644 --- a/sunbeam-python/sunbeam/provider/local/commands.py +++ b/sunbeam-python/sunbeam/provider/local/commands.py @@ -28,7 +28,6 @@ ) from sunbeam.commands.dashboard import retrieve_dashboard_url from sunbeam.commands.proxy import PromptForProxyStep -from sunbeam.core import ovn from sunbeam.core.checks import ( Check, DaemonGroupCheck, @@ -92,7 +91,6 @@ LocalConfigDPDKStep, LocalConfigSRIOVStep, LocalEndpointsConfigurationStep, - LocalSetHypervisorUnitsOptionsStep, LocalSetOpenStackNetworkAgentsStep, LocalUserQuestions, ) @@ -151,7 +149,6 @@ AddK8SCloudStep, AddK8SCredentialStep, CheckMysqlK8SDistributionStep, - CheckOvnK8SDistributionStep, CheckRabbitmqK8SDistributionStep, CordonK8SUnitStep, DeployK8SApplicationStep, @@ -177,7 +174,6 @@ ReapplyMicroOVNOptionalIntegrationsStep, ReapplyMicroOVNTerraformPlanStep, RemoveMicroOVNUnitsStep, - SetOvnProviderStep, ) from sunbeam.steps.openstack import ( DeployControlPlaneStep, @@ -751,7 +747,6 @@ def bootstrap( # noqa: C901 plan.append(ClusterInitStep(client, roles_to_str_list(roles), 0, management_cidr)) plan.append(SyncFeatureGatesToCluster(client)) plan.append(SaveManagementCidrStep(client, management_cidr)) - plan.append(SetOvnProviderStep(client, snap)) plan.append(AddManifestStep(client, manifest_path)) plan.append( PromptForProxyStep( @@ -982,7 +977,7 @@ def bootstrap( # noqa: C901 plan2: list[BaseStep] = [] if is_control_node or is_region_controller: - plan2.append(OpenStackPatchLoadBalancerServicesIPStep(client, ovn_manager)) + plan2.append(OpenStackPatchLoadBalancerServicesIPStep(client)) if not is_region_controller: # NOTE(jamespage): @@ -1626,17 +1621,16 @@ def join( # noqa: C901 deployment.get_ovn_manager(), ) ) - if ovn_manager.is_network_agent_dataplane_node(roles) or microovn_necessary: - plan4.append( - LocalSetOpenStackNetworkAgentsStep( - client, - name, - jhelper, - deployment.openstack_machines_model, - join_mode=True, - manifest=manifest, - ), - ) + plan4.append( + LocalSetOpenStackNetworkAgentsStep( + client, + name, + jhelper, + deployment.openstack_machines_model, + join_mode=True, + manifest=manifest, + ), + ) if is_storage_node: # Check if this is the first storage node joining @@ -1753,19 +1747,6 @@ def join( # noqa: C901 ), ] ) - if not microovn_necessary: - # Only set local settings if MicroOVN is not deployed on the - # current node - plan4.append( - LocalSetHypervisorUnitsOptionsStep( - client, - name, - jhelper, - deployment.openstack_machines_model, - join_mode=True, - manifest=manifest, - ) - ) plan4.extend( [ LocalConfigSRIOVStep( @@ -1901,13 +1882,6 @@ def remove(ctx: click.Context, name: str, force: bool, show_hints: bool) -> None deployment.openstack_machines_model, force=force, ), - CheckOvnK8SDistributionStep( - client, - name, - jhelper, - deployment.openstack_machines_model, - force=force, - ), CheckRabbitmqK8SDistributionStep( client, name, @@ -2117,25 +2091,8 @@ def configure_cmd( ] ) - is_microovn_deployment = ( - deployment.get_ovn_manager().get_provider() == ovn.OvnProvider.MICROOVN - ) - if "compute" in node["role"]: tfhelper_hypervisor = deployment.get_tfhelper("hypervisor-plan") - if not is_microovn_deployment: - plan.append( - LocalSetHypervisorUnitsOptionsStep( - client, - name, - jhelper, - deployment.openstack_machines_model, - # Accept preseed file but do not allow 'accept_defaults' as nic - # selection may vary from machine to machine and is potentially - # destructive if it takes over an unintended nic. - manifest=manifest, - ) - ) plan.append(TerraformInitStep(tfhelper_hypervisor)) plan.append( ReapplyHypervisorTerraformPlanStep( @@ -2147,10 +2104,7 @@ def configure_cmd( ) ) - if "network" in node["role"] or ( - is_microovn_deployment - and ("compute" in node["role"] or "control" in node["role"]) - ): + if {"control", "compute", "network"}.intersection(node["role"]): role_distributor_tfhelper = deployment.get_tfhelper("role-distributor-plan") microovn_tfhelper = deployment.get_tfhelper("microovn-plan") plan.append(TerraformInitStep(role_distributor_tfhelper)) diff --git a/sunbeam-python/sunbeam/provider/local/steps.py b/sunbeam-python/sunbeam/provider/local/steps.py index 8f80bfaf4..cfe4ed5e4 100644 --- a/sunbeam-python/sunbeam/provider/local/steps.py +++ b/sunbeam-python/sunbeam/provider/local/steps.py @@ -43,7 +43,6 @@ CLOUD_CONFIG_SECTION, BaseUserQuestions, OpenstackNetworkAgentsUnitGetterMixin, - PrincipalUnitGetterMixin, SetExternalNetworkUnitsOptionsStep, physical_network_question, user_questions, @@ -300,19 +299,6 @@ def prompt( ) -class LocalSetHypervisorUnitsOptionsStep( - LocalSetExternalNetworkUnitsOptionsStep, PrincipalUnitGetterMixin -): - APP = hypervisor.APPLICATION - DISPLAY_NAME = "hypervisor" - ACTION = "set-hypervisor-local-settings" - - def _fetch_nics(self) -> dict: - return nic_utils.fetch_nics( - self.client, self.names[0], self.jhelper, self.model - ) - - class LocalSetOpenStackNetworkAgentsStep( LocalSetExternalNetworkUnitsOptionsStep, OpenstackNetworkAgentsUnitGetterMixin ): diff --git a/sunbeam-python/sunbeam/provider/maas/commands.py b/sunbeam-python/sunbeam/provider/maas/commands.py index 81c6ec11b..b456051fa 100644 --- a/sunbeam-python/sunbeam/provider/maas/commands.py +++ b/sunbeam-python/sunbeam/provider/maas/commands.py @@ -27,7 +27,6 @@ ) from sunbeam.commands.dashboard import retrieve_dashboard_url from sunbeam.commands.proxy import PromptForProxyStep -from sunbeam.core import ovn from sunbeam.core.checks import ( Check, DiagnosticResultType, @@ -108,7 +107,6 @@ MaasSaveClusterdCredentialsStep, MaasSaveControllerStep, MaasScaleJujuStep, - MaasSetHypervisorUnitsOptionsStep, MaasSetOpenStackNetworkAgentsStep, MaasUserQuestions, MachineComputeNicCheck, @@ -157,7 +155,6 @@ from sunbeam.steps.k8s import ( AddK8SCloudStep, CheckMysqlK8SDistributionStep, - CheckOvnK8SDistributionStep, CheckRabbitmqK8SDistributionStep, CordonK8SUnitStep, DestroyK8SApplicationStep, @@ -183,7 +180,6 @@ ReapplyMicroOVNOptionalIntegrationsStep, ReapplyMicroOVNTerraformPlanStep, RemoveMicroOVNUnitsStep, - SetOvnProviderStep, ) from sunbeam.steps.openstack import ( DeployControlPlaneStep, @@ -507,10 +503,8 @@ def bootstrap( sys.exit(1) client = deployment.get_client() - snap = Snap() plan3: list[BaseStep] = [] plan3.append(AddManifestStep(client, manifest_path)) - plan3.append(SetOvnProviderStep(client, snap)) plan3.append(MaasAddMachinesToClusterdStep(client, maas_client)) if not juju_controller: plan3.append( @@ -953,7 +947,7 @@ def deploy( deployment.openstack_machines_model, ) ) - plan2.append(OpenStackPatchLoadBalancerServicesIPStep(client, ovn_manager)) + plan2.append(OpenStackPatchLoadBalancerServicesIPStep(client)) if nb_compute: plan2.append(TerraformInitStep(tfhelper_hypervisor_deploy)) @@ -1075,19 +1069,16 @@ def configure_cmd( network = list( map(_name_mapper, client.cluster.list_nodes_by_role(RoleTags.NETWORK.value)) ) - ovn_manager = deployment.get_ovn_manager() - network_agents_names = network - if ovn_manager.get_provider() == ovn.OvnProvider.MICROOVN: - # All nodes with MicroOVN network agents need the action called. Only - # network nodes get enable-chassis-as-gw=true; compute-only and - # control-only nodes get enable-chassis-as-gw=false. - control = list( - map( - _name_mapper, - client.cluster.list_nodes_by_role(RoleTags.CONTROL.value), - ) + # All nodes with MicroOVN network agents need the action called. Only + # network nodes get enable-chassis-as-gw=true; compute-only and + # control-only nodes get enable-chassis-as-gw=false. + control = list( + map( + _name_mapper, + client.cluster.list_nodes_by_role(RoleTags.CONTROL.value), ) - network_agents_names = list(dict.fromkeys(network + compute + control)) + ) + network_agents_names = list(dict.fromkeys(network + compute + control)) plan = [ AddManifestStep(client, manifest_path), JujuLoginStep(deployment.juju_account), @@ -1128,19 +1119,6 @@ def configure_cmd( manifest, ), ] - if ovn_manager.get_provider() == ovn.OvnProvider.OVN_K8S: - # In OVN_K8S mode, dataplane is managed by hypervisor units - plan.append( - MaasSetHypervisorUnitsOptionsStep( - client, - maas_client, - compute, - jhelper, - deployment.openstack_machines_model, - manifest, - ), - ) - run_plan(plan, console, show_hints) dashboard_url = retrieve_dashboard_url(jhelper) console.print("The cloud has been configured for sample usage.") @@ -1704,13 +1682,6 @@ def remove_node(ctx: click.Context, name: str, force: bool, show_hints: bool) -> deployment.openstack_machines_model, force=force, ), - CheckOvnK8SDistributionStep( - client, - name, - jhelper, - deployment.openstack_machines_model, - force=force, - ), CheckRabbitmqK8SDistributionStep( client, name, diff --git a/sunbeam-python/sunbeam/provider/maas/steps.py b/sunbeam-python/sunbeam/provider/maas/steps.py index 5431cc90a..ea0fac8f9 100644 --- a/sunbeam-python/sunbeam/provider/maas/steps.py +++ b/sunbeam-python/sunbeam/provider/maas/steps.py @@ -64,7 +64,6 @@ from sunbeam.steps.configure import ( BaseUserQuestions, OpenstackNetworkAgentsUnitGetterMixin, - PrincipalUnitGetterMixin, SetExternalNetworkUnitsOptionsStep, ) from sunbeam.steps.juju import ( @@ -2245,16 +2244,6 @@ def is_skip(self, context: StepContext): return Result(ResultType.COMPLETED) -class MaasSetHypervisorUnitsOptionsStep( - MaasSetExternalNetworkUnitsOptionsStep, PrincipalUnitGetterMixin -): - """Configure hypervisor settings on the machines.""" - - APP = "openstack-hypervisor" - DISPLAY_NAME = "hypervisor" - ACTION = "set-hypervisor-local-settings" - - class MaasSetOpenStackNetworkAgentsStep( MaasSetExternalNetworkUnitsOptionsStep, OpenstackNetworkAgentsUnitGetterMixin ): diff --git a/sunbeam-python/sunbeam/steps/configure.py b/sunbeam-python/sunbeam/steps/configure.py index fb2797df2..34ad9fda9 100644 --- a/sunbeam-python/sunbeam/steps/configure.py +++ b/sunbeam-python/sunbeam/steps/configure.py @@ -472,15 +472,6 @@ def get_unit(self, name: str) -> str: raise NotImplementedError -class PrincipalUnitGetterMixin(UnitGetterMixin): - def get_unit(self, name: str) -> str: - """Get the juju unit for the given machine name.""" - node = self.client.cluster.get_node_info(name) - machine_id = str(node.get("machineid")) - unit = self.jhelper.get_unit_from_machine(self.APP, machine_id, self.model) - return unit - - class OpenstackNetworkAgentsUnitGetterMixin(UnitGetterMixin, JujuStepHelper): def get_unit(self, name: str) -> str: """Get the juju unit for the given network agent name.""" diff --git a/sunbeam-python/sunbeam/steps/hypervisor.py b/sunbeam-python/sunbeam/steps/hypervisor.py index c59c82bb1..45cae9b2e 100644 --- a/sunbeam-python/sunbeam/steps/hypervisor.py +++ b/sunbeam-python/sunbeam/steps/hypervisor.py @@ -16,7 +16,6 @@ get_dpdk_config, get_pci_whitelist_config, ) -from sunbeam.core import ovn from sunbeam.core.common import ( BaseStep, Result, @@ -94,7 +93,6 @@ def __init__( ) self.openstack_tfhelper = openstack_tfhelper self.cinder_volume_tfhelper = cinder_volume_tfhelper - self.ovn_manager = deployment.get_ovn_manager() def extra_tfvars(self) -> dict: """Extra terraform vars to pass to terraform apply.""" @@ -115,8 +113,6 @@ def extra_tfvars(self) -> dict: "ca-offer-url", "nova-offer-url", } - if self.ovn_manager.get_provider() == ovn.OvnProvider.OVN_K8S: - juju_offers.add("ovn-relay-offer-url") extra_tfvars = {offer: openstack_tf_output.get(offer) for offer in juju_offers} if len(storage_nodes) > 0: diff --git a/sunbeam-python/sunbeam/steps/k8s.py b/sunbeam-python/sunbeam/steps/k8s.py index 88e92b6f9..b78691e25 100644 --- a/sunbeam-python/sunbeam/steps/k8s.py +++ b/sunbeam-python/sunbeam/steps/k8s.py @@ -1093,11 +1093,6 @@ class CheckRabbitmqK8SDistributionStep(CheckApplicationK8SDistributionStep): _SUBSTRATE = APPLICATION -class CheckOvnK8SDistributionStep(CheckApplicationK8SDistributionStep): - _CHARM = "ovn-central-k8s" - _SUBSTRATE = APPLICATION - - class CordonK8SUnitStep(BaseStep, _CommonK8SStepMixin): _SUBSTRATE: str = APPLICATION diff --git a/sunbeam-python/sunbeam/steps/microovn.py b/sunbeam-python/sunbeam/steps/microovn.py index 12ef976ff..f6bc18aaa 100644 --- a/sunbeam-python/sunbeam/steps/microovn.py +++ b/sunbeam-python/sunbeam/steps/microovn.py @@ -5,7 +5,6 @@ from typing import Any import tenacity -from snaphelpers import Snap, UnknownConfigKey from sunbeam import versions from sunbeam.clusterd.client import Client @@ -80,12 +79,9 @@ def _role_distributor_application_name(jhelper: JujuHelper, model: str) -> str | return ROLE_DISTRIBUTOR_APP -def _microovn_accepted_statuses(ovn_manager: ovn.OvnManager) -> list[str]: +def _microovn_accepted_statuses() -> list[str]: """Return statuses accepted while waiting for MicroOVN.""" - statuses = ["active", "unknown"] - if ovn_manager.get_provider() == ovn.OvnProvider.OVN_K8S: - statuses.append("blocked") - return statuses + return ["active", "unknown"] def _openstack_network_agents_tfvars( @@ -151,26 +147,21 @@ def get_application_timeout(self) -> int: def get_accepted_application_status(self) -> list[str]: """Accepted status to pass wait_application_ready function.""" - return _microovn_accepted_statuses(self.ovn_manager) + return _microovn_accepted_statuses() def extra_tfvars(self) -> dict: """Extra terraform vars to pass to terraform apply.""" openstack_tfhelper = self.deployment.get_tfhelper("openstack-plan") openstack_tf_output = openstack_tfhelper.output() - juju_offers = { - "ca-offer-url", - "ovn-relay-offer-url", - } + juju_offers = {"ca-offer-url"} extra_tfvars: dict[str, Any] = { offer: openstack_tf_output.get(offer) for offer in juju_offers } machines_by_arch = self.ovn_manager.get_machines_by_architecture() extra_tfvars["microovn_machine_ids_by_architecture"] = machines_by_arch - distributor_ids = self.ovn_manager.get_token_distributor_machines( - ovn.OvnProvider.MICROOVN - ) + distributor_ids = self.ovn_manager.get_token_distributor_machines() extra_tfvars["token_distributor_machine_ids"] = distributor_ids[:1] # Juju does not resolve per-arch revisions for subordinates, so pin the @@ -277,11 +268,9 @@ def tf_apply_extra_args(self) -> list[str]: extra_args = [ "-target=juju_integration.microovn-microcluster-token-distributor", "-target=juju_integration.microovn-certs", - "-target=juju_integration.microovn-ovsdb-cms", "-target=juju_integration.microovn-openstack-network-agents", "-target=juju_integration.microovn_arm64_microcluster_token_distributor", "-target=juju_integration.microovn_arm64_certs", - "-target=juju_integration.microovn_arm64_ovsdb_cms", ] if _role_distributor_application_name(self.jhelper, self.model): extra_args.append("-target=juju_integration.role-distributor-microovn") @@ -352,7 +341,7 @@ def run(self, context: StepContext) -> Result: network_configs ) - statuses = _microovn_accepted_statuses(self.ovn_manager) + statuses = _microovn_accepted_statuses() try: self.tfhelper.update_tfvars_and_apply_tf( self.client, @@ -537,87 +526,3 @@ def run(self, context: StepContext) -> Result: return Result(ResultType.FAILED, "Unit not found on machine") return Result(ResultType.COMPLETED) - - -class SetOvnProviderStep(BaseStep): - """Set OVN provider in the deployment configuration.""" - - def __init__(self, client: Client, snap: Snap): - super().__init__( - "Set OVN provider", - "Setting OVN provider in deployment configuration", - ) - self.client = client - self.snap = snap - self.wanted_provider: ovn.OvnProvider | None = None - - def get_config_from_snap(self, snap: Snap) -> ovn.OvnProvider: - """Get OVN provider from snap configuration. - - Returns MICROOVN only when the provider config 'ovn.provider' is set - to 'microovn'. - - :param snap: the snap instance - :return: the OVN provider - """ - try: - provider_value = snap.config.get(ovn.SNAP_PROVIDER_CONFIG_KEY) - if provider_value: - # Check if it's a valid OvnProvider value - try: - parsed_provider = ovn.OvnProvider(provider_value) - if parsed_provider == ovn.OvnProvider.MICROOVN: - return ovn.OvnProvider.MICROOVN - except ValueError: - # Invalid provider value - raise error to fail fast - valid_values = ", ".join([p.value for p in ovn.OvnProvider]) - raise ValueError( - f"Invalid value '{provider_value}' for " - f"{ovn.SNAP_PROVIDER_CONFIG_KEY}. " - f"Valid values are: {valid_values}" - ) - except UnknownConfigKey: - # fallback to default - pass - return ovn.DEFAULT_PROVIDER - - def is_skip(self, context: StepContext) -> Result: - """Determines if the step should be skipped or not. - - :return: ResultType.SKIPPED if the Step should be skipped, - ResultType.COMPLETED or ResultType.FAILED otherwise - """ - try: - snap_value = self.get_config_from_snap(self.snap) - except ValueError as e: - return Result( - ResultType.FAILED, - str(e), - ) - - config = ovn.load_provider_config(self.client) - configured_provider = config.provider - if configured_provider == snap_value: - LOG.debug( - "OVN provider is already set to %s in deployment configuration", - snap_value, - ) - return Result(ResultType.SKIPPED) - - already_bootstrapped = self.client.cluster.check_sunbeam_bootstrapped() - if already_bootstrapped and configured_provider != snap_value: - LOG.debug( - "OVN provider change detected after bootstrap, which is not supported" - ) - return Result(ResultType.FAILED, "Changing OVN provider is not supported.") - self.wanted_provider = snap_value - return Result(ResultType.COMPLETED) - - def run(self, context: StepContext) -> Result: - """Set OVN provider in deployment configuration to the desired provider.""" - if self.wanted_provider is None: - return Result(ResultType.FAILED, "Invalid state, wanted_provider is None") - config = ovn.load_provider_config(self.client) - config.provider = self.wanted_provider - ovn.write_provider_config(self.client, config) - return Result(ResultType.COMPLETED) diff --git a/sunbeam-python/sunbeam/steps/openstack.py b/sunbeam-python/sunbeam/steps/openstack.py index c3fbce5f0..59dad008d 100644 --- a/sunbeam-python/sunbeam/steps/openstack.py +++ b/sunbeam-python/sunbeam/steps/openstack.py @@ -14,7 +14,6 @@ import sunbeam.steps.microceph as microceph from sunbeam.clusterd.client import Client from sunbeam.clusterd.service import ConfigItemNotFoundException -from sunbeam.core import ovn from sunbeam.core.common import ( RAM_32_GB_IN_KB, BaseStep, @@ -110,15 +109,6 @@ def remove_blocked_apps_from_features(jhelper: JujuHelper, model: str) -> list[s return apps_to_remove -def remove_blocked_apps_from_ovn_provider( - ovn_manager: "ovn.OvnManager", -) -> list[str]: - """Juju apps that are in blocked state because of the OVN provider.""" - if ovn_manager.get_provider() == ovn.OvnProvider.MICROOVN: - return ["neutron"] - return [] - - def remove_blocked_apps_from_role( external_keystone_model: str | None, is_region_controller: bool, @@ -160,8 +150,6 @@ def remove_blocked_apps_from_role( "nova-api-mysql-router", "nova-cell-mysql", "nova-cell-mysql-router", - "ovn-relay", - "ovn-central", "placement", "placement-mysql", "placement-mysql-router", @@ -683,10 +671,6 @@ def remove_blocked_apps_from_features(self) -> list: """Apps that are in blocked state from features.""" return remove_blocked_apps_from_features(self.jhelper, self.model) - def remove_blocked_apps_from_ovn_provider(self) -> list: - """Juju apps that are in blocked state because of the OVN provider.""" - return remove_blocked_apps_from_ovn_provider(self.ovn_manager) - def remove_blocked_apps_from_role(self) -> list: """Juju apps that are in blocked state because of the node role.""" return remove_blocked_apps_from_role( @@ -846,7 +830,8 @@ def run(self, context: StepContext) -> Result: apps.remove("cinder") apps = list(set(apps) - set(self.remove_blocked_apps_from_features())) apps = list(set(apps) - set(self.remove_blocked_apps_from_role())) - apps = list(set(apps) - set(self.remove_blocked_apps_from_ovn_provider())) + # Neutron remains blocked until the MicroOVN relation is established. + apps = list(set(apps) - {"neutron"}) LOG.debug("Applications monitored for readiness: %s", apps) status_queue: queue.Queue[str] = queue.Queue() @@ -869,24 +854,9 @@ def run(self, context: StepContext) -> Result: class OpenStackPatchLoadBalancerServicesIPStep(PatchLoadBalancerServicesIPStep): - def __init__( - self, - client: Client, - ovn_manager: ovn.OvnManager, - ): - super().__init__(client) - self.ovn_manager = ovn_manager - def services(self): """List of services to patch.""" services = ["traefik", "traefik-public", "rabbitmq"] - if not ( - self.client.cluster.list_nodes_by_role("region_controller") - or self.ovn_manager.get_provider() == ovn.OvnProvider.MICROOVN - ): - # The region controller cluster is not expected to have ovn-relay. - # Microovn based deployments do not use ovn-relay. - services.append("ovn-relay") if self.client.cluster.list_nodes_by_role("storage"): services.append("traefik-rgw") return services @@ -937,7 +907,6 @@ def __init__( self.deployment = deployment self.client = client self.storage_manager = deployment.get_storage_manager() - self.ovn_manager = deployment.get_ovn_manager() self.tfhelper = tfhelper self.jhelper = jhelper self.manifest = manifest @@ -1029,9 +998,8 @@ def run(self, context: StepContext) -> Result: ) ) ) - apps = list( - set(apps) - set(remove_blocked_apps_from_ovn_provider(self.ovn_manager)) - ) + # Neutron remains blocked until the MicroOVN relation is established. + apps = list(set(apps) - {"neutron"}) LOG.debug("Application monitored for readiness: %s", apps) pre_status: dict[str, str] = {} try: diff --git a/sunbeam-python/sunbeam/steps/role_distributor.py b/sunbeam-python/sunbeam/steps/role_distributor.py index 2db41ce04..f1bf7b249 100644 --- a/sunbeam-python/sunbeam/steps/role_distributor.py +++ b/sunbeam-python/sunbeam/steps/role_distributor.py @@ -19,7 +19,6 @@ from sunbeam.core.deployment import Deployment from sunbeam.core.juju import JujuHelper from sunbeam.core.manifest import CharmManifest, Manifest -from sunbeam.core.ovn import OvnProvider from sunbeam.core.role_assignments import ( build_microovn_role_mapping, dump_role_mapping, @@ -67,9 +66,6 @@ def _role_distributor_extra_tfvars( client, model, microovn_machine_ids, - assign_central_roles=( - deployment.get_ovn_manager().get_provider() == OvnProvider.MICROOVN - ), ) config = _manifest_config(manifest) diff --git a/sunbeam-python/sunbeam/steps/upgrades/inter_channel.py b/sunbeam-python/sunbeam/steps/upgrades/inter_channel.py index 7969b22f3..527814d94 100644 --- a/sunbeam-python/sunbeam/steps/upgrades/inter_channel.py +++ b/sunbeam-python/sunbeam/steps/upgrades/inter_channel.py @@ -35,7 +35,6 @@ MISC_CHARMS_K8S, MYSQL_CHARMS_K8S, OPENSTACK_CHARMS_K8S, - OVN_CHARMS_K8S, ) LOG = logging.getLogger(__name__) @@ -188,11 +187,7 @@ def upgrade_tasks(self, context: StepContext) -> Result: # Step 2: Upgrade all openstack core charms LOG.debug("Upgrading OpenStack core charms") - charms = ( - list(MISC_CHARMS_K8S.keys()) - + list(OVN_CHARMS_K8S.keys()) - + list(OPENSTACK_CHARMS_K8S.keys()) - ) + charms = list(MISC_CHARMS_K8S.keys()) + list(OPENSTACK_CHARMS_K8S.keys()) apps = self.get_apps_filter_by_charms(self.model, charms) result = self.upgrade_applications( apps, diff --git a/sunbeam-python/sunbeam/steps/upgrades/intra_channel.py b/sunbeam-python/sunbeam/steps/upgrades/intra_channel.py index d831fb29f..6fd341ef1 100644 --- a/sunbeam-python/sunbeam/steps/upgrades/intra_channel.py +++ b/sunbeam-python/sunbeam/steps/upgrades/intra_channel.py @@ -523,9 +523,7 @@ def get_plan(self) -> list[BaseStep]: ) ovn_manager = self.deployment.get_ovn_manager() - plan.extend( - [OpenStackPatchLoadBalancerServicesIPStep(self.client, ovn_manager)] - ) + plan.extend([OpenStackPatchLoadBalancerServicesIPStep(self.client)]) network_nodes = [] microovn_roles = ovn_manager.get_roles_for_microovn() diff --git a/sunbeam-python/sunbeam/versions.py b/sunbeam-python/sunbeam/versions.py index 66643fe07..32b55e4d7 100644 --- a/sunbeam-python/sunbeam/versions.py +++ b/sunbeam-python/sunbeam/versions.py @@ -23,7 +23,6 @@ def determine_version() -> str: JUJU_CHANNEL = "3.6/stable" JUJU_BASE = "ubuntu@24.04" OPENSTACK_CHANNEL = f"{determine_version()}/stable" -OVN_CHANNEL = "26.03/stable" RABBITMQ_CHANNEL = "3.12/stable" TRAEFIK_CHANNEL = "latest/stable" MICROCEPH_CHANNEL = "squid/stable" @@ -59,10 +58,6 @@ def determine_version() -> str: "nova-k8s": OPENSTACK_CHANNEL, "placement-k8s": OPENSTACK_CHANNEL, } -OVN_CHARMS_K8S = { - "ovn-central-k8s": OVN_CHANNEL, - "ovn-relay-k8s": OVN_CHANNEL, -} MYSQL_CHARMS_K8S = { "mysql-k8s": MYSQL_CHANNEL, "mysql-router-k8s": MYSQL_CHANNEL, @@ -92,7 +87,6 @@ def determine_version() -> str: K8S_CHARMS: dict[str, str] = {} K8S_CHARMS |= OPENSTACK_CHARMS_K8S -K8S_CHARMS |= OVN_CHARMS_K8S K8S_CHARMS |= MYSQL_CHARMS_K8S K8S_CHARMS |= MISC_CHARMS_K8S diff --git a/sunbeam-python/tests/unit/sunbeam/core/test_role_assignments.py b/sunbeam-python/tests/unit/sunbeam/core/test_role_assignments.py index f22de3a6b..7374269df 100644 --- a/sunbeam-python/tests/unit/sunbeam/core/test_role_assignments.py +++ b/sunbeam-python/tests/unit/sunbeam/core/test_role_assignments.py @@ -22,7 +22,6 @@ def test_microovn_role_mapping_keeps_gateways_on_network_nodes(): client, model_name="openstack-machines", machine_ids=["0", "1", "2"], - assign_central_roles=True, ) assert mapping["openstack-machines"]["microovn"]["machines"] == { @@ -45,7 +44,6 @@ def test_microovn_role_mapping_filters_to_actual_microovn_machines(): client, model_name="openstack-machines", machine_ids=["2"], - assign_central_roles=True, ) assert mapping["openstack-machines"]["microovn"]["machines"] == { @@ -53,7 +51,7 @@ def test_microovn_role_mapping_filters_to_actual_microovn_machines(): } -def test_microovn_role_mapping_can_disable_central_roles(): +def test_microovn_role_mapping_allows_central_and_gateway_on_same_node(): client = _client_with_nodes( { "control": [{"machineid": "0", "role": ["control", "network"]}], @@ -66,28 +64,6 @@ def test_microovn_role_mapping_can_disable_central_roles(): client, model_name="openstack-machines", machine_ids=["0"], - assign_central_roles=False, - ) - - assert mapping["openstack-machines"]["microovn"]["machines"] == { - "0": {"roles": ["chassis", "gateway"]}, - } - - -def test_microovn_role_mapping_allows_central_and_gateway_for_provider_microovn(): - client = _client_with_nodes( - { - "control": [{"machineid": "0", "role": ["control", "network"]}], - "compute": [], - "network": [{"machineid": "0", "role": ["control", "network"]}], - } - ) - - mapping = build_microovn_role_mapping( - client, - model_name="openstack-machines", - machine_ids=["0"], - assign_central_roles=True, ) assert mapping["openstack-machines"]["microovn"]["machines"] == { diff --git a/sunbeam-python/tests/unit/sunbeam/provider/local/test_steps.py b/sunbeam-python/tests/unit/sunbeam/provider/local/test_steps.py index e14f52318..815ed7a55 100644 --- a/sunbeam-python/tests/unit/sunbeam/provider/local/test_steps.py +++ b/sunbeam-python/tests/unit/sunbeam/provider/local/test_steps.py @@ -12,10 +12,7 @@ from sunbeam.core.common import ResultType from sunbeam.provider.common import nic_utils -from ...steps.test_configure import ( - BaseTestSetHypervisorUnitsOptionsStep, - BaseTestUserQuestions, -) +from ...steps.test_configure import BaseTestUserQuestions @pytest.fixture() @@ -101,252 +98,23 @@ def test_prompt_local_no_demo_setup(self): self.check_not_remote_questions(net_bank_mock) -class TestLocalSetHypervisorUnitsOptionsStep(BaseTestSetHypervisorUnitsOptionsStep): - __test__ = True - - @pytest.fixture(autouse=True) - def setup_local(self, fetch_nics): - self.fetch_nics = fetch_nics - - @pytest.fixture - def physical_network_question(self): - with patch("sunbeam.provider.local.steps.physical_network_question") as p: - yield p - - def get_step(self, join_mode=False): - return local_steps.LocalSetHypervisorUnitsOptionsStep( - self.cclient, "maas0.local", self.jhelper, "test-model", join_mode=join_mode - ) - - def mock_physnet_qs(self, physical_network_question): - physnet_name_mock = Mock() - physnet_name_mock.ask.return_value = "physnet1" - configure_more_mock = Mock() - configure_more_mock.ask.return_value = False - - physnet_qs = { - "physnet_name": physnet_name_mock, - "configure_more": configure_more_mock, - } - physical_network_question.return_value = physnet_qs - - def test_prompt_remote(self, fetch_nics, physical_network_question): - self.load_answers.return_value = {"user": {"remote_access_location": "remote"}} - # Mock no network nodes in cluster - self.cclient.cluster.list_nodes_by_role.return_value = [] - local_hypervisor_bank_mock = Mock() - self.question_bank.return_value = local_hypervisor_bank_mock - local_hypervisor_bank_mock.nics.ask.return_value = "eth2" - local_hypervisor_bank_mock.nics.question = "Select interface" - - self.mock_physnet_qs(physical_network_question) - - step = self.get_step() - nics_result = { - "nics": [ - {"name": "eth2", "up": True, "connected": True, "configured": False} - ], - "candidates": ["eth2"], - } - fetch_nics.return_value = nics_result - step.prompt() - assert step.bridge_mappings["maas0.local"] == "br-physnet1:physnet1:eth2" - - def test_prompt_remote_join(self, fetch_nics, physical_network_question): - self.load_answers.return_value = {"user": {"remote_access_location": "remote"}} - # Mock no network nodes in cluster - self.cclient.cluster.list_nodes_by_role.return_value = [] - local_hypervisor_bank_mock = Mock() - self.question_bank.return_value = local_hypervisor_bank_mock - local_hypervisor_bank_mock.nics.ask.return_value = "eth2" - local_hypervisor_bank_mock.nics.question = "Select interface" - - self.mock_physnet_qs(physical_network_question) - - step = self.get_step(join_mode=True) - nics_result = { - "nics": [ - {"name": "eth2", "up": True, "connected": True, "configured": False} - ], - "candidates": ["eth2"], - } - fetch_nics.return_value = nics_result - step.prompt() - assert step.bridge_mappings["maas0.local"] == "br-physnet1:physnet1:eth2" - - def mock_candidates(self, candidates: list[str]): - # Construct the return value expected by nic_utils.fetch_nics - nics_result = { - "nics": [ - {"name": c, "up": True, "connected": True, "configured": False} - for c in candidates - ], - "candidates": candidates, - } - self.fetch_nics.return_value = nics_result - - def test_prompt(self, physical_network_question): - self.load_answers.return_value = {"user": {"remote_access_location": "remote"}} - # Mock no network nodes in cluster - self.cclient.cluster.list_nodes_by_role.return_value = [] - - hypervisor_bank_mock = Mock() - self.question_bank.return_value = hypervisor_bank_mock - hypervisor_bank_mock.nics.ask.return_value = "eth2" - hypervisor_bank_mock.nics.question = "Select interface" - - self.mock_physnet_qs(physical_network_question) - - step = self.get_step() - - self.mock_candidates(["eth2"]) - - step.prompt() - - machine_name = self.get_machine_name() - assert step.bridge_mappings[machine_name] == "br-physnet1:physnet1:eth2" - - def test_prompt_local(self): - """Test specific to Local provider: local access mode.""" - self.load_answers.return_value = {"user": {"remote_access_location": "local"}} - local_hypervisor_bank_mock = Mock() - self.question_bank.return_value = local_hypervisor_bank_mock - local_hypervisor_bank_mock.nics.ask.return_value = "eth12" - local_hypervisor_bank_mock.nics.question = "Select interface" - step = self.get_step() - step.prompt() - assert len(step.bridge_mappings) == 0 - - def test_prompt_local_join(self, physical_network_question): - """Test specific to Local provider: local access mode with join.""" - self.load_answers.return_value = {"user": {"remote_access_location": "local"}} - # Mock no network nodes in cluster - self.cclient.cluster.list_nodes_by_role.return_value = [] - local_hypervisor_bank_mock = Mock() - self.question_bank.return_value = local_hypervisor_bank_mock - local_hypervisor_bank_mock.nics.ask.return_value = "eth2" - local_hypervisor_bank_mock.nics.question = "Select interface" - - self.mock_physnet_qs(physical_network_question) - - step = self.get_step(join_mode=True) - self.mock_candidates(["eth2"]) - - step.prompt() - assert step.bridge_mappings["maas0.local"] == "br-physnet1:physnet1:eth2" - - def test_prompt_join_mode(self, physical_network_question): - self.load_answers.return_value = {"user": {"remote_access_location": "remote"}} - # Mock no network nodes in cluster - self.cclient.cluster.list_nodes_by_role.return_value = [] - - hypervisor_bank_mock = Mock() - self.question_bank.return_value = hypervisor_bank_mock - hypervisor_bank_mock.nics.ask.return_value = "eth2" - hypervisor_bank_mock.nics.question = "Select interface" - - self.mock_physnet_qs(physical_network_question) - - step = self.get_step(join_mode=True) - - self.mock_candidates(["eth2"]) - - step.prompt() - - machine_name = self.get_machine_name() - assert step.bridge_mappings[machine_name] == "br-physnet1:physnet1:eth2" - - def test_prompt_with_deprecated_nic_field_in_manifest(self): - """Test that deprecated 'nic' field in manifest is used without prompting.""" - from sunbeam.core.manifest import Manifest - - # Load answers with remote access to trigger manifest reading - self.load_answers.return_value = {"user": {"remote_access_location": "remote"}} - - # Create a manifest with deprecated nic field - manifest_dict = { - "core": { - "config": { - "external-network": { - "nic": "eth1", # Deprecated field - "cidr": "10.0.0.0/24", - } - } - } - } - manifest = Manifest.model_validate(manifest_dict) - - step = local_steps.LocalSetHypervisorUnitsOptionsStep( - self.cclient, - "maas0.local", - self.jhelper, - "test-model", - join_mode=False, - manifest=manifest, - ) - - # Call prompt - it should NOT prompt user since manifest has nic - step.prompt() - - # Verify bridge_mappings is set from manifest - machine_name = self.get_machine_name() - assert step.bridge_mappings[machine_name] == "br-physnet1:physnet1:eth1" - - def test_prompt_with_new_nics_field_in_manifest(self): - """Test that new 'nics' field in manifest is used without prompting.""" - from sunbeam.core.manifest import Manifest - - # Load answers with remote access to trigger manifest reading - self.load_answers.return_value = {"user": {"remote_access_location": "remote"}} - - # Create a manifest with new nics field - manifest_dict = { - "core": { - "config": { - "external-networks": { - "physnet1": { - "nics": {"maas0.local": "ens1f0"}, - "cidr": "10.0.0.0/24", - } - } - } - } - } - manifest = Manifest.model_validate(manifest_dict) - - step = local_steps.LocalSetHypervisorUnitsOptionsStep( - self.cclient, - "maas0.local", - self.jhelper, - "test-model", - join_mode=False, - manifest=manifest, - ) +def test_network_agents_prompt_skips_compute_only_host(cclient, jhelper, load_answers): + load_answers.return_value = {"user": {"remote_access_location": "remote"}} + cclient.cluster.get_node_info.return_value = { + "role": ["compute"], + } + step = local_steps.LocalSetOpenStackNetworkAgentsStep( + cclient, + "maas0.local", + jhelper, + "test-model", + ) - # Call prompt - it should NOT prompt user since manifest has nics + with patch.object(nic_utils, "fetch_nics_from_subordinate") as fetch_nics: step.prompt() - # Verify bridge_mappings is set from manifest - machine_name = self.get_machine_name() - assert step.bridge_mappings[machine_name] == "br-physnet1:physnet1:ens1f0" - - def test_network_agents_prompt_skips_compute_only_host(self): - self.load_answers.return_value = {"user": {"remote_access_location": "remote"}} - self.cclient.cluster.get_node_info.return_value = { - "role": ["compute"], - } - step = local_steps.LocalSetOpenStackNetworkAgentsStep( - self.cclient, - "maas0.local", - self.jhelper, - "test-model", - ) - - with patch.object(nic_utils, "fetch_nics_from_subordinate") as fetch_nics: - step.prompt() - - assert step.bridge_mappings["maas0.local"] is None - fetch_nics.assert_not_called() + assert step.bridge_mappings["maas0.local"] is None + fetch_nics.assert_not_called() class TestLocalClusterStatusStep: diff --git a/sunbeam-python/tests/unit/sunbeam/provider/maas/test_maas.py b/sunbeam-python/tests/unit/sunbeam/provider/maas/test_maas.py index e490f2611..fbdcc42b8 100644 --- a/sunbeam-python/tests/unit/sunbeam/provider/maas/test_maas.py +++ b/sunbeam-python/tests/unit/sunbeam/provider/maas/test_maas.py @@ -13,7 +13,6 @@ from maas.client.bones import CallError import sunbeam.provider.maas.steps as maas_steps -from sunbeam.core import ovn from sunbeam.core.checks import DiagnosticResultType from sunbeam.core.deployment import Networks from sunbeam.core.deployments import DeploymentsConfig @@ -66,22 +65,10 @@ class TestMaasConfigureCommand: - @pytest.mark.parametrize( - "provider,expected_names", - [ - (ovn.OvnProvider.OVN_K8S, ["net-1"]), - ( - ovn.OvnProvider.MICROOVN, - ["net-1", "compute-1", "control-1"], - ), - ], - ) - def test_network_agents_match_ovn_provider( + def test_network_agents_include_all_microovn_nodes( self, mocker, tmp_path, - provider, - expected_names, ): client = Mock() nodes_by_role = { @@ -95,14 +82,10 @@ def test_network_agents_match_ovn_provider( tfhelper.env = {} tfhelper.path = tmp_path - ovn_manager = Mock() - ovn_manager.get_provider.return_value = provider - deployment = Mock() deployment.get_client.return_value = client deployment.get_manifest.return_value = Mock(core={}, features={}) deployment.get_tfhelper.return_value = tfhelper - deployment.get_ovn_manager.return_value = ovn_manager deployment.juju_controller = "controller" deployment.juju_account = "account" deployment.openstack_machines_model = "openstack-machines" @@ -138,7 +121,7 @@ def test_network_agents_match_ovn_provider( for step in plan if isinstance(step, maas_steps.MaasSetOpenStackNetworkAgentsStep) ) - assert network_agents_step.names == expected_names + assert network_agents_step.names == ["net-1", "compute-1", "control-1"] class TestAddMaasDeployment: diff --git a/sunbeam-python/tests/unit/sunbeam/provider/maas/test_steps.py b/sunbeam-python/tests/unit/sunbeam/provider/maas/test_steps.py index b87084665..6cb9a980c 100644 --- a/sunbeam-python/tests/unit/sunbeam/provider/maas/test_steps.py +++ b/sunbeam-python/tests/unit/sunbeam/provider/maas/test_steps.py @@ -8,10 +8,7 @@ import sunbeam.core.questions import sunbeam.provider.maas.steps as maas_steps -from ...steps.test_configure import ( - BaseTestSetHypervisorUnitsOptionsStep, - BaseTestUserQuestions, -) +from ...steps.test_configure import BaseTestUserQuestions @pytest.fixture() @@ -44,27 +41,3 @@ def setup_maas(self): def get_step(self): return maas_steps.MaasUserQuestions(self.cclient, self.maas_client) - - -class TestMaasSetHypervisorUnitsOptionsStep(BaseTestSetHypervisorUnitsOptionsStep): - __test__ = True - - @pytest.fixture(autouse=True) - def setup_maas(self): - self.maas_client = Mock() - with patch("sunbeam.provider.maas.steps.maas_deployment") as p: - self.maas_deployment = p - yield - - def test_has_prompts(self): - step = self.get_step() - assert not step.has_prompts() - - def get_step(self, join_mode=False): - return maas_steps.MaasSetHypervisorUnitsOptionsStep( - self.cclient, - self.maas_client, - ["machine1.maas", "machine2.maas"], - self.jhelper, - "test-model", - ) diff --git a/sunbeam-python/tests/unit/sunbeam/steps/test_configure.py b/sunbeam-python/tests/unit/sunbeam/steps/test_configure.py index 5ebd3bd45..f7a1c1eae 100644 --- a/sunbeam-python/tests/unit/sunbeam/steps/test_configure.py +++ b/sunbeam-python/tests/unit/sunbeam/steps/test_configure.py @@ -125,74 +125,6 @@ def test_prompt_nameservers_comma_separated(self, nameserver_input, expected): assert step.variables["user"]["dns_nameservers"] == expected -class BaseTestSetHypervisorUnitsOptionsStep: - __test__ = False - - @pytest.fixture(autouse=True) - def setup(self, cclient, jhelper, load_answers, question_bank): - self.cclient = cclient - self.jhelper = jhelper - self.load_answers = load_answers - self.question_bank = question_bank - - def get_step(self, join_mode=False): - raise NotImplementedError - - def get_machine_name(self): - """Return the expected machine name key in bridge_mappings.""" - return "maas0.local" - - def mock_candidates(self, candidates: list[str]): - """Mock the backend to return these candidate NICs.""" - raise NotImplementedError - - def test_has_prompts(self): - step = self.get_step() - assert step.has_prompts() - - def test_run_compute_only_does_not_send_gateway_flag(self, step_context): - name = self.get_machine_name() - self.cclient.cluster.get_node_info.return_value = { - "machineid": 1, - "role": ["compute"], - } - step = self.get_step() - step.names = [name] - step.get_unit = Mock(return_value="openstack-hypervisor/0") - step.bridge_mappings = {name: "br-physnet1:physnet1:eth2"} - - result = step.run(step_context) - - assert result.result_type == ResultType.COMPLETED - self.jhelper.run_action.assert_called_once_with( - "openstack-hypervisor/0", - "test-model", - "set-hypervisor-local-settings", - action_params={}, - ) - - def test_run_network_node_sends_only_bridge_mapping(self, step_context): - name = self.get_machine_name() - self.cclient.cluster.get_node_info.return_value = { - "machineid": 1, - "role": ["network"], - } - step = self.get_step() - step.names = [name] - step.get_unit = Mock(return_value="openstack-hypervisor/0") - step.bridge_mappings = {name: "br-physnet1:physnet1:eth2"} - - result = step.run(step_context) - - assert result.result_type == ResultType.COMPLETED - self.jhelper.run_action.assert_called_once_with( - "openstack-hypervisor/0", - "test-model", - "set-hypervisor-local-settings", - action_params={"bridge-mapping": "br-physnet1:physnet1:eth2"}, - ) - - class _TestableStep(SetExternalNetworkUnitsOptionsStep): APP = "openstack-network-agents" DISPLAY_NAME = "OpenStack Network Agents" diff --git a/sunbeam-python/tests/unit/sunbeam/steps/test_microovn.py b/sunbeam-python/tests/unit/sunbeam/steps/test_microovn.py index 9d4e171ea..4c24b6f03 100644 --- a/sunbeam-python/tests/unit/sunbeam/steps/test_microovn.py +++ b/sunbeam-python/tests/unit/sunbeam/steps/test_microovn.py @@ -14,7 +14,6 @@ EnableMicroOVNStep, ReapplyMicroOVNOptionalIntegrationsStep, ReapplyMicroOVNTerraformPlanStep, - SetOvnProviderStep, ) @@ -60,20 +59,7 @@ def test_get_application_timeout(self, deploy_microovn_step): timeout = deploy_microovn_step.get_application_timeout() assert timeout == 1200 - def test_get_accepted_application_status_allows_blocked_for_ovn_k8s( - self, deploy_microovn_step, ovn_manager - ): - ovn_manager.get_provider.return_value = ovn.OvnProvider.OVN_K8S - - statuses = deploy_microovn_step.get_accepted_application_status() - - assert statuses == ["active", "unknown", "blocked"] - - def test_get_accepted_application_status_excludes_blocked_for_microovn_provider( - self, deploy_microovn_step, ovn_manager - ): - ovn_manager.get_provider.return_value = ovn.OvnProvider.MICROOVN - + def test_get_accepted_application_status(self, deploy_microovn_step): statuses = deploy_microovn_step.get_accepted_application_status() assert statuses == ["active", "unknown"] @@ -292,11 +278,9 @@ def test_tf_apply_extra_args(self, reapply_microovn_step): expected_args = [ "-target=juju_integration.microovn-microcluster-token-distributor", "-target=juju_integration.microovn-certs", - "-target=juju_integration.microovn-ovsdb-cms", "-target=juju_integration.microovn-openstack-network-agents", "-target=juju_integration.microovn_arm64_microcluster_token_distributor", "-target=juju_integration.microovn_arm64_certs", - "-target=juju_integration.microovn_arm64_ovsdb_cms", "-target=juju_integration.role-distributor-microovn", "-target=juju_integration.role-distributor-microovn-arm64", ] @@ -315,11 +299,9 @@ def test_tf_apply_extra_args_omits_role_distributor_when_missing( assert extra_args == [ "-target=juju_integration.microovn-microcluster-token-distributor", "-target=juju_integration.microovn-certs", - "-target=juju_integration.microovn-ovsdb-cms", "-target=juju_integration.microovn-openstack-network-agents", "-target=juju_integration.microovn_arm64_microcluster_token_distributor", "-target=juju_integration.microovn_arm64_certs", - "-target=juju_integration.microovn_arm64_ovsdb_cms", ] @@ -530,39 +512,12 @@ def test_run_success( == "10.0.0.1/24" ) - def test_run_allows_blocked_status_for_ovn_k8s( - self, - reapply_microovn_terraform_step, - basic_jhelper, - step_context, - ): - reapply_microovn_terraform_step.ovn_manager.get_provider.return_value = ( - ovn.OvnProvider.OVN_K8S - ) - - with patch( - "sunbeam.steps.microovn.get_external_network_configs", return_value={} - ): - result = reapply_microovn_terraform_step.run(step_context) - - assert result.result_type == ResultType.COMPLETED - basic_jhelper.wait_application_ready.assert_called_once_with( - "microovn", - "test-model", - accepted_status=["active", "unknown", "blocked"], - timeout=1200, - ) - - def test_run_excludes_blocked_status_for_microovn_provider( + def test_run_uses_microovn_accepted_statuses( self, reapply_microovn_terraform_step, basic_jhelper, step_context, ): - reapply_microovn_terraform_step.ovn_manager.get_provider.return_value = ( - ovn.OvnProvider.MICROOVN - ) - with patch( "sunbeam.steps.microovn.get_external_network_configs", return_value={} ): @@ -575,111 +530,3 @@ def test_run_excludes_blocked_status_for_microovn_provider( accepted_status=["active", "unknown"], timeout=1200, ) - - -class TestSetOvnProviderStep: - def test_get_config_from_snap_provider_not_set(self, basic_client): - """Test get_config_from_snap when no provider is configured.""" - mock_snap = Mock() - mock_snap.config.get.return_value = None - step = SetOvnProviderStep(basic_client, mock_snap) - - result = step.get_config_from_snap(mock_snap) - - assert result == ovn.DEFAULT_PROVIDER - mock_snap.config.get.assert_called_once_with(ovn.SNAP_PROVIDER_CONFIG_KEY) - - def test_get_config_from_snap_provider_microovn(self, basic_client): - """Test get_config_from_snap when provider is set to microovn.""" - mock_snap = Mock() - mock_snap.config.get.return_value = ovn.OvnProvider.MICROOVN - step = SetOvnProviderStep(basic_client, mock_snap) - - result = step.get_config_from_snap(mock_snap) - - assert result == ovn.OvnProvider.MICROOVN - mock_snap.config.get.assert_called_once_with(ovn.SNAP_PROVIDER_CONFIG_KEY) - - def test_get_config_from_snap_unknown_config_key(self, basic_client): - """Test get_config_from_snap when snap config key doesn't exist.""" - from snaphelpers import UnknownConfigKey - - mock_snap = Mock() - mock_snap.config.get.side_effect = UnknownConfigKey("ovn.provider") - step = SetOvnProviderStep(basic_client, mock_snap) - - result = step.get_config_from_snap(mock_snap) - - assert result == ovn.DEFAULT_PROVIDER - - def test_get_config_from_snap_invalid_provider_value(self, basic_client): - """Test get_config_from_snap with invalid provider value raises error.""" - mock_snap = Mock() - mock_snap.config.get.return_value = "invalid-provider" - step = SetOvnProviderStep(basic_client, mock_snap) - - with pytest.raises(ValueError) as exc_info: - step.get_config_from_snap(mock_snap) - assert "Invalid value 'invalid-provider'" in str(exc_info.value) - assert "ovn.provider" in str(exc_info.value) - assert "Valid values are:" in str(exc_info.value) - - def test_is_skip(self, basic_client, step_context): - """Test is_skip method.""" - step = SetOvnProviderStep(basic_client, Mock()) - with patch.object(step, "get_config_from_snap") as mock_get_config: - mock_get_config.return_value = ovn.OvnProvider.OVN_K8S - with patch("sunbeam.core.ovn.load_provider_config") as mock_load: - mock_load.return_value = ovn.OvnConfig(provider=ovn.OvnProvider.OVN_K8S) - result = step.is_skip(step_context) - assert result.result_type == ResultType.SKIPPED - - def test_is_skip_not_bootstrapped(self, basic_client, step_context): - step = SetOvnProviderStep(basic_client, Mock()) - with patch.object(step, "get_config_from_snap") as mock_get_config: - mock_get_config.return_value = ovn.OvnProvider.MICROOVN - with patch("sunbeam.core.ovn.load_provider_config") as mock_load: - mock_load.return_value = ovn.OvnConfig(provider=ovn.OvnProvider.OVN_K8S) - with patch.object( - basic_client.cluster, "check_sunbeam_bootstrapped" - ) as mock_bootstrapped: - mock_bootstrapped.return_value = False - result = step.is_skip(step_context) - assert result.result_type == ResultType.COMPLETED - - def test_is_skip_bootstrapped_change_provider(self, basic_client, step_context): - step = SetOvnProviderStep(basic_client, Mock()) - with patch.object(step, "get_config_from_snap") as mock_get_config: - mock_get_config.return_value = ovn.OvnProvider.MICROOVN - with patch("sunbeam.core.ovn.load_provider_config") as mock_load: - mock_load.return_value = ovn.OvnConfig(provider=ovn.OvnProvider.OVN_K8S) - with patch.object( - basic_client.cluster, "check_sunbeam_bootstrapped" - ) as mock_bootstrapped: - mock_bootstrapped.return_value = True - result = step.is_skip(step_context) - assert result.result_type == ResultType.FAILED - - def test_is_skip_invalid_provider(self, basic_client, step_context): - """Test is_skip returns FAILED when invalid provider is configured.""" - step = SetOvnProviderStep(basic_client, Mock()) - with patch.object(step, "get_config_from_snap") as mock_get_config: - mock_get_config.side_effect = ValueError( - "Invalid value 'bad-provider' for ovn.provider. Valid values are: ovn-k8s, microovn" - ) - result = step.is_skip(step_context) - assert result.result_type == ResultType.FAILED - assert "Invalid value 'bad-provider'" in result.message - - def test_run(self, basic_client, step_context): - """Test run method.""" - step = SetOvnProviderStep(basic_client, Mock()) - step.wanted_provider = ovn.OvnProvider.OVN_K8S - with patch("sunbeam.core.ovn.load_provider_config") as mock_load: - mock_config = ovn.OvnConfig(provider=None) - mock_load.return_value = mock_config - with patch("sunbeam.core.ovn.write_provider_config") as mock_write: - result = step.run(step_context) - assert result.result_type == ResultType.COMPLETED - mock_write.assert_called_once_with(basic_client, mock_config) - assert mock_config.provider == ovn.OvnProvider.OVN_K8S diff --git a/sunbeam-python/tests/unit/sunbeam/steps/test_openstack.py b/sunbeam-python/tests/unit/sunbeam/steps/test_openstack.py index f4789df7d..ad25254fa 100644 --- a/sunbeam-python/tests/unit/sunbeam/steps/test_openstack.py +++ b/sunbeam-python/tests/unit/sunbeam/steps/test_openstack.py @@ -8,7 +8,6 @@ import tenacity from sunbeam.clusterd.service import ConfigItemNotFoundException -from sunbeam.core import ovn from sunbeam.core.common import ResultType from sunbeam.core.juju import ( ApplicationNotFoundException, @@ -46,7 +45,6 @@ get_database_storage_dict, get_rabbitmq_storage_tfvars, remove_blocked_apps_from_features, - remove_blocked_apps_from_ovn_provider, remove_blocked_apps_from_role, ) @@ -364,14 +362,6 @@ def test_is_skip_fails_on_storage_modification( assert "rabbitmq-storage" in result.message -@pytest.fixture -def ovn_manager(): - """Ovn manager mock.""" - ovn_manager = Mock() - ovn_manager.get_provider.return_value = ovn.OvnProvider.OVN_K8S - yield ovn_manager - - class PatchLoadBalancerServicesIPStepTest: @pytest.fixture def patch_client(self): @@ -386,7 +376,6 @@ def test_is_skip( read_config_patch, snap_patch, snap_mock, - ovn_manager, step_context, ): snap_mock().config.get.return_value = "k8s" @@ -404,7 +393,7 @@ def test_is_skip( ) ), ): - step = OpenStackPatchLoadBalancerServicesIPStep(patch_client, ovn_manager) + step = OpenStackPatchLoadBalancerServicesIPStep(patch_client) result = step.is_skip(step_context) assert result.result_type == ResultType.SKIPPED @@ -414,7 +403,6 @@ def test_is_skip_missing_annotation( read_config_patch, snap_patch, snap_mock, - ovn_manager, step_context, ): snap_mock().config.get.return_value = "k8s" @@ -426,7 +414,7 @@ def test_is_skip_missing_annotation( ) ), ): - step = OpenStackPatchLoadBalancerServicesIPStep(patch_client, ovn_manager) + step = OpenStackPatchLoadBalancerServicesIPStep(patch_client) result = step.is_skip(step_context) assert result.result_type == ResultType.COMPLETED @@ -435,7 +423,6 @@ def test_is_skip_missing_config( patch_client, snap_patch, snap_mock, - ovn_manager, step_context, ): snap_mock().config.get.return_value = "k8s" @@ -443,7 +430,7 @@ def test_is_skip_missing_config( "sunbeam.core.steps.read_config", new=Mock(side_effect=ConfigItemNotFoundException), ): - step = OpenStackPatchLoadBalancerServicesIPStep(patch_client, ovn_manager) + step = OpenStackPatchLoadBalancerServicesIPStep(patch_client) result = step.is_skip(step_context) assert result.result_type == ResultType.FAILED @@ -453,7 +440,6 @@ def test_run( read_config_patch, snap_patch, snap_mock, - ovn_manager, step_context, ): snap_mock().config.get.return_value = "k8s" @@ -472,7 +458,7 @@ def test_run( ) ), ): - step = OpenStackPatchLoadBalancerServicesIPStep(patch_client, ovn_manager) + step = OpenStackPatchLoadBalancerServicesIPStep(patch_client) step.is_skip(step_context) result = step.run(step_context) assert result.result_type == ResultType.COMPLETED @@ -490,7 +476,7 @@ class TestPatchLoadBalancerServicesIPStaleAnnotation: @pytest.fixture def patch_client(self): - """Client mock; returns node-1 for any role so ovn-relay is excluded.""" + """Client mock returning node-1 for any role.""" client = Mock() client.cluster.list_nodes_by_role.return_value = ["node-1"] return client @@ -520,7 +506,6 @@ def test_is_skip_stale_annotation_pending_returns_completed( read_config_patch, snap_patch, snap_mock, - ovn_manager, step_context, ): """is_skip should return COMPLETED when a service has a stale IP annotation. @@ -545,7 +530,7 @@ def test_is_skip_stale_annotation_pending_returns_completed( "sunbeam.core.steps.l_client.Client", new=Mock(return_value=Mock(get=get_mock)), ): - step = OpenStackPatchLoadBalancerServicesIPStep(patch_client, ovn_manager) + step = OpenStackPatchLoadBalancerServicesIPStep(patch_client) result = step.is_skip(step_context) assert result.result_type == ResultType.COMPLETED @@ -556,7 +541,6 @@ def test_run_removes_stale_ip_annotation( read_config_patch, snap_patch, snap_mock, - ovn_manager, step_context, ): """run() should remove a stale IP annotation from a pending service. @@ -588,7 +572,7 @@ def test_run_removes_stale_ip_annotation( "sunbeam.core.steps.l_client.Client", new=Mock(return_value=Mock(get=get_mock)), ): - step = OpenStackPatchLoadBalancerServicesIPStep(patch_client, ovn_manager) + step = OpenStackPatchLoadBalancerServicesIPStep(patch_client) step.is_skip(step_context) result = step.run(step_context) @@ -1167,25 +1151,6 @@ def _get_app(name, model): assert result == ["barbican"] -# --------------------------------------------------------------------------- -# remove_blocked_apps_from_ovn_provider -# --------------------------------------------------------------------------- - - -def test_remove_blocked_apps_from_ovn_provider_microovn(): - ovn_manager = Mock() - ovn_manager.get_provider.return_value = ovn.OvnProvider.MICROOVN - result = remove_blocked_apps_from_ovn_provider(ovn_manager) - assert result == ["neutron"] - - -def test_remove_blocked_apps_from_ovn_provider_non_microovn(): - ovn_manager = Mock() - ovn_manager.get_provider.return_value = ovn.OvnProvider.OVN_K8S - result = remove_blocked_apps_from_ovn_provider(ovn_manager) - assert result == [] - - # --------------------------------------------------------------------------- # remove_blocked_apps_from_role # --------------------------------------------------------------------------- diff --git a/sunbeam-python/tests/unit/sunbeam/steps/test_role_distributor.py b/sunbeam-python/tests/unit/sunbeam/steps/test_role_distributor.py index 5f302fc9a..121af0983 100644 --- a/sunbeam-python/tests/unit/sunbeam/steps/test_role_distributor.py +++ b/sunbeam-python/tests/unit/sunbeam/steps/test_role_distributor.py @@ -5,7 +5,6 @@ import yaml -from sunbeam.core import ovn from sunbeam.core.common import ResultType from sunbeam.steps.role_distributor import ( DeployRoleDistributorApplicationStep, @@ -63,43 +62,6 @@ def test_extra_tfvars_emits_role_mapping_config( } assert extra_tfvars["role_distributor_machine_ids"] == ["0"] - def test_extra_tfvars_does_not_assign_central_for_ovn_k8s( - self, - basic_deployment, - basic_client, - basic_tfhelper, - basic_jhelper, - basic_manifest, - test_model, - ): - basic_deployment.get_ovn_manager.return_value.get_machines.return_value = ["0"] - basic_deployment.get_ovn_manager.return_value.get_provider.return_value = ( - ovn.OvnProvider.OVN_K8S - ) - basic_client.cluster.list_nodes_by_role.side_effect = lambda role: { - "control": [{"machineid": "0", "role": ["control", "network"]}], - "compute": [], - "network": [{"machineid": "0", "role": ["control", "network"]}], - }.get(role, []) - - step = DeployRoleDistributorApplicationStep( - basic_deployment, - basic_client, - basic_tfhelper, - basic_jhelper, - basic_manifest, - "openstack-machines", - ) - - extra_tfvars = step.extra_tfvars() - - role_mapping = yaml.safe_load( - extra_tfvars["charm_role_distributor_config"]["role-mapping"] - ) - assert role_mapping["openstack-machines"]["microovn"]["machines"] == { - "0": {"roles": ["chassis", "gateway"]}, - } - def test_get_accepted_application_status_allows_waiting( self, basic_deployment,