Skip to content

Commit

Permalink
feat: create thin pool
Browse files Browse the repository at this point in the history
Signed-off-by: Santosh Pillai <[email protected]>
  • Loading branch information
sp98 committed Apr 18, 2022
1 parent b7961c6 commit aaa080a
Show file tree
Hide file tree
Showing 2 changed files with 94 additions and 7 deletions.
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
59 changes: 52 additions & 7 deletions 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 @@ -43,7 +44,8 @@ import (
)

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

// SetupWithManager sets up the controller with the Manager.
Expand Down Expand Up @@ -188,16 +190,30 @@ func (r *VGReconciler) reconcile(ctx context.Context, req ctrl.Request, volumeGr
}

if !found {
lvmdConfig.DeviceClasses = append(lvmdConfig.DeviceClasses, &lvmd.DeviceClass{
Name: volumeGroup.Name,
VolumeGroup: volumeGroup.Name,
Default: true,
})
dc := &lvmd.DeviceClass{
Name: volumeGroup.Name,
VolumeGroup: volumeGroup.Name,
Default: true,
ThinPoolConfig: &lvmd.ThinPoolConfig{},
}

if volumeGroup.Spec.ThinPoolConfig != nil {
dc.Type = lvmd.TypeThin
dc.ThinPoolConfig.Name = volumeGroup.Spec.ThinPoolConfig.Name
dc.ThinPoolConfig.OverprovisionRatio = float64(volumeGroup.Spec.ThinPoolConfig.OverprovisionRatio)
}

lvmdConfig.DeviceClasses = append(lvmdConfig.DeviceClasses, dc)
}

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

// apply and save lvmconfig
// pass config to configChannel only if config has changed

if !cmp.Equal(existingLvmdConfig, lvmdConfig) {
err := saveLVMDConfig(lvmdConfig)
if err != nil {
Expand All @@ -224,6 +240,35 @@ func (r *VGReconciler) reconcile(ctx context.Context, req ctrl.Request, volumeGr
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) processDelete(ctx context.Context, volumeGroup *lvmv1alpha1.LVMVolumeGroup) error {

// Read the lvmd config file
Expand Down

0 comments on commit aaa080a

Please sign in to comment.