Skip to content

Commit

Permalink
feat: create thin pool
Browse files Browse the repository at this point in the history
Use vgManager to create thin pool after volume group is created.

Signed-off-by: Santosh Pillai <[email protected]>
  • Loading branch information
sp98 committed Mar 30, 2022
1 parent 66f8c2e commit 063de1c
Show file tree
Hide file tree
Showing 2 changed files with 80 additions and 1 deletion.
42 changes: 42 additions & 0 deletions pkg/vgmanager/lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const (
vgExtendCmd = "/usr/sbin/vgextend"
vgRemoveCmd = "/usr/sbin/vgremove"
pvRemoveCmd = "/usr/sbin/pvremove"
lvCreateCmd = "/usr/sbin/lvcreate"
)

// vgsOutput represents the output of the `vgs --reportformat json` command
Expand All @@ -58,6 +59,18 @@ type pvsOutput struct {
} `json:"report"`
}

// lvsOutput represents the output of the `lvs --reportformat json` command
type lvsOutput struct {
Report []struct {
Lv []struct {
Name string `json:"lv_name"`
VgName string `json:"vg_name"`
PoolName string `json:"pool_lv"`
LvAttr string `json:"lv_attr"`
} `json:"pv"`
} `json:"report"`
}

// VolumeGroup represents a volume group of linux lvm.
type VolumeGroup struct {
// Name is the name of the volume group
Expand Down Expand Up @@ -214,6 +227,35 @@ func ListVolumeGroups(exec internal.Executor) ([]VolumeGroup, error) {
return vgList, nil
}

// ListLogicalVolumes returns list of logical volumes for a volume group
func ListLogicalVolumes(exec internal.Executor, vgName string) ([]string, error) {
res, err := GetLVSOutput(exec, vgName)
if err != nil {
return []string{}, err
}

lvs := []string{}
for _, report := range res.Report {
for _, lv := range report.Lv {
lvs = append(lvs, lv.Name)
}
}
return lvs, nil
}

// GetLVSOutput returns the output for `lvs` command in json format
func GetLVSOutput(exec internal.Executor, vgName string) (*lvsOutput, error) {
res := new(lvsOutput)
args := []string{
"lvs", "-S", fmt.Sprintf("vgname=%s", vgName), "--reportformat", "json",
}
if err := execute(exec, res, args...); err != nil {
return nil, err
}

return res, nil
}

func execute(exec internal.Executor, v interface{}, args ...string) error {
output, err := exec.ExecuteCommandWithOutputAsHost(lvmCmd, args...)
if err != nil {
Expand Down
39 changes: 38 additions & 1 deletion pkg/vgmanager/vgmanager_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"fmt"
"os"
"strings"
"time"

"github.com/go-logr/logr"
Expand All @@ -42,7 +43,8 @@ import (
)

const (
ControllerName = "vg-manager"
ControllerName = "vg-manager"
DefaultChunkSize = "512"
)

// SetupWithManager sets up the controller with the Manager.
Expand Down Expand Up @@ -210,6 +212,12 @@ func (r *VGReconciler) reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re
})
}

// Create thin pool
err = r.addThinPoolToVG(volumeGroup.Name, volumeGroup.Spec.ThinPoolConfig)
if err != nil {
r.Log.Error(err, "failed to create thin pool", "VGName", volumeGroup.Name)
}

// apply and save lvmconfig
// pass config to configChannel only if config has changed
if !cmp.Equal(existingLvmdConfig, lvmdConfig) {
Expand Down Expand Up @@ -244,6 +252,35 @@ func (r *VGReconciler) reconcile(ctx context.Context, req ctrl.Request) (ctrl.Re
return ctrl.Result{RequeueAfter: requeueAfter}, nil
}

func (r *VGReconciler) addThinPoolToVG(vgName string, config lvmv1alpha1.ThinPoolConfig) error {
resp, err := GetLVSOutput(r.executor, vgName)
if err != nil {
return fmt.Errorf("failed to list logical volumes in the volume group %q. %v", vgName, err)
}

for _, report := range resp.Report {
for _, lv := range report.Lv {
if lv.Name == config.Name {
if strings.Contains(lv.LvAttr, "t") {
r.Log.Info("lvm thinpool already exists", "VGName", vgName, "ThinPool", config.Name)
return nil
}

return fmt.Errorf("failed to create thin pool %q. Logical volume with same name already exists", config.Name)
}
}
}

args := []string{"-l", fmt.Sprintf("%d%%FREE", config.SizePercent), "-c", DefaultChunkSize, "-T", fmt.Sprintf("%s/%s", vgName, config.Name)}

_, err = r.executor.ExecuteCommandWithOutputAsHost(lvCreateCmd, args...)
if err != nil {
return fmt.Errorf("failed to create thin pool %q in the volume group %q. %v", config.Name, vgName, err)
}

return nil
}

func (r *VGReconciler) addDevicesToVG(vgName string, devices []internal.BlockDevice) error {
if len(devices) < 1 {
return fmt.Errorf("can't create vg %q with 0 devices", vgName)
Expand Down

0 comments on commit 063de1c

Please sign in to comment.