Skip to content

Commit

Permalink
feat(lvm-driver): enable RAID support
Browse files Browse the repository at this point in the history
Add support for LVM2 RAID types and parameters, with sane defaults
for backwards compatibility. lvm-driver now assumes that a non-
specified RAID type corresponds to the previous default of linear
RAID, where data is packed onto disk until it runs out of space,
continuing to the next as necessary.

Tests have been added to cover the main supported RAID types (e.g.
raid0, raid1, raid5, raid6, and raid10), but technically any valid
LVM RAID type should work as well.

Fixes openebs#164

Signed-off-by: Nicholas Cioli <[email protected]>
  • Loading branch information
nicholascioli committed Sep 25, 2023
1 parent 45ebdf6 commit 5111243
Show file tree
Hide file tree
Showing 15 changed files with 504 additions and 3 deletions.
3 changes: 2 additions & 1 deletion buildscripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,13 @@ output_name="bin/${PNAME}/"$GOOS"_"$GOARCH"/"$CTLNAME
if [ $GOOS = "windows" ]; then
output_name+='.exe'
fi
env GOOS=$GOOS GOARCH=$GOARCH go build -ldflags \
env GOOS=$GOOS GOARCH=$GOARCH CGO_ENABLED=0 go build -ldflags \
"-X github.com/openebs/lvm-localpv/pkg/version.GitCommit=${GIT_COMMIT} \
-X main.CtlName='${CTLNAME}' \
-X github.com/openebs/lvm-localpv/pkg/version.Version=${VERSION} \
-X github.com/openebs/lvm-localpv/pkg/version.VersionMeta=${VERSION_META}"\
-o $output_name\
-installsuffix cgo \
./cmd

echo ""
Expand Down
2 changes: 1 addition & 1 deletion buildscripts/lvm-driver/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.

FROM alpine:3.14.8
FROM alpine:3.17.5
RUN apk add --no-cache lvm2 lvm2-extra util-linux device-mapper
RUN apk add --no-cache btrfs-progs xfsprogs xfsprogs-extra e2fsprogs e2fsprogs-extra
RUN apk add --no-cache ca-certificates libc6-compat
Expand Down
1 change: 1 addition & 0 deletions changelogs/unreleased/164-nicholascioli
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
add support for LVM raid options
35 changes: 35 additions & 0 deletions ci/ci-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ fi
LVM_SYSTEMID="openebs-ci-test-system"
LVM_CONFIG="global{system_id_source=lvmlocal}local{system_id=${LVM_SYSTEMID}}"

# RAID info for corresponding tests
RAID_COUNT=5

# Clean up generated resources for successive tests.
cleanup_loopdev() {
sudo losetup -l | grep '(deleted)' | awk '{print $1}' \
Expand Down Expand Up @@ -60,13 +63,28 @@ cleanup_foreign_lvmvg() {
cleanup_loopdev
}

cleanup_raidvg() {
sudo vgremove raidvg -y || true

for IMG in `seq ${RAID_COUNT}`
do
if [ -f /tmp/openebs_ci_raid_disk_${IMG}.img ]
then
rm /tmp/openebs_ci_raid_disk_${IMG}.img
fi
done

cleanup_loopdev
}

cleanup() {
set +e

echo "Cleaning up test resources"

cleanup_lvmvg
cleanup_foreign_lvmvg
cleanup_raidvg

kubectl delete pvc -n openebs lvmpv-pvc
kubectl delete -f "${SNAP_CLASS}"
Expand All @@ -93,10 +111,27 @@ foreign_disk="$(sudo losetup -f /tmp/openebs_ci_foreign_disk.img --show)"
sudo pvcreate "${foreign_disk}"
sudo vgcreate foreign_lvmvg "${foreign_disk}" --systemid="${LVM_SYSTEMID}"

# setup a RAID volume group
cleanup_raidvg
raid_disks=()
for IMG in `seq ${RAID_COUNT}`
do
truncate -s 1024G /tmp/openebs_ci_raid_disk_${IMG}.img
raid_disk="$(sudo losetup -f /tmp/openebs_ci_raid_disk_${IMG}.img --show)"
sudo pvcreate "${raid_disk}"

raid_disks+=("${raid_disk}")
done
sudo vgcreate raidvg "${raid_disks[@]}"

# install snapshot and thin volume module for lvm
sudo modprobe dm-snapshot
sudo modprobe dm_thin_pool

# install RAID modules for lvm
sudo modprobe dm_raid
sudo modprobe dm_integrity

# Prepare env for running BDD tests
# Minikube is already running
kubectl apply -f "${LVM_OPERATOR}"
Expand Down
48 changes: 48 additions & 0 deletions deploy/lvm-operator.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,47 @@ spec:
description: Capacity of the volume
minLength: 1
type: string
integrity:
description: Integrity specifies whether logical volumes should be
checked for integrity. If it is set to "yes", then the LVM LocalPV
Driver will enable DM integrity for the logical volume
enum:
- "yes"
- "no"
type: string
lvcreateoptions:
description: LvCreateOptions are extra options for creating a volume.
Options should be separated by ; e.g. "--vdo;--readahead;auto"
type: string
mirrors:
description: Mirrors specifies the mirror count for a RAID configuration.
minimum: 0
type: integer
nosync:
description: NoSync enables the `--nosync` option of a RAID volume.
If it is set to "yes", then LVM will skip drive sync when creating
the mirrors. Defaults to "no"
enum:
- "yes"
- "no"
type: string
ownerNodeID:
description: OwnerNodeID is the Node ID where the volume group is
present which is where the volume has been provisioned. OwnerNodeID
can not be edited after the volume has been provisioned.
minLength: 1
type: string
raidtype:
description: RaidType specifies the type of RAID for the logical volume.
Defaults to linear, if unspecified.
enum:
- linear
- raid0
- raid1
- raid5
- raid6
- raid10
type: string
shared:
description: Shared specifies whether the volume can be shared among
multiple pods. If it is not set to "yes", then the LVM LocalPV Driver
Expand All @@ -109,6 +144,18 @@ spec:
- "yes"
- "no"
type: string
stripecount:
description: StripeCount specifies the stripe count for a RAID configuration.
This is equal to the number of physical volumes to scatter the logical
volume
minimum: 0
type: integer
stripesize:
description: StripeSize specifies the size of a stripe for a RAID
configuration. Must be a power of 2 but must not exceed the physical
extent size
minimum: 0
type: integer
thinProvision:
description: ThinProvision specifies whether logical volumes can be
thinly provisioned. If it is set to "yes", then the LVM LocalPV
Expand All @@ -129,6 +176,7 @@ spec:
required:
- capacity
- ownerNodeID
- raidtype
- vgPattern
- volGroup
type: object
Expand Down
48 changes: 48 additions & 0 deletions deploy/yamls/lvmvolume-crd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,47 @@ spec:
description: Capacity of the volume
minLength: 1
type: string
integrity:
description: Integrity specifies whether logical volumes should be
checked for integrity. If it is set to "yes", then the LVM LocalPV
Driver will enable DM integrity for the logical volume
enum:
- "yes"
- "no"
type: string
lvcreateoptions:
description: LvCreateOptions are extra options for creating a volume.
Options should be separated by ; e.g. "--vdo;--readahead;auto"
type: string
mirrors:
description: Mirrors specifies the mirror count for a RAID configuration.
minimum: 0
type: integer
nosync:
description: NoSync enables the `--nosync` option of a RAID volume.
If it is set to "yes", then LVM will skip drive sync when creating
the mirrors. Defaults to "no"
enum:
- "yes"
- "no"
type: string
ownerNodeID:
description: OwnerNodeID is the Node ID where the volume group is
present which is where the volume has been provisioned. OwnerNodeID
can not be edited after the volume has been provisioned.
minLength: 1
type: string
raidtype:
description: RaidType specifies the type of RAID for the logical volume.
Defaults to linear, if unspecified.
enum:
- linear
- raid0
- raid1
- raid5
- raid6
- raid10
type: string
shared:
description: Shared specifies whether the volume can be shared among
multiple pods. If it is not set to "yes", then the LVM LocalPV Driver
Expand All @@ -88,6 +123,18 @@ spec:
- "yes"
- "no"
type: string
stripecount:
description: StripeCount specifies the stripe count for a RAID configuration.
This is equal to the number of physical volumes to scatter the logical
volume
minimum: 0
type: integer
stripesize:
description: StripeSize specifies the size of a stripe for a RAID
configuration. Must be a power of 2 but must not exceed the physical
extent size
minimum: 0
type: integer
thinProvision:
description: ThinProvision specifies whether logical volumes can be
thinly provisioned. If it is set to "yes", then the LVM LocalPV
Expand All @@ -108,6 +155,7 @@ spec:
required:
- capacity
- ownerNodeID
- raidtype
- vgPattern
- volGroup
type: object
Expand Down
49 changes: 49 additions & 0 deletions pkg/apis/openebs.io/lvm/v1alpha1/lvmvolume.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,55 @@ type VolumeInfo struct {
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=yes;no
ThinProvision string `json:"thinProvision,omitempty"`

// RaidType specifies the type of RAID for the logical volume.
// Defaults to linear, if unspecified.
// +kubebuilder:validation:Required
// +kubebuilder:validation:default=linear
// +kubebuilder:validation:Enum=linear;raid0;raid1;raid5;raid6;raid10
RaidType string `json:"raidtype"`

// Integrity specifies whether logical volumes should be checked for integrity.
// If it is set to "yes", then the LVM LocalPV Driver will enable DM integrity
// for the logical volume
// +kubebuilder:validation:Required
// +kubebuilder:validation:Enum=yes;no
Integrity string `json:"integrity,omitempty"`

// Mirrors specifies the mirror count for a RAID configuration.
// +kubebuilder:validation:Required
// +kubebuilder:validation:default=0
// +kubebuilder:validation:Minimum=0
Mirrors uint `json:"mirrors,omitempty"`

// NoSync enables the `--nosync` option of a RAID volume.
// If it is set to "yes", then LVM will skip drive sync when creating
// the mirrors. Defaults to "no"
// +kubebuilder:validation:Required
// +kubebuilder:validation:default=no
// +kubebuilder:validation:Enum=yes;no
NoSync string `json:"nosync,omitempty"`

// StripeCount specifies the stripe count for a RAID configuration.
// This is equal to the number of physical volumes to scatter the
// logical volume
// +kubebuilder:validation:Required
// +kubebuilder:validation:default=0
// +kubebuilder:validation:Minimum=0
StripeCount uint `json:"stripecount,omitempty"`

// StripeSize specifies the size of a stripe for a RAID configuration.
// Must be a power of 2 but must not exceed the physical extent size
// +kubebuilder:validation:Required
// +kubebuilder:validation:default=0
// +kubebuilder:validation:Minimum=0
StripeSize uint `json:"stripesize,omitempty"`

// LvCreateOptions are extra options for creating a volume.
// Options should be separated by ;
// e.g. "--vdo;--readahead;auto"
// +kubebuilder:validation:Required
LvCreateOptions string `json:"lvcreateoptions,omitempty"`
}

// VolStatus string that specifies the current state of the volume provisioning request.
Expand Down
82 changes: 82 additions & 0 deletions pkg/builder/volbuilder/volume.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,11 @@ limitations under the License.
package volbuilder

import (
"strconv"

"github.com/openebs/lib-csi/pkg/common/errors"
apis "github.com/openebs/lvm-localpv/pkg/apis/openebs.io/lvm/v1alpha1"
"k8s.io/apimachinery/pkg/api/resource"
)

// Builder is the builder object for LVMVolume
Expand Down Expand Up @@ -139,6 +142,85 @@ func (b *Builder) WithThinProvision(thinProvision string) *Builder {
return b
}

// WithRaidType sets the RAID type for all logical volumes in the volume group
func (b *Builder) WithRaidType(raidType string) *Builder {
b.volume.Object.Spec.RaidType = raidType
return b
}

// WithIntegrity sets where integrity is enable or not
func (b *Builder) WithIntegrity(integrity string) *Builder {
b.volume.Object.Spec.Integrity = integrity
return b
}

// WithMirrors sets the RAID mirror count
func (b *Builder) WithMirrors(mirrors string) *Builder {
mirrorCount, err := strconv.ParseUint(mirrors, 10, 32)
if err != nil {
b.errs = append(
b.errs,
errors.New(
"invalid mirror count: must be a positive 32-bit integer",
),
err,
)
}

b.volume.Object.Spec.Mirrors = uint(mirrorCount)
return b
}

// WithNoSync sets the `nosync` RAID option
func (b *Builder) WithNoSync(nosync string) *Builder {
b.volume.Object.Spec.NoSync = nosync
return b
}

func (b *Builder) WithStripeCount(stripes string) *Builder {
stripeCount, err := strconv.ParseUint(stripes, 10, 32)
if err != nil {
b.errs = append(
b.errs,
errors.New(
"invalid stripe count: must be a positive 32-bit integer",
),
err,
)
}

b.volume.Object.Spec.StripeCount = uint(stripeCount)
return b
}

// WithStripeSize sets the size of each stripe for a RAID volume
func (b *Builder) WithStripeSize(size string) *Builder {
stripeSize, err := resource.ParseQuantity(size)
if err != nil {
b.errs = append(
b.errs,
errors.New(
"invalid stripe size",
),
err,
)
}

value := stripeSize.Value()
if value < 0 {
b.errs = append(b.errs, errors.New("invalid stripe size: value must be positive"))
}

b.volume.Object.Spec.StripeSize = uint(value)
return b
}

// WithLvCreateOptions sets any additional LVM options used when creating a volume
func (b *Builder) WithLvCreateOptions(options string) *Builder {
b.volume.Object.Spec.LvCreateOptions = options
return b
}

// WithVolGroup sets volume group name for creating volume
func (b *Builder) WithVolGroup(vg string) *Builder {
if vg == "" {
Expand Down
Loading

0 comments on commit 5111243

Please sign in to comment.