diff --git a/pkg/controller/plan/adapter/base/doc.go b/pkg/controller/plan/adapter/base/doc.go index b3c9c30f5..cce62e86f 100644 --- a/pkg/controller/plan/adapter/base/doc.go +++ b/pkg/controller/plan/adapter/base/doc.go @@ -106,6 +106,8 @@ type Client interface { type Validator interface { // Validate that a VM's disk backing storage has been mapped. StorageMapped(vmRef ref.Ref) (bool, error) + // Validate that a VM's direct LUN/FC has the details (oVirt only) + DirectStorage(vmRef ref.Ref) (bool, error) // Validate that a VM's networks have been mapped. NetworksMapped(vmRef ref.Ref) (bool, error) // Validate that a VM's Host isn't in maintenance mode. diff --git a/pkg/controller/plan/adapter/ocp/validator.go b/pkg/controller/plan/adapter/ocp/validator.go index 2dbdf6a26..1cc5341a3 100644 --- a/pkg/controller/plan/adapter/ocp/validator.go +++ b/pkg/controller/plan/adapter/ocp/validator.go @@ -179,3 +179,8 @@ func (r *Validator) NetworksMapped(vmRef ref.Ref) (ok bool, err error) { return true, nil } + +// NO-OP +func (r *Validator) DirectStorage(vmRef ref.Ref) (bool, error) { + return true, nil +} diff --git a/pkg/controller/plan/adapter/openstack/validator.go b/pkg/controller/plan/adapter/openstack/validator.go index 3c14be692..8531805a6 100644 --- a/pkg/controller/plan/adapter/openstack/validator.go +++ b/pkg/controller/plan/adapter/openstack/validator.go @@ -110,3 +110,8 @@ func (r *Validator) PodNetwork(vmRef ref.Ref) (ok bool, err error) { ok = podMapped <= 1 return } + +// NO-OP +func (r *Validator) DirectStorage(vmRef ref.Ref) (bool, error) { + return true, nil +} diff --git a/pkg/controller/plan/adapter/ova/validator.go b/pkg/controller/plan/adapter/ova/validator.go index 590d1ebc0..9f73a60aa 100644 --- a/pkg/controller/plan/adapter/ova/validator.go +++ b/pkg/controller/plan/adapter/ova/validator.go @@ -108,3 +108,8 @@ func (r *Validator) MaintenanceMode(vmRef ref.Ref) (ok bool, err error) { ok = true return } + +// NO-OP +func (r *Validator) DirectStorage(vmRef ref.Ref) (bool, error) { + return true, nil +} diff --git a/pkg/controller/plan/adapter/ovirt/BUILD.bazel b/pkg/controller/plan/adapter/ovirt/BUILD.bazel index f61362258..539a508c0 100644 --- a/pkg/controller/plan/adapter/ovirt/BUILD.bazel +++ b/pkg/controller/plan/adapter/ovirt/BUILD.bazel @@ -18,6 +18,7 @@ go_library( "//pkg/controller/plan/adapter/base", "//pkg/controller/plan/context", "//pkg/controller/plan/util", + "//pkg/controller/provider/container", "//pkg/controller/provider/container/ovirt", "//pkg/controller/provider/web", "//pkg/controller/provider/web/base", diff --git a/pkg/controller/plan/adapter/ovirt/client.go b/pkg/controller/plan/adapter/ovirt/client.go index 0217149f3..c65d791a9 100644 --- a/pkg/controller/plan/adapter/ovirt/client.go +++ b/pkg/controller/plan/adapter/ovirt/client.go @@ -286,6 +286,22 @@ func (r *Client) getVM(vmRef ref.Ref) (ovirtVm *ovirtsdk.Vm, vmService *ovirtsdk vmRef.String()) return } + //system, err := r.connection.SystemService().Get().Send() + //product_info + /* + // + // oVirt Engine + // ovirt.org + // + // 4 + // 4.0.4 + // 4 + // 0 + // 0 + // + // + */ + vmService = r.connection.SystemService().VmsService().VmService(vm.ID) vmResponse, err := vmService.Get().Query("correlation_id", r.Migration.Name).Send() if err != nil { diff --git a/pkg/controller/plan/adapter/ovirt/validator.go b/pkg/controller/plan/adapter/ovirt/validator.go index 5716b850f..2a96dd757 100644 --- a/pkg/controller/plan/adapter/ovirt/validator.go +++ b/pkg/controller/plan/adapter/ovirt/validator.go @@ -1,15 +1,18 @@ package ovirt import ( + "strconv" + api "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1" "github.com/konveyor/forklift-controller/pkg/apis/forklift/v1beta1/ref" + "github.com/konveyor/forklift-controller/pkg/controller/provider/container" "github.com/konveyor/forklift-controller/pkg/controller/provider/web" model "github.com/konveyor/forklift-controller/pkg/controller/provider/web/ovirt" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" "github.com/konveyor/forklift-controller/pkg/settings" ) -// vSphere validator. +// oVirt validator. type Validator struct { plan *api.Plan inventory web.Client @@ -92,7 +95,6 @@ func (r *Validator) PodNetwork(vmRef ref.Ref) (ok bool, err error) { // Validate that a VM's disk backing storage has been mapped. func (r *Validator) StorageMapped(vmRef ref.Ref) (ok bool, err error) { - // TODO validate ovirt version > ovirt-engine-4.5.2.1 (https://github.com/oVirt/ovirt-engine/commit/e7c1f585863a332bcecfc8c3d909c9a3a56eb922) if r.plan.Referenced.Map.Storage == nil { return } @@ -106,20 +108,96 @@ func (r *Validator) StorageMapped(vmRef ref.Ref) (ok bool, err error) { vmRef.String()) return } + for _, da := range vm.DiskAttachments { if da.Disk.StorageType != "lun" { if !r.plan.Referenced.Map.Storage.Status.Refs.Find(ref.Ref{ID: da.Disk.StorageDomain}) { return } - } else if len(da.Disk.Lun.LogicalUnits.LogicalUnit) > 0 { - // Have LUN disk but without the relevant data. This might happen with older oVirt versions. - return } } ok = true return } +// Validates oVirt version in case we use direct LUN/FC storage +func (r *Validator) DirectStorage(vmRef ref.Ref) (ok bool, err error) { + if r.plan.Referenced.Map.Storage == nil { + return + } + vm := &model.Workload{} + err = r.inventory.Find(vm, vmRef) + if err != nil { + err = liberr.Wrap( + err, + "VM not found in inventory.", + "vm", + vmRef.String()) + return + } + + for _, da := range vm.DiskAttachments { + if da.Disk.StorageType == "lun" { + if len(da.Disk.Lun.LogicalUnits.LogicalUnit) > 0 { + if ok, err := r.checkOvirtVersion(); err != nil || !ok { + return ok, err + } + } + } + } + ok = true + return +} + +// Checks the version for ovirt direct LUN/FC +func (r *Validator) checkOvirtVersion() (ok bool, err error) { + // validate ovirt version > ovirt-engine-4.5.2.1 (https://github.com/oVirt/ovirt-engine/commit/e7c1f585863a332bcecfc8c3d909c9a3a56eb922) + rl := container.Build(nil, r.plan.Referenced.Provider.Source, r.plan.Referenced.Secret) + major, minor, build, revision, err := rl.Version() + if err != nil { + return false, err + } + val, err := strconv.Atoi(major) + if err != nil { + return false, err + } + if val != 4 { + if val > 4 { + return true, nil + } + return false, nil + } + val, err = strconv.Atoi(minor) + if err != nil { + return false, err + } + if val != 5 { + if val > 5 { + return true, nil + } + return false, nil + } + val, err = strconv.Atoi(build) + if err != nil { + return false, err + } + if val != 2 { + if val > 2 { + return true, nil + } + return false, nil + } + val, err = strconv.Atoi(revision) + if err != nil { + return false, err + } + if val < 1 { + return false, nil + } + ok = true + return +} + // Validate that a VM's Host isn't in maintenance mode. No-op for oVirt. func (r *Validator) MaintenanceMode(_ ref.Ref) (ok bool, err error) { ok = true diff --git a/pkg/controller/plan/adapter/vsphere/validator.go b/pkg/controller/plan/adapter/vsphere/validator.go index 404cb26a6..bd01649c6 100644 --- a/pkg/controller/plan/adapter/vsphere/validator.go +++ b/pkg/controller/plan/adapter/vsphere/validator.go @@ -144,3 +144,8 @@ func (r *Validator) MaintenanceMode(vmRef ref.Ref) (ok bool, err error) { ok = !host.InMaintenanceMode return } + +// NO-OP +func (r *Validator) DirectStorage(vmRef ref.Ref) (bool, error) { + return true, nil +} diff --git a/pkg/controller/plan/validation.go b/pkg/controller/plan/validation.go index d71cfe9ca..0935a6378 100644 --- a/pkg/controller/plan/validation.go +++ b/pkg/controller/plan/validation.go @@ -15,6 +15,7 @@ import ( libcnd "github.com/konveyor/forklift-controller/pkg/lib/condition" liberr "github.com/konveyor/forklift-controller/pkg/lib/error" libref "github.com/konveyor/forklift-controller/pkg/lib/ref" + v1 "k8s.io/api/core/v1" k8serr "k8s.io/apimachinery/pkg/api/errors" k8svalidation "k8s.io/apimachinery/pkg/util/validation" "sigs.k8s.io/controller-runtime/pkg/client" @@ -34,6 +35,7 @@ const ( VMAlreadyExists = "VMAlreadyExists" VMNetworksNotMapped = "VMNetworksNotMapped" VMStorageNotMapped = "VMStorageNotMapped" + VMStorageNotSupported = "VMStorageNotSupported" VMMultiplePodNetworkMappings = "VMMultiplePodNetworkMappings" HostNotReady = "HostNotReady" DuplicateVM = "DuplicateVM" @@ -342,6 +344,14 @@ func (r *Reconciler) validateVM(plan *api.Plan) error { Message: "VM has unmapped storage.", Items: []string{}, } + unsupportedStorage := libcnd.Condition{ + Type: VMStorageNotSupported, + Status: True, + Reason: NotValid, + Category: Critical, + Message: "VM has unsupported storage. Direct LUN/FC in oVirt requires version 4.5.2.1", + Items: []string{}, + } maintenanceMode := libcnd.Condition{ Type: HostNotReady, Status: True, @@ -411,6 +421,12 @@ func (r *Reconciler) validateVM(plan *api.Plan) error { if err != nil { return err } + if plan.Referenced.Secret == nil { + err = r.setupSecret(plan) + if err != nil { + return err + } + } if plan.Referenced.Map.Network != nil { ok, err := validator.NetworksMapped(*ref) if err != nil { @@ -435,6 +451,13 @@ func (r *Reconciler) validateVM(plan *api.Plan) error { if !ok { unmappedStorage.Items = append(unmappedStorage.Items, ref.String()) } + ok, err = validator.DirectStorage(*ref) + if err != nil { + return err + } + if !ok { + unsupportedStorage.Items = append(unsupportedStorage.Items, ref.String()) + } } ok, err := validator.MaintenanceMode(*ref) if err != nil { @@ -491,6 +514,9 @@ func (r *Reconciler) validateVM(plan *api.Plan) error { if len(unmappedStorage.Items) > 0 { plan.Status.SetCondition(unmappedStorage) } + if len(unsupportedStorage.Items) > 0 { + plan.Status.SetCondition(unsupportedStorage) + } if len(maintenanceMode.Items) > 0 { plan.Status.SetCondition(maintenanceMode) } @@ -670,3 +696,17 @@ func (r *Reconciler) validateVddkImage(plan *api.Plan) (err error) { } return nil } + +func (r *Reconciler) setupSecret(plan *api.Plan) (err error) { + key := client.ObjectKey{ + Namespace: plan.Referenced.Provider.Source.Spec.Secret.Namespace, + Name: plan.Referenced.Provider.Source.Spec.Secret.Name, + } + secret := v1.Secret{} + err = r.Get(context.TODO(), key, &secret) + if err != nil { + return + } + plan.Referenced.Secret = &secret + return +} diff --git a/pkg/controller/provider/container/ocp/collector.go b/pkg/controller/provider/container/ocp/collector.go index 349be939b..5f126f07e 100644 --- a/pkg/controller/provider/container/ocp/collector.go +++ b/pkg/controller/provider/container/ocp/collector.go @@ -48,6 +48,11 @@ func New(db libmodel.DB, provider *api.Provider, secret *core.Secret) libcontain } } +// NO-OP +func (r *Collector) Version() (_, _, _, _ string, err error) { + return +} + // OCP collector. type Collector struct { *libocp.Collector diff --git a/pkg/controller/provider/container/openstack/collector.go b/pkg/controller/provider/container/openstack/collector.go index 87b4e0236..a1535e5c4 100644 --- a/pkg/controller/provider/container/openstack/collector.go +++ b/pkg/controller/provider/container/openstack/collector.go @@ -113,6 +113,11 @@ func (r *Collector) Test() (_ int, err error) { return } +// NO-OP +func (r *Collector) Version() (_ , _, _, _ string, err error) { + return +} + // Start the collector. func (r *Collector) Start() error { ctx := Context{ diff --git a/pkg/controller/provider/container/ova/collector.go b/pkg/controller/provider/container/ova/collector.go index 6e5e64fe2..610ee33fc 100644 --- a/pkg/controller/provider/container/ova/collector.go +++ b/pkg/controller/provider/container/ova/collector.go @@ -117,6 +117,11 @@ func (r *Collector) Test() (_ int, err error) { return } +// NO-OP +func (r *Collector) Version() (_, _, _, _ string, err error) { + return +} + // Start the collector. func (r *Collector) Start() error { ctx := Context{ diff --git a/pkg/controller/provider/container/ovirt/client.go b/pkg/controller/provider/container/ovirt/client.go index beae959ee..e9d9a9e4f 100644 --- a/pkg/controller/provider/container/ovirt/client.go +++ b/pkg/controller/provider/container/ovirt/client.go @@ -189,12 +189,12 @@ func (r *Client) get(path string, object interface{}, param ...libweb.Param) (er } // Get system. -func (r *Client) system() (s *System, status int, err error) { +func (r *Client) system() (system *System, status int, err error) { status, err = r.connect() if err != nil { return } - system := &System{} + system = &System{} status, err = r.client.Get(r.url, system) if err != nil { return diff --git a/pkg/controller/provider/container/ovirt/collector.go b/pkg/controller/provider/container/ovirt/collector.go index 4972561b1..df700add2 100644 --- a/pkg/controller/provider/container/ovirt/collector.go +++ b/pkg/controller/provider/container/ovirt/collector.go @@ -133,6 +133,21 @@ func (r *Collector) Test() (status int, err error) { return } +func (r *Collector) Version() (major, minor, build, revision string, err error) { + system, _, err := r.client.system() + if err != nil { + return + } + version := strings.Split(system.Product.Version.FullVersion, ".") + major = version[0] + minor = version[1] + build = version[2] + if len(version) > 3 { + revision = strings.Split(version[3], "-")[0] + } + return +} + // Start the collector. func (r *Collector) Start() error { ctx := Context{ diff --git a/pkg/controller/provider/container/ovirt/resource.go b/pkg/controller/provider/container/ovirt/resource.go index b925308c5..f7bcb4c9e 100644 --- a/pkg/controller/provider/container/ovirt/resource.go +++ b/pkg/controller/provider/container/ovirt/resource.go @@ -13,10 +13,11 @@ type System struct { Name string `json:"name"` Vendor string `json:"vendor"` Version struct { - Build string `json:"build"` - Major string `json:"major"` - Minor string `json:"minor"` - Revision string `json:"revision"` + Build string `json:"build"` + Major string `json:"major"` + Minor string `json:"minor"` + Revision string `json:"revision"` + FullVersion string `json:"full_version"` } `json:"version"` } `json:"product_info"` } diff --git a/pkg/controller/provider/container/vsphere/collector.go b/pkg/controller/provider/container/vsphere/collector.go index aeb82c93c..c427f02f6 100644 --- a/pkg/controller/provider/container/vsphere/collector.go +++ b/pkg/controller/provider/container/vsphere/collector.go @@ -286,6 +286,11 @@ func (r *Collector) Test() (status int, err error) { return } +// NO-OP +func (r *Collector) Version() (_, _, _, _ string, err error) { + return +} + // Start the collector. func (r *Collector) Start() error { ctx := context.Background() diff --git a/pkg/lib/inventory/container/container.go b/pkg/lib/inventory/container/container.go index 525ce156d..90e5ea17a 100644 --- a/pkg/lib/inventory/container/container.go +++ b/pkg/lib/inventory/container/container.go @@ -148,4 +148,6 @@ type Collector interface { Test() (int, error) // Reset Reset() + // Get the system verion - only ovirt + Version() (string, string, string, string, error) }