From 6b2de1dbf686a030d4fcadf636c5b0a4726985ed Mon Sep 17 00:00:00 2001 From: Vicente Cheng <vicente.cheng@suse.com> Date: Tue, 23 Jul 2024 00:49:04 +0800 Subject: [PATCH] controller: support lvm type `dm-thin` - also drop linear/mirror Signed-off-by: Vicente Cheng <vicente.cheng@suse.com> --- cmd/provisioner/createsnap.go | 10 +++- pkg/lvm/controllerserver.go | 5 +- pkg/lvm/lvm.go | 93 ++++++++++++++++++++++++++++------- 3 files changed, 87 insertions(+), 21 deletions(-) diff --git a/cmd/provisioner/createsnap.go b/cmd/provisioner/createsnap.go index 7f045d81..f865aedc 100644 --- a/cmd/provisioner/createsnap.go +++ b/cmd/provisioner/createsnap.go @@ -30,6 +30,10 @@ func createSnapCmd() *cli.Command { Name: flagLVName, Usage: "Required. the name of the volumegroup", }, + &cli.StringFlag{ + Name: flagLVMType, + Usage: "Required. the type of the source lvm", + }, }, Action: func(c *cli.Context) error { if err := createSnap(c); err != nil { @@ -58,6 +62,10 @@ func createSnap(c *cli.Context) error { if snapName == "" { return fmt.Errorf("invalid empty flag %v", flagLVMType) } + lvType := c.String(flagLVMType) + if lvType == "" { + return fmt.Errorf("invalid empty flag %v", flagLVMType) + } klog.Infof("create snapshot: %s source size: %d source lv: %s/%s", snapName, lvSize, vgName, lvName) @@ -69,7 +77,7 @@ func createSnap(c *cli.Context) error { } } - output, err := lvm.CreateSnapshot(snapName, lvName, vgName, lvSize) + output, err := lvm.CreateSnapshot(snapName, lvName, vgName, lvSize, lvType) if err != nil { return fmt.Errorf("unable to create Snapshot: %w output:%s", err, output) } diff --git a/pkg/lvm/controllerserver.go b/pkg/lvm/controllerserver.go index 0f38d32b..036c2a38 100644 --- a/pkg/lvm/controllerserver.go +++ b/pkg/lvm/controllerserver.go @@ -116,7 +116,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol } lvmType := req.GetParameters()["type"] - if !(lvmType == "linear" || lvmType == "mirror" || lvmType == "striped") { + if !(lvmType == "striped" || lvmType == "dm-thin") { return nil, status.Errorf(codes.Internal, "lvmType is incorrect: %s", lvmType) } @@ -430,6 +430,7 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS ns := volume.Spec.NodeAffinity.Required.NodeSelectorTerms node := ns[0].MatchExpressions[0].Values[0] vgName := volume.Spec.CSI.VolumeAttributes["vgName"] + lvType := volume.Spec.CSI.VolumeAttributes["type"] //snapSize := strconv.FormatUint(volume.Spec.CSI.VolumeAttributes["RequiredBytes"], 10) snapSizeStr := volume.Spec.CSI.VolumeAttributes["RequiredBytes"] snapSize, err := strconv.ParseInt(snapSizeStr, 10, 64) @@ -449,6 +450,7 @@ func (cs *controllerServer) CreateSnapshot(ctx context.Context, req *csi.CreateS nodeName: node, snapSize: snapSize, vgName: vgName, + lvType: lvType, hostWritePath: cs.hostWritePath, kubeClient: cs.kubeClient, namespace: cs.namespace, @@ -503,6 +505,7 @@ func (cs *controllerServer) DeleteSnapshot(ctx context.Context, req *csi.DeleteS snapshotName: snapName, nodeName: node, vgName: vgName, + lvType: "", // not used hostWritePath: cs.hostWritePath, kubeClient: cs.kubeClient, namespace: cs.namespace, diff --git a/pkg/lvm/lvm.go b/pkg/lvm/lvm.go index db3ce685..238ed783 100644 --- a/pkg/lvm/lvm.go +++ b/pkg/lvm/lvm.go @@ -86,13 +86,13 @@ type snapshotAction struct { kubeClient kubernetes.Clientset namespace string vgName string + lvType string hostWritePath string } const ( - linearType = "linear" stripedType = "striped" - mirrorType = "mirror" + dmThinType = "dm-thin" actionTypeCreate = "create" actionTypeDelete = "delete" actionTypeClone = "clone" @@ -279,7 +279,7 @@ func createSnapshotterPod(ctx context.Context, sa snapshotAction) (err error) { args := []string{} switch sa.action { case actionTypeCreate: - args = append(args, "createsnap", "--snapname", sa.snapshotName, "--lvname", sa.srcVolName, "--vgname", sa.vgName, "--lvsize", fmt.Sprintf("%d", sa.snapSize)) + args = append(args, "createsnap", "--snapname", sa.snapshotName, "--lvname", sa.srcVolName, "--vgname", sa.vgName, "--lvsize", fmt.Sprintf("%d", sa.snapSize), "--lvmtype", sa.lvType) case actionTypeDelete: args = append(args, "deletesnap", "--snapname", sa.snapshotName, "--vgname", sa.vgName) default: @@ -436,40 +436,51 @@ func CreateLVS(vg string, name string, size uint64, lvmType string) (string, err return "", fmt.Errorf("size must be greater than 0") } - if !(lvmType == "linear" || lvmType == "mirror" || lvmType == "striped") { - return "", fmt.Errorf("lvmType is incorrect: %s", lvmType) - } - // TODO: check available capacity, fail if request doesn't fit - args := []string{"-v", "--yes", "-n", name, "-W", "y", "-L", fmt.Sprintf("%db", size)} + executor := cmd.NewExecutor() + thinPoolName := "" + // we need to create thin pool first if the lvmType is dm-thin + if lvmType == dmThinType { + thinPoolName = fmt.Sprintf("%s-thinpool", vg) + found, err := getThinPool(vg, thinPoolName) + if err != nil { + return "", fmt.Errorf("unable to determine if thinpool exists: %w", err) + } + if !found { + args := []string{"-l90%FREE", "--thinpool", thinPoolName, vg} + klog.Infof("lvcreate %s", args) + _, err := executor.Execute("lvcreate", args) + if err != nil { + return "", fmt.Errorf("unable to create thinpool: %w", err) + } + } + } + + args := []string{"-v", "--yes", "-n", name, "-W", "y"} pvs, err := pvCount(vg) if err != nil { return "", fmt.Errorf("unable to determine pv count of vg: %w", err) } - if pvs < 2 { - klog.Warning("pvcount is <2 only linear is supported") - lvmType = linearType + if pvs < 2 && lvmType == stripedType { + klog.Warning("pvcount is <2, the striped does not meaningful.") } switch lvmType { case stripedType: - args = append(args, "--type", "striped", "--stripes", fmt.Sprintf("%d", pvs)) - case mirrorType: - args = append(args, "--type", "raid1", "--mirrors", "1", "--nosync") - case linearType: + args = append(args, "-L", fmt.Sprintf("%db", size), "--type", "striped", "--stripes", fmt.Sprintf("%d", pvs), vg) + case dmThinType: + args = append(args, "-V", fmt.Sprintf("%db", size), "--thin-pool", thinPoolName, vg) default: return "", fmt.Errorf("unsupported lvmtype: %s", lvmType) } - executor := cmd.NewExecutor() tags := []string{"harvester-csi-lvm"} for _, tag := range tags { args = append(args, "--addtag", tag) } - args = append(args, vg) klog.Infof("lvcreate %s", args) out, err := executor.Execute("lvcreate", args) return out, err @@ -535,7 +546,7 @@ func RemoveLVS(name string) (string, error) { return out, err } -func CreateSnapshot(snapshotName, srcVolName, vgName string, volSize int64) (string, error) { +func CreateSnapshot(snapshotName, srcVolName, vgName string, volSize int64, lvType string) (string, error) { if snapshotName == "" || srcVolName == "" { return "", fmt.Errorf("invalid empty name or path") } @@ -552,7 +563,16 @@ func CreateSnapshot(snapshotName, srcVolName, vgName string, volSize int64) (str // Names starting "snapshot" are reserved for internal use by LVM // we patch new snapName as "lvm-<snapshotName>" snapshotName = fmt.Sprintf("lvm-%s", snapshotName) - args := []string{"-s", "-y", "-n", snapshotName, "-L", fmt.Sprintf("%db", volSize)} + args := []string{"-s", "-y", "-n", snapshotName} + switch lvType { + case stripedType: + args = append(args, "-L", fmt.Sprintf("%db", volSize)) + case dmThinType: + // no-size option for the dm-thin + break + default: + return "", fmt.Errorf("unsupported lvmtype: %s", lvType) + } args = append(args, fmt.Sprintf("/dev/%s/%s", vgName, srcVolName)) klog.Infof("lvcreate %s", args) out, err := executor.Execute("lvcreate", args) @@ -593,6 +613,41 @@ func pvCount(vgname string) (int, error) { return count, nil } +func getThinPool(vgName, thinpoolName string) (bool, error) { + executor := cmd.NewExecutor() + // we would like to get the segtype, name as below: + // thin thinvol01 <-- this is volume + // thin-pool vg02-thinpool <-- this is thin-pool + args := []string{"--noheadings", "-o", "segtype,name", vgName} + out, err := executor.Execute("lvs", args) + if err != nil { + klog.Infof("execute lvs %s, err: %v", args, err) + return false, err + } + lines := strings.Split(out, "\n") + // type[Name] + // type: thin -> vol name + // thin-pool -> pool name + thinInfo := make(map[string]string) + for _, line := range lines { + if line == "" { + continue + } + parts := strings.Fields(line) + if len(parts) != 2 { + klog.Warningf("unexpected output from lvs: %s", line) + continue + } + thinInfo[parts[0]] = parts[1] + } + for typeName, val := range thinInfo { + if typeName == "thin-pool" && val == thinpoolName { + return true, nil + } + } + return false, nil +} + func getRelatedVG(lvname string) (string, error) { executor := cmd.NewExecutor() // we would like to get the lvname, vgname as below: