Skip to content

Commit

Permalink
feat: bring more visibility to dynamic device discovery by warnings a…
Browse files Browse the repository at this point in the history
…nd a new status field

Signed-off-by: Suleyman Akbas <[email protected]>
  • Loading branch information
suleymanakbas91 committed Apr 24, 2024
1 parent 7a7d364 commit 5fde32c
Show file tree
Hide file tree
Showing 11 changed files with 105 additions and 17 deletions.
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ end
- [Cleanup](#cleanup)
- [Metrics](#metrics)
- [Known Limitations](#known-limitations)
* [Dynamic Device Discovery](#dynamic-device-discovery)
* [Unsupported Device Types](#unsupported-device-types)
* [Single LVMCluster support](#single-lvmcluster-support)
* [Upgrades from v 4.10 and v4.11](#upgrades-from-v-410-and-v411)
Expand Down Expand Up @@ -369,6 +370,16 @@ LVMS provides [TopoLVM metrics](https://github.com/topolvm/topolvm/blob/v0.21.0/
## Known Limitations
### Dynamic Device Discovery
When a `DeviceSelector` isn't configured for a device class, LVMS operates dynamically, continuously monitoring attached devices on the node and adding them to the volume group if they're unused and supported. However, this approach presents several potential issues:
- LVMS may inadvertently add a device to the volume group that wasn't intended for LVMS.
- Removing devices could disrupt the volume group.
- LVMS lacks awareness of volume group changes that could lead to data loss, potentially necessitating manual node remediation.

Given these considerations, it's advised against using LVMS in dynamic discovery mode for production environments.
### Unsupported Device Types
Here is a list of the types of devices that are excluded by LVMS. To get more information about the devices on your machine and to check if they fall under any of these filters, run:
Expand Down
16 changes: 10 additions & 6 deletions api/v1alpha1/lvmcluster_webhook.go
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ func (v *lvmClusterValidator) ValidateCreate(ctx context.Context, obj runtime.Ob
return warnings, err
}

err = v.verifyPathsAreNotEmpty(l)
pathWarnings, err := v.verifyPathsAreNotEmpty(l)
warnings = append(warnings, pathWarnings...)
if err != nil {
return warnings, err
}
Expand Down Expand Up @@ -129,7 +130,8 @@ func (v *lvmClusterValidator) ValidateUpdate(_ context.Context, old, new runtime
return warnings, err
}

err = v.verifyPathsAreNotEmpty(l)
pathWarnings, err := v.verifyPathsAreNotEmpty(l)
warnings = append(warnings, pathWarnings...)
if err != nil {
return warnings, err
}
Expand Down Expand Up @@ -298,23 +300,25 @@ func (v *lvmClusterValidator) verifyDeviceClass(l *LVMCluster) (admission.Warnin
return nil, nil
}

func (v *lvmClusterValidator) verifyPathsAreNotEmpty(l *LVMCluster) error {
func (v *lvmClusterValidator) verifyPathsAreNotEmpty(l *LVMCluster) (admission.Warnings, error) {

var deviceClassesWithoutPaths []string
for _, deviceClass := range l.Spec.Storage.DeviceClasses {
if deviceClass.DeviceSelector != nil {
if len(deviceClass.DeviceSelector.Paths) == 0 && len(deviceClass.DeviceSelector.OptionalPaths) == 0 {
return ErrPathsOrOptionalPathsMandatoryWithNonNilDeviceSelector
return nil, ErrPathsOrOptionalPathsMandatoryWithNonNilDeviceSelector
}
} else {
deviceClassesWithoutPaths = append(deviceClassesWithoutPaths, deviceClass.Name)
}
}
if len(l.Spec.Storage.DeviceClasses) > 1 && len(deviceClassesWithoutPaths) > 0 {
return fmt.Errorf("%w. Please specify device path(s) under deviceSelector.paths for %s deviceClass(es)", ErrEmptyPathsWithMultipleDeviceClasses, strings.Join(deviceClassesWithoutPaths, `,`))
return nil, fmt.Errorf("%w. Please specify device path(s) under deviceSelector.paths for %s deviceClass(es)", ErrEmptyPathsWithMultipleDeviceClasses, strings.Join(deviceClassesWithoutPaths, `,`))
} else if len(l.Spec.Storage.DeviceClasses) == 1 && len(deviceClassesWithoutPaths) == 1 {
return admission.Warnings{fmt.Sprintf("no device path(s) under deviceSelector.paths was specified for the %s deviceClass, LVMS will actively monitor and dynamically utilize any supported unused devices. This is not recommended for production environments. Please refer to the limitations outlined in the product documentation for further details.", deviceClassesWithoutPaths[0])}, nil
}

return nil
return nil, nil
}

func (v *lvmClusterValidator) verifyAbsolutePath(l *LVMCluster) error {
Expand Down
16 changes: 16 additions & 0 deletions api/v1alpha1/lvmvolumegroupnodestatus_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ import (

// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.

type DeviceDiscoveryPolicy string

const (
DeviceDiscoveryPolicyPreConfigured DeviceDiscoveryPolicy = "PreConfigured"
DeviceDiscoveryPolicyRuntimeDynamic DeviceDiscoveryPolicy = "RuntimeDynamic"
)

// LVMVolumeGroupNodeStatusSpec defines the desired state of LVMVolumeGroupNodeStatus
type LVMVolumeGroupNodeStatusSpec struct {
// NodeStatus contains the per node status of the VG
Expand Down Expand Up @@ -53,6 +60,15 @@ type VGStatus struct {
// Excluded contains the per node status of applied device exclusions that were picked up via selector,
// but were not used for other reasons.
Excluded []ExcludedDevice `json:"excluded,omitempty"`
// DeviceDiscoveryPolicy is a field to indicate whether the devices are discovered
// at runtime or preconfigured through a DeviceSelector
// If set to DeviceDiscoveryPolicyPreConfigured, the devices are preconfigured through a DeviceSelector.
// If set to DeviceDiscoveryPolicyRuntimeDynamic, the devices will be added to the VG if they are present at runtime.
// By default, the value is set to RuntimeDynamic.
// +kubebuilder:validation:Enum=None;RuntimeDynamic
// +kubebuilder:default=RuntimeDynamic
// +kubebuilder:validation:Required
DeviceDiscoveryPolicy DeviceDiscoveryPolicy `json:"deviceDiscoveryPolicy,omitempty"`
}

type ExcludedDevice struct {
Expand Down
12 changes: 12 additions & 0 deletions bundle/manifests/lvm.topolvm.io_lvmclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,18 @@ spec:
description: NodeStatus defines the observed state of the
deviceclass on the node
properties:
deviceDiscoveryPolicy:
default: RuntimeDynamic
description: |-
DeviceDiscoveryPolicy is a field to indicate whether the devices are discovered
at runtime or preconfigured through a DeviceSelector
If set to DeviceDiscoveryPolicyPreConfigured, the devices are preconfigured through a DeviceSelector.
If set to DeviceDiscoveryPolicyRuntimeDynamic, the devices will be added to the VG if they are present at runtime.
By default, the value is set to RuntimeDynamic.
enum:
- None
- RuntimeDynamic
type: string
devices:
description: Devices is the list of devices used by the
volume group
Expand Down
12 changes: 12 additions & 0 deletions bundle/manifests/lvm.topolvm.io_lvmvolumegroupnodestatuses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ spec:
description: NodeStatus contains the per node status of the VG
items:
properties:
deviceDiscoveryPolicy:
default: RuntimeDynamic
description: |-
DeviceDiscoveryPolicy is a field to indicate whether the devices are discovered
at runtime or preconfigured through a DeviceSelector
If set to DeviceDiscoveryPolicyPreConfigured, the devices are preconfigured through a DeviceSelector.
If set to DeviceDiscoveryPolicyRuntimeDynamic, the devices will be added to the VG if they are present at runtime.
By default, the value is set to RuntimeDynamic.
enum:
- None
- RuntimeDynamic
type: string
devices:
description: Devices is the list of devices used by the volume
group
Expand Down
4 changes: 2 additions & 2 deletions catalog/lvms-operator/v0.0.1.yaml

Large diffs are not rendered by default.

12 changes: 12 additions & 0 deletions config/crd/bases/lvm.topolvm.io_lvmclusters.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,18 @@ spec:
description: NodeStatus defines the observed state of the
deviceclass on the node
properties:
deviceDiscoveryPolicy:
default: RuntimeDynamic
description: |-
DeviceDiscoveryPolicy is a field to indicate whether the devices are discovered
at runtime or preconfigured through a DeviceSelector
If set to DeviceDiscoveryPolicyPreConfigured, the devices are preconfigured through a DeviceSelector.
If set to DeviceDiscoveryPolicyRuntimeDynamic, the devices will be added to the VG if they are present at runtime.
By default, the value is set to RuntimeDynamic.
enum:
- None
- RuntimeDynamic
type: string
devices:
description: Devices is the list of devices used by the
volume group
Expand Down
12 changes: 12 additions & 0 deletions config/crd/bases/lvm.topolvm.io_lvmvolumegroupnodestatuses.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ spec:
description: NodeStatus contains the per node status of the VG
items:
properties:
deviceDiscoveryPolicy:
default: RuntimeDynamic
description: |-
DeviceDiscoveryPolicy is a field to indicate whether the devices are discovered
at runtime or preconfigured through a DeviceSelector
If set to DeviceDiscoveryPolicyPreConfigured, the devices are preconfigured through a DeviceSelector.
If set to DeviceDiscoveryPolicyRuntimeDynamic, the devices will be added to the VG if they are present at runtime.
By default, the value is set to RuntimeDynamic.
enum:
- None
- RuntimeDynamic
type: string
devices:
description: Devices is the list of devices used by the volume
group
Expand Down
2 changes: 1 addition & 1 deletion hack/verify-catalog.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
set -euo pipefail

function print_failure {
echo "There are unexpected changes to the tree when running 'make catalog-render'. Please"
echo "There are unexpected changes to the tree when running 'make catalog'. Please"
echo "run these commands locally and double-check the Git repository for unexpected changes which may"
echo "need to be committed."
exit 1
Expand Down
19 changes: 11 additions & 8 deletions internal/controllers/vgmanager/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -266,8 +266,9 @@ func testVGWithLocalDevice(ctx context.Context, vgTemplate lvmv1alpha1.LVMVolume
Expect(instances.client.Get(ctx, client.ObjectKeyFromObject(nodeStatus), nodeStatus)).To(Succeed())
Expect(nodeStatus.Spec.LVMVGStatus).ToNot(BeEmpty())
Expect(nodeStatus.Spec.LVMVGStatus).To(ContainElement(lvmv1alpha1.VGStatus{
Name: vg.GetName(),
Status: lvmv1alpha1.VGStatusProgressing,
Name: vg.GetName(),
Status: lvmv1alpha1.VGStatusProgressing,
DeviceDiscoveryPolicy: lvmv1alpha1.DeviceDiscoveryPolicyPreConfigured,
}))
})

Expand Down Expand Up @@ -365,9 +366,10 @@ func testVGWithLocalDevice(ctx context.Context, vgTemplate lvmv1alpha1.LVMVolume
Expect(instances.client.Get(ctx, client.ObjectKeyFromObject(nodeStatus), nodeStatus)).To(Succeed())
Expect(nodeStatus.Spec.LVMVGStatus).ToNot(BeEmpty())
Expect(nodeStatus.Spec.LVMVGStatus).To(ContainElement(lvmv1alpha1.VGStatus{
Name: vg.GetName(),
Status: lvmv1alpha1.VGStatusReady,
Devices: []string{device},
Name: vg.GetName(),
Status: lvmv1alpha1.VGStatusReady,
Devices: []string{device},
DeviceDiscoveryPolicy: lvmv1alpha1.DeviceDiscoveryPolicyPreConfigured,
}))
oldReadyGeneration = nodeStatus.GetGeneration()
})
Expand Down Expand Up @@ -422,9 +424,10 @@ func testVGWithLocalDevice(ctx context.Context, vgTemplate lvmv1alpha1.LVMVolume
Expect(instances.client.Get(ctx, client.ObjectKeyFromObject(nodeStatus), nodeStatus)).To(Succeed())
Expect(nodeStatus.Spec.LVMVGStatus).ToNot(BeEmpty())
Expect(nodeStatus.Spec.LVMVGStatus).To(ContainElement(lvmv1alpha1.VGStatus{
Name: vg.GetName(),
Status: lvmv1alpha1.VGStatusReady,
Devices: []string{device},
Name: vg.GetName(),
Status: lvmv1alpha1.VGStatusReady,
Devices: []string{device},
DeviceDiscoveryPolicy: lvmv1alpha1.DeviceDiscoveryPolicyPreConfigured,
}))
Expect(oldReadyGeneration).To(Equal(nodeStatus.GetGeneration()))
})
Expand Down
6 changes: 6 additions & 0 deletions internal/controllers/vgmanager/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ func (r *Reconciler) setVolumeGroupFailedStatus(ctx context.Context, vg *lvmv1al
func (r *Reconciler) setVolumeGroupStatus(ctx context.Context, vg *lvmv1alpha1.LVMVolumeGroup, status *lvmv1alpha1.VGStatus) (bool, error) {
logger := log.FromContext(ctx).WithValues("VolumeGroup", client.ObjectKeyFromObject(vg))

if vg.Spec.DeviceSelector == nil {
status.DeviceDiscoveryPolicy = lvmv1alpha1.DeviceDiscoveryPolicyRuntimeDynamic
} else {
status.DeviceDiscoveryPolicy = lvmv1alpha1.DeviceDiscoveryPolicyPreConfigured
}

// Get LVMVolumeGroupNodeStatus and set the relevant VGStatus
nodeStatus := r.getLVMVolumeGroupNodeStatus()

Expand Down

0 comments on commit 5fde32c

Please sign in to comment.