Skip to content

Commit

Permalink
(feat): ResizePhysicalVolume Layer
Browse files Browse the repository at this point in the history
  • Loading branch information
lasith-kg committed May 22, 2024
1 parent 58aa73d commit 459c90b
Show file tree
Hide file tree
Showing 9 changed files with 249 additions and 31 deletions.
1 change: 1 addition & 0 deletions cmd/ebs-bootstrap.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ func main() {
// LVM Layers
lvmLayers := []layer.Layer{
layer.NewCreatePhysicalVolumeLayer(db, lb),
layer.NewResizePhysicalVolumeLayer(lb),
layer.NewCreateVolumeGroupLayer(lb),
layer.NewCreateLogicalVolumeLayer(lb),
layer.NewActivateLogicalVolumeLayer(lb),
Expand Down
11 changes: 3 additions & 8 deletions configs/ubuntu.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,11 @@ defaults:
lvmConsumption: 100
devices:
/dev/vdb:
fs: xfs
lvm: ifmx-etc
mountPoint: /mnt/foo
user: ubuntu
group: ubuntu
permissions: 755
/dev/vdc:
fs: xfs
lvm: ifmx-var
mountPoint: /mnt/bar
mountPoint: /mnt/foo
user: ubuntu
group: ubuntu
permissions: 755
resize: true
resizeThreshold: 99
39 changes: 39 additions & 0 deletions internal/action/lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,3 +170,42 @@ func (a *ActivateLogicalVolumeAction) Refuse() string {
func (a *ActivateLogicalVolumeAction) Success() string {
return fmt.Sprintf("Successfully activated logical volume %s in volume group %s", a.name, a.volumeGroup)
}

type ResizePhysicalVolumeAction struct {
name string
mode model.Mode
lvmService service.LvmService
}

func NewResizePhysicalVolumeAction(name string, ls service.LvmService) *ResizePhysicalVolumeAction {
return &ResizePhysicalVolumeAction{
name: name,
mode: model.Empty,
lvmService: ls,
}
}

func (a *ResizePhysicalVolumeAction) Execute() error {
return a.lvmService.ResizePhysicalVolume(a.name)
}

func (a *ResizePhysicalVolumeAction) GetMode() model.Mode {
return a.mode
}

func (a *ResizePhysicalVolumeAction) SetMode(mode model.Mode) Action {
a.mode = mode
return a
}

func (a *ResizePhysicalVolumeAction) Prompt() string {
return fmt.Sprintf("Would you like to resize physical volume %s", a.name)
}

func (a *ResizePhysicalVolumeAction) Refuse() string {
return fmt.Sprintf("Refused to resize physical volume %s", a.name)
}

func (a *ResizePhysicalVolumeAction) Success() string {
return fmt.Sprintf("Successfully resized physical volume %s", a.name)
}
34 changes: 30 additions & 4 deletions internal/backend/lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ type LvmBackend interface {
GetLogicalVolume(name string, volumeGroup string) (*model.LogicalVolume, error)
SearchLogicalVolumes(volumeGroup string) []*model.LogicalVolume
SearchVolumeGroup(physicalVolume string) *model.VolumeGroup
ShouldResizePhysicalVolume(name string, threshold float64) bool
ResizePhysicalVolume(name string) action.Action
From(config *config.Config) error
}

Expand Down Expand Up @@ -78,6 +80,7 @@ func (lb *LinuxLvmBackend) SearchLogicalVolumes(volumeGroup string) []*model.Log
Name: lv.Name,
VolumeGroup: node.Name,
State: model.LogicalVolumeState(lv.State),
Size: lv.Size,
})
}
return lvs
Expand All @@ -95,6 +98,7 @@ func (lb *LinuxLvmBackend) SearchVolumeGroup(physicalVolume string) *model.Volum
return &model.VolumeGroup{
Name: vgn[0].Name,
PhysicalVolume: node.Name,
Size: vgn[0].Size,
}
}

Expand All @@ -114,28 +118,50 @@ func (lb *LinuxLvmBackend) ActivateLogicalVolume(name string, volumeGroup string
return action.NewActivateLogicalVolumeAction(name, volumeGroup, lb.lvmService)
}

func (lb *LinuxLvmBackend) ShouldResizePhysicalVolume(name string, threshold float64) bool {
node, err := lb.lvmGraph.GetPhysicalVolume(name)
if err != nil {
return false
}
dvn := lb.lvmGraph.GetParents(node, datastructures.Device)
if len(dvn) == 0 {
return false
}
return (float64(node.Size) / float64(dvn[0].Size) * 100) < threshold
}

func (lb *LinuxLvmBackend) ResizePhysicalVolume(name string) action.Action {
return action.NewResizePhysicalVolumeAction(name, lb.lvmService)
}

func (db *LinuxLvmBackend) From(config *config.Config) error {
ds, err := db.lvmService.GetDevices()
if err != nil {
return err
}
for _, d := range ds {
db.lvmGraph.AddDevice(d.Name, d.Size)
}
pvs, err := db.lvmService.GetPhysicalVolumes()
if err != nil {
return err
}
for _, pv := range pvs {
db.lvmGraph.AddBlockDevice(pv.Name)
db.lvmGraph.AddPhysicalVolume(pv.Name)
db.lvmGraph.AddPhysicalVolume(pv.Name, pv.Size)
}
vgs, err := db.lvmService.GetVolumeGroups()
if err != nil {
return err
}
for _, vg := range vgs {
db.lvmGraph.AddVolumeGroup(vg.Name, vg.PhysicalVolume)
db.lvmGraph.AddVolumeGroup(vg.Name, vg.PhysicalVolume, vg.Size)
}
lvs, err := db.lvmService.GetLogicalVolumes()
if err != nil {
return err
}
for _, lv := range lvs {
db.lvmGraph.AddLogicalVolume(lv.Name, lv.VolumeGroup, datastructures.LvmNodeState(lv.State))
db.lvmGraph.AddLogicalVolume(lv.Name, lv.VolumeGroup, datastructures.LvmNodeState(lv.State), lv.Size)
}
return nil
}
35 changes: 20 additions & 15 deletions internal/datastructures/lvm_graph.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type LvmNodeState int32
type LvmNodeCategory int32

const (
BlockDeviceActive LvmNodeState = 0b0000001
DeviceActive LvmNodeState = 0b0000001
PhysicalVolumeActive LvmNodeState = 0b0000010
VolumeGroupInactive LvmNodeState = 0b0000100
VolumeGroupActive LvmNodeState = 0b0001100
Expand All @@ -18,7 +18,7 @@ const (
)

const (
BlockDevice LvmNodeCategory = 0b0000001
Device LvmNodeCategory = 0b0000001
PhysicalVolume LvmNodeCategory = 0b0000010
VolumeGroup LvmNodeCategory = 0b0000100
LogicalVolume LvmNodeCategory = 0b0010000
Expand All @@ -28,45 +28,50 @@ type LvmNode struct {
id string
Name string
State LvmNodeState
Size uint64
children []*LvmNode
parents []*LvmNode
}

func NewBlockDevice(name string) *LvmNode {
func NewDevice(name string, size uint64) *LvmNode {
return &LvmNode{
id: fmt.Sprintf("device:%s", name),
Name: name,
State: BlockDeviceActive,
State: DeviceActive,
Size: size,
children: []*LvmNode{},
parents: []*LvmNode{},
}
}

func NewPhysicalVolume(name string) *LvmNode {
func NewPhysicalVolume(name string, size uint64) *LvmNode {
return &LvmNode{
id: fmt.Sprintf("pv:%s", name),
Name: name,
State: PhysicalVolumeActive,
Size: size,
children: []*LvmNode{},
parents: []*LvmNode{},
}
}

func NewVolumeGroup(name string) *LvmNode {
func NewVolumeGroup(name string, size uint64) *LvmNode {
return &LvmNode{
id: fmt.Sprintf("vg:%s", name),
Name: name,
State: VolumeGroupInactive,
Size: size,
children: []*LvmNode{},
parents: []*LvmNode{},
}
}

func NewLogicalVolume(name string, vg string, State LvmNodeState) *LvmNode {
func NewLogicalVolume(name string, vg string, State LvmNodeState, size uint64) *LvmNode {
return &LvmNode{
id: fmt.Sprintf("lv:%s:vg:%s", name, vg),
Name: name,
State: State,
Size: size,
children: []*LvmNode{},
parents: []*LvmNode{},
}
Expand All @@ -82,8 +87,8 @@ func NewLvmGraph() *LvmGraph {
}
}

func (lg *LvmGraph) AddBlockDevice(name string) error {
bd := NewBlockDevice(name)
func (lg *LvmGraph) AddDevice(name string, size uint64) error {
bd := NewDevice(name, size)

_, found := lg.nodes[bd.id]
if found {
Expand All @@ -94,8 +99,8 @@ func (lg *LvmGraph) AddBlockDevice(name string) error {
return nil
}

func (lg *LvmGraph) AddPhysicalVolume(name string) error {
pv := NewPhysicalVolume(name)
func (lg *LvmGraph) AddPhysicalVolume(name string, size uint64) error {
pv := NewPhysicalVolume(name, size)

_, found := lg.nodes[pv.id]
if found {
Expand All @@ -114,12 +119,12 @@ func (lg *LvmGraph) AddPhysicalVolume(name string) error {
return nil
}

func (lg *LvmGraph) AddVolumeGroup(name string, pv string) error {
func (lg *LvmGraph) AddVolumeGroup(name string, pv string, size uint64) error {
id := fmt.Sprintf("vg:%s", name)

vg, found := lg.nodes[id]
if !found {
vg = NewVolumeGroup(name)
vg = NewVolumeGroup(name, size)
}

pvId := fmt.Sprintf("pv:%s", pv)
Expand All @@ -138,8 +143,8 @@ func (lg *LvmGraph) AddVolumeGroup(name string, pv string) error {
return nil
}

func (lg *LvmGraph) AddLogicalVolume(name string, vg string, state LvmNodeState) error {
lv := NewLogicalVolume(name, vg, state)
func (lg *LvmGraph) AddLogicalVolume(name string, vg string, state LvmNodeState, size uint64) error {
lv := NewLogicalVolume(name, vg, state, size)

_, found := lg.nodes[lv.id]
if found {
Expand Down
90 changes: 90 additions & 0 deletions internal/layer/pv_resize.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package layer

import (
"fmt"

"github.com/reecetech/ebs-bootstrap/internal/action"
"github.com/reecetech/ebs-bootstrap/internal/backend"
"github.com/reecetech/ebs-bootstrap/internal/config"
)

const (
// The % threshold at which to resize a physical volume
// -------------------------------------------------------
// If the (physical volume / device size) * 100 falls
// under this threshold then we perform a resize operation
// -------------------------------------------------------
// The smallest gp3 EBS volume you can create is 1GiB (1073741824 bytes).
// The default size of the extent of a PV is 4 MiB (4194304 bytes).
// Typically, the first extent of a PV is reserved for metadata. This
// produces a PV of size 1069547520 bytes (Usage=99.6093%). We ensure
// that we set the resize threshold to 99.6% to ensure that a 1 GiB EBS
// volume won't be always resized
// -------------------------------------------------------
// Why not just look for a difference of 4194304 bytes?
// - The size of the extent can be changed by the user
// - Therefore we may not always see a difference of 4194304 bytes between
// the block device and physical volume size
ResizeThreshold = float64(99.6)
)

type ResizePhysicalVolumeLayer struct {
lvmBackend backend.LvmBackend
}

func NewResizePhysicalVolumeLayer(lb backend.LvmBackend) *ResizePhysicalVolumeLayer {
return &ResizePhysicalVolumeLayer{
lvmBackend: lb,
}
}

func (rpvl *ResizePhysicalVolumeLayer) Modify(c *config.Config) ([]action.Action, error) {
actions := make([]action.Action, 0)
for name, cd := range c.Devices {
if len(cd.Lvm) == 0 {
continue
}
if !c.GetResizeFs(name) {
continue
}
if !rpvl.lvmBackend.ShouldResizePhysicalVolume(name, ResizeThreshold) {
continue
}
mode := c.GetMode(name)
a := rpvl.lvmBackend.ResizePhysicalVolume(name)
actions = append(actions, a.SetMode(mode))
}
return actions, nil
}

func (rpvl *ResizePhysicalVolumeLayer) Validate(c *config.Config) error {
for name, cd := range c.Devices {
if len(cd.Lvm) == 0 {
continue
}
if !c.GetResizeFs(name) {
continue
}
if rpvl.lvmBackend.ShouldResizePhysicalVolume(name, ResizeThreshold) {
return fmt.Errorf("🔴 %s: Failed to resize physical volume", name)
}
}
return nil
}

func (rpvl *ResizePhysicalVolumeLayer) Warning() string {
return DisabledWarning
}

func (rpvl *ResizePhysicalVolumeLayer) From(c *config.Config) error {
return rpvl.lvmBackend.From(c)
}

func (rpvl *ResizePhysicalVolumeLayer) ShouldProcess(c *config.Config) bool {
for name, cd := range c.Devices {
if len(cd.Lvm) > 0 && c.GetResizeFs(name) {
return true
}
}
return false
}
8 changes: 8 additions & 0 deletions internal/model/lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,20 @@ package model

import "github.com/reecetech/ebs-bootstrap/internal/datastructures"

type Device struct {
Name string
Size uint64
}

type PhysicalVolume struct {
Name string
Size uint64
}

type VolumeGroup struct {
Name string
PhysicalVolume string
Size uint64
}

type LogicalVolumeState int32
Expand All @@ -23,4 +30,5 @@ type LogicalVolume struct {
Name string
VolumeGroup string
State LogicalVolumeState
Size uint64
}
Loading

0 comments on commit 459c90b

Please sign in to comment.