From eaa81733b029dda707ff08d729315985a2e57d18 Mon Sep 17 00:00:00 2001 From: Cosmin Tupangiu Date: Tue, 3 Sep 2024 11:07:17 +0200 Subject: [PATCH] add mtv feature support --- .../featuresupport/feature_support_level.go | 1 + .../featuresupport/feature_support_test.go | 6 +-- .../featuresupport/features_olm_operators.go | 54 +++++++++++++++++++ .../features_olm_operators_test.go | 32 +++++++++++ internal/featuresupport/features_platforms.go | 2 + internal/operators/mtv/config.go | 9 +++- internal/operators/mtv/manifest.go | 16 +++--- internal/operators/mtv/manifest_test.go | 8 +-- internal/operators/mtv/operator_test.go | 13 +++-- subsystem/cluster_test.go | 14 ++++- subsystem/operators_test.go | 3 +- 11 files changed, 133 insertions(+), 25 deletions(-) diff --git a/internal/featuresupport/feature_support_level.go b/internal/featuresupport/feature_support_level.go index 254300f8673..83fcbddf243 100644 --- a/internal/featuresupport/feature_support_level.go +++ b/internal/featuresupport/feature_support_level.go @@ -34,6 +34,7 @@ var featuresList = map[models.FeatureSupportLevelID]SupportLevelFeature{ models.FeatureSupportLevelIDLSO: (&LsoFeature{}).New(), models.FeatureSupportLevelIDMCE: (&MceFeature{}).New(), models.FeatureSupportLevelIDODF: (&OdfFeature{}).New(), + models.FeatureSupportLevelIDMTV: (&MtvFeature{}).New(), // Platform features models.FeatureSupportLevelIDNUTANIXINTEGRATION: (&NutanixIntegrationFeature{}).New(), diff --git a/internal/featuresupport/feature_support_test.go b/internal/featuresupport/feature_support_test.go index 64a12ee4c9a..951fb81f383 100644 --- a/internal/featuresupport/feature_support_test.go +++ b/internal/featuresupport/feature_support_test.go @@ -269,19 +269,19 @@ var _ = Describe("V2ListFeatureSupportLevels API", func() { When("GetFeatureSupportList 4.12 with Platform", func() { It(string(*filters.PlatformType)+" "+swag.StringValue(filters.ExternalPlatformName), func() { list := GetFeatureSupportList("dummy", nil, filters.PlatformType, filters.ExternalPlatformName) - Expect(len(list)).To(Equal(19)) + Expect(len(list)).To(Equal(20)) }) }) } It("GetFeatureSupportList 4.12", func() { list := GetFeatureSupportList("4.12", nil, nil, nil) - Expect(len(list)).To(Equal(24)) + Expect(len(list)).To(Equal(25)) }) It("GetFeatureSupportList 4.13", func() { list := GetFeatureSupportList("4.13", nil, nil, nil) - Expect(len(list)).To(Equal(24)) + Expect(len(list)).To(Equal(25)) }) It("GetCpuArchitectureSupportList 4.12", func() { diff --git a/internal/featuresupport/features_olm_operators.go b/internal/featuresupport/features_olm_operators.go index 3538065f949..d6d8b4f3522 100644 --- a/internal/featuresupport/features_olm_operators.go +++ b/internal/featuresupport/features_olm_operators.go @@ -289,3 +289,57 @@ func (feature *MceFeature) getFeatureActiveLevel(cluster *common.Cluster, _ *mod } return activeLevelNotActive } + +// MtvFeature +type MtvFeature struct{} + +func (feature *MtvFeature) New() SupportLevelFeature { + return &MtvFeature{} +} + +func (feature *MtvFeature) getId() models.FeatureSupportLevelID { + return models.FeatureSupportLevelIDMTV +} + +func (feature *MtvFeature) GetName() string { + return "OpenShift Migration Toolkit for Virtualization" +} + +func (feature *MtvFeature) getSupportLevel(filters SupportLevelFilters) models.SupportLevel { + if !isFeatureCompatibleWithArchitecture(feature, filters.OpenshiftVersion, swag.StringValue(filters.CPUArchitecture)) { + return models.SupportLevelUnavailable + } + + if filters.PlatformType != nil && (*filters.PlatformType == models.PlatformTypeVsphere || *filters.PlatformType == models.PlatformTypeNutanix || *filters.PlatformType == models.PlatformTypeNone) { + return models.SupportLevelUnavailable + } + + if isNotSupported, err := common.BaseVersionLessThan("4.14", filters.OpenshiftVersion); isNotSupported || err != nil { + return models.SupportLevelUnavailable + } + + return models.SupportLevelSupported +} + +func (feature *MtvFeature) getIncompatibleArchitectures(_ *string) *[]models.ArchitectureSupportLevelID { + incompatibleArchitecture := []models.ArchitectureSupportLevelID{ + models.ArchitectureSupportLevelIDARM64ARCHITECTURE, + models.ArchitectureSupportLevelIDS390XARCHITECTURE, + models.ArchitectureSupportLevelIDPPC64LEARCHITECTURE, + } + return &incompatibleArchitecture +} + +func (feature *MtvFeature) getIncompatibleFeatures(string) *[]models.FeatureSupportLevelID { + return &[]models.FeatureSupportLevelID{ + models.FeatureSupportLevelIDNUTANIXINTEGRATION, + models.FeatureSupportLevelIDVSPHEREINTEGRATION, + } +} + +func (feature *MtvFeature) getFeatureActiveLevel(cluster *common.Cluster, _ *models.InfraEnv, clusterUpdateParams *models.V2ClusterUpdateParams, _ *models.InfraEnvUpdateParams) featureActiveLevel { + if isOperatorActivated("mtv", cluster, clusterUpdateParams) && isOperatorActivated("cnv", cluster, clusterUpdateParams) { + return activeLevelActive + } + return activeLevelNotActive +} diff --git a/internal/featuresupport/features_olm_operators_test.go b/internal/featuresupport/features_olm_operators_test.go index 3b03fea7fb2..cc47400d51e 100644 --- a/internal/featuresupport/features_olm_operators_test.go +++ b/internal/featuresupport/features_olm_operators_test.go @@ -236,4 +236,36 @@ var _ = Describe("V2ListFeatureSupportLevels API", func() { Entry("on S390x is NOT supported", []string{"4.11", "4.13", "4.14", "4.21"}, models.ClusterCPUArchitectureS390x, false), Entry("on ppc64le is NOT supported", []string{"4.11", "4.13", "4.14", "4.21"}, models.ClusterCPUArchitecturePpc64le, false), ) + + Context("Test MTV feature", func() { + DescribeTable("Validate MTV on Architecture", func(ocpVersion []string, cpuArch string, expectedResult bool) { + for _, v := range ocpVersion { + version := v + result := IsFeatureAvailable(models.FeatureSupportLevelIDMTV, version, swag.String(cpuArch)) + Expect(result).Should(Equal(expectedResult), + fmt.Sprintf("Feature: %s, OCP version: %s, CpuArch: %s, should be %v", models.FeatureSupportLevelIDMTV, v, cpuArch, expectedResult)) + } + }, + Entry("on X86 is supported above 4.14", []string{"4.14", "4.21"}, models.ClusterCPUArchitectureX8664, true), + Entry("on X86 is NOT supported", []string{"4.8", "4.11", "4.13"}, models.ClusterCPUArchitectureX8664, false), + Entry("on arm64 is NOT supported", []string{"4.8", "4.11", "4.14", "4.21"}, models.ClusterCPUArchitectureArm64, false), + Entry("on S390x is NOT supported", []string{"4.11", "4.13", "4.14", "4.21"}, models.ClusterCPUArchitectureS390x, false), + Entry("on ppc64le is NOT supported", []string{"4.11", "4.13", "4.14", "4.21"}, models.ClusterCPUArchitecturePpc64le, false), + ) + + DescribeTable("Validate MTV on platform", func(ocpVersion string, cpuArch string, platformType models.PlatformType, expectedResult models.SupportLevel) { + featureSupportLevels := GetFeatureSupportList( + ocpVersion, + swag.String(cpuArch), + common.PlatformTypePtr(platformType), + nil, + ) + Expect(featureSupportLevels[string(models.FeatureSupportLevelIDMTV)]).To(Equal(expectedResult)) + }, + Entry("on Vsphere", "4.14", common.X86CPUArchitecture, models.PlatformTypeVsphere, models.SupportLevelUnavailable), + Entry("on Nutanix", "4.14", common.X86CPUArchitecture, models.PlatformTypeNutanix, models.SupportLevelUnavailable), + Entry("on None", "4.14", common.X86CPUArchitecture, models.PlatformTypeNone, models.SupportLevelUnavailable), + Entry("on baremetal", "4.14", common.X86CPUArchitecture, models.PlatformTypeBaremetal, models.SupportLevelSupported), + ) + }) }) diff --git a/internal/featuresupport/features_platforms.go b/internal/featuresupport/features_platforms.go index f79ae1e5887..eae4ad4cca8 100644 --- a/internal/featuresupport/features_platforms.go +++ b/internal/featuresupport/features_platforms.go @@ -182,6 +182,7 @@ func (feature *NutanixIntegrationFeature) getIncompatibleFeatures(string) *[]mod models.FeatureSupportLevelIDMCE, models.FeatureSupportLevelIDCNV, models.FeatureSupportLevelIDPLATFORMMANAGEDNETWORKING, + models.FeatureSupportLevelIDMTV, } } @@ -234,6 +235,7 @@ func (feature *VsphereIntegrationFeature) getIncompatibleFeatures(openshiftVersi models.FeatureSupportLevelIDLVM, models.FeatureSupportLevelIDPLATFORMMANAGEDNETWORKING, models.FeatureSupportLevelIDCNV, + models.FeatureSupportLevelIDMTV, } if isNotSupported, err := common.BaseVersionLessThan("4.13", openshiftVersion); isNotSupported || err != nil { diff --git a/internal/operators/mtv/config.go b/internal/operators/mtv/config.go index b41374a2eed..e2508649c51 100644 --- a/internal/operators/mtv/config.go +++ b/internal/operators/mtv/config.go @@ -8,9 +8,16 @@ const ( Source = "redhat-operators" SourceName = "mtv-operator" MtvMinOpenshiftVersion = "4.14.0" + + // Memory value provided in MiB + MasterMemory int64 = 1024 + MasterCPU int64 = 1 + // Memory value provided in MiB + WorkerMemory int64 = 1024 + WorkerCPU int64 = 1 ) type Config struct { MtvCPUPerHost int64 `envconfig:"MTV_CPU_PER_HOST" default:"1"` - MtvMemoryPerHostMiB int64 `envconfig:"MTV_MEMORY_PER_HOST_MIB" default:"400"` + MtvMemoryPerHostMiB int64 `envconfig:"MTV_MEMORY_PER_HOST_MIB" default:"1024"` } diff --git a/internal/operators/mtv/manifest.go b/internal/operators/mtv/manifest.go index 0e1bbc40c08..085ffb1434e 100644 --- a/internal/operators/mtv/manifest.go +++ b/internal/operators/mtv/manifest.go @@ -6,22 +6,22 @@ import ( ) func Manifests() (map[string][]byte, []byte, error) { - mtvSubscription, err := subscription(Namespace, Subscription, Source, SourceName) + mtvSubscription, err := getSubscription(Namespace, Subscription, Source, SourceName) if err != nil { return nil, nil, err } - mtvNamespace, err := namespace(Namespace) + mtvNamespace, err := getNamespace(Namespace) if err != nil { return nil, nil, err } - mtvOperatorGroup, err := group(Namespace) + mtvOperatorGroup, err := getOperatorGroup(Namespace) if err != nil { return nil, nil, err } - forklistController, err := controller(Namespace) + forklistController, err := getController(Namespace) if err != nil { return nil, nil, err } @@ -48,7 +48,7 @@ func executeTemplate(data map[string]string, contentName, content string) ([]byt return buf.Bytes(), nil } -func subscription(namespace, subscription, source, sourceName string) ([]byte, error) { +func getSubscription(namespace, subscription, source, sourceName string) ([]byte, error) { data := map[string]string{ "OPERATOR_NAMESPACE": namespace, "OPERATOR_SUBSCRIPTION_NAME": subscription, @@ -58,21 +58,21 @@ func subscription(namespace, subscription, source, sourceName string) ([]byte, e return executeTemplate(data, "mtvSubscription", mtvSubscription) } -func namespace(namespace string) ([]byte, error) { +func getNamespace(namespace string) ([]byte, error) { data := map[string]string{ "OPERATOR_NAMESPACE": namespace, } return executeTemplate(data, "mtvNamespace", mtvNamespace) } -func group(namespace string) ([]byte, error) { +func getOperatorGroup(namespace string) ([]byte, error) { data := map[string]string{ "OPERATOR_NAMESPACE": namespace, } return executeTemplate(data, "mtvGroup", mtvGroup) } -func controller(namespace string) ([]byte, error) { +func getController(namespace string) ([]byte, error) { data := map[string]string{ "OPERATOR_NAMESPACE": namespace, } diff --git a/internal/operators/mtv/manifest_test.go b/internal/operators/mtv/manifest_test.go index 49669de6ab9..65e98430481 100644 --- a/internal/operators/mtv/manifest_test.go +++ b/internal/operators/mtv/manifest_test.go @@ -42,7 +42,7 @@ var _ = Describe("MTV manifest generation", func() { }) It("Check Subscription manifest", func() { - subscriptionData, err := subscription(Namespace, Subscription, Source, SourceName) + subscriptionData, err := getSubscription(Namespace, Subscription, Source, SourceName) Expect(err).To(BeNil()) Expect(extractData(subscriptionData, "metadata.name")).To(Equal(Subscription)) @@ -53,21 +53,21 @@ var _ = Describe("MTV manifest generation", func() { }) It("Check namespace manifest", func() { - nsData, err := namespace(Namespace) + nsData, err := getNamespace(Namespace) Expect(err).To(BeNil()) Expect(extractData(nsData, "metadata.name")).To(Equal(Namespace)) }) It("Check operator group manifest", func() { - opData, err := group(Namespace) + opData, err := getOperatorGroup(Namespace) Expect(err).To(BeNil()) Expect(extractData(opData, "metadata.namespace")).To(Equal(Namespace)) }) It("Check controller manifest", func() { - controllerData, err := controller(Namespace) + controllerData, err := getController(Namespace) Expect(err).To(BeNil()) Expect(extractData(controllerData, "metadata.namespace")).To(Equal(Namespace)) diff --git a/internal/operators/mtv/operator_test.go b/internal/operators/mtv/operator_test.go index b03ecac03eb..2d554589594 100644 --- a/internal/operators/mtv/operator_test.go +++ b/internal/operators/mtv/operator_test.go @@ -16,9 +16,8 @@ import ( var _ = Describe("MTV Operator", func() { const ( - openshiftVersion = "4.10.0" - minCpu = 1 - minRamMib = 400 + minCpu = 1 + minRamMib = 1024 ) var ( @@ -56,7 +55,7 @@ var _ = Describe("MTV Operator", func() { BeforeEach(func() { mode := models.ClusterHighAvailabilityModeFull cluster = common.Cluster{ - Cluster: models.Cluster{HighAvailabilityMode: &mode, OpenshiftVersion: openshiftVersion}, + Cluster: models.Cluster{HighAvailabilityMode: &mode}, } }) @@ -90,12 +89,12 @@ var _ = Describe("MTV Operator", func() { BeforeEach(func() { mode := models.ClusterHighAvailabilityModeFull cluster = common.Cluster{ - Cluster: models.Cluster{HighAvailabilityMode: &mode, OpenshiftVersion: openshiftVersion}, + Cluster: models.Cluster{HighAvailabilityMode: &mode}, } }) It("host should be valid", func() { - host := models.Host{Role: models.HostRoleMaster, Inventory: getInventory(int64(400))} + host := models.Host{Role: models.HostRoleMaster, Inventory: getInventory(int64(1024))} result, err := operator.ValidateHost(context.TODO(), &cluster, &host, nil) Expect(err).To(BeNil()) @@ -125,7 +124,7 @@ func newRequirements(cpuCores int64, ramMib int64) *models.ClusterHostRequiremen } func getInventory(memMiB int64) string { - inventory := models.Inventory{CPU: &models.CPU{Architecture: "x86", Count: 1}, Memory: &models.Memory{UsableBytes: conversions.MibToBytes(memMiB)}} + inventory := models.Inventory{CPU: &models.CPU{Architecture: "x86_64", Count: 1}, Memory: &models.Memory{UsableBytes: conversions.MibToBytes(memMiB)}} inventoryJSON, err := common.MarshalInventory(&inventory) Expect(err).ToNot(HaveOccurred()) return inventoryJSON diff --git a/subsystem/cluster_test.go b/subsystem/cluster_test.go index 1b1d899802a..e8c598f9369 100644 --- a/subsystem/cluster_test.go +++ b/subsystem/cluster_test.go @@ -35,6 +35,7 @@ import ( "github.com/openshift/assisted-service/internal/operators/lso" "github.com/openshift/assisted-service/internal/operators/lvm" "github.com/openshift/assisted-service/internal/operators/mce" + "github.com/openshift/assisted-service/internal/operators/mtv" "github.com/openshift/assisted-service/internal/operators/odf" "github.com/openshift/assisted-service/internal/usage" "github.com/openshift/assisted-service/models" @@ -3704,6 +3705,14 @@ var _ = Describe("Preflight Cluster Requirements", func() { CPUCores: mce.MinimumCPU, RAMMib: conversions.GibToMib(mce.MinimumMemory), } + workerMTVRequirements = models.ClusterHostRequirementsDetails{ + CPUCores: mtv.WorkerCPU, + RAMMib: conversions.GibToMib(mtv.WorkerMemory), + } + masterMTVRequirements = models.ClusterHostRequirementsDetails{ + CPUCores: mtv.MasterCPU, + RAMMib: conversions.GibToMib(mtv.MasterMemory), + } ) BeforeEach(func() { @@ -3731,7 +3740,7 @@ var _ = Describe("Preflight Cluster Requirements", func() { }, } Expect(*requirements.Ocp).To(BeEquivalentTo(expectedOcpRequirements)) - Expect(requirements.Operators).To(HaveLen(5)) + Expect(requirements.Operators).To(HaveLen(6)) for _, op := range requirements.Operators { switch op.OperatorName { case lso.Operator.Name: @@ -3746,6 +3755,9 @@ var _ = Describe("Preflight Cluster Requirements", func() { case mce.Operator.Name: Expect(*op.Requirements.Master.Quantitative).To(BeEquivalentTo(masterMCERequirements)) Expect(*op.Requirements.Worker.Quantitative).To(BeEquivalentTo(workerMCERequirements)) + case mtv.Operator.Name: + Expect(*op.Requirements.Master.Quantitative).To(BeEquivalentTo(masterMTVRequirements)) + Expect(*op.Requirements.Worker.Quantitative).To(BeEquivalentTo(workerMTVRequirements)) case lvm.Operator.Name: continue // lvm operator is tested separately default: diff --git a/subsystem/operators_test.go b/subsystem/operators_test.go index 4cf7cc47e7f..ee1c4b9ad43 100644 --- a/subsystem/operators_test.go +++ b/subsystem/operators_test.go @@ -21,6 +21,7 @@ import ( "github.com/openshift/assisted-service/internal/operators/lso" "github.com/openshift/assisted-service/internal/operators/lvm" "github.com/openshift/assisted-service/internal/operators/mce" + "github.com/openshift/assisted-service/internal/operators/mtv" "github.com/openshift/assisted-service/internal/operators/odf" "github.com/openshift/assisted-service/models" ) @@ -36,7 +37,7 @@ var _ = Describe("Operators endpoint tests", func() { reply, err := userBMClient.Operators.V2ListSupportedOperators(context.TODO(), opclient.NewV2ListSupportedOperatorsParams()) Expect(err).ToNot(HaveOccurred()) - Expect(reply.GetPayload()).To(ConsistOf(odf.Operator.Name, lso.Operator.Name, cnv.Operator.Name, lvm.Operator.Name, mce.Operator.Name)) + Expect(reply.GetPayload()).To(ConsistOf(odf.Operator.Name, lso.Operator.Name, cnv.Operator.Name, lvm.Operator.Name, mce.Operator.Name, mtv.Operator.Name)) }) It("should provide operator properties", func() {