Skip to content

Commit

Permalink
Merge pull request topolvm#916 from jakobmoellerdev/volume-health-mon…
Browse files Browse the repository at this point in the history
…itoring

feat: setup volume health monitoring based on lv_attr bits
  • Loading branch information
peng225 authored Sep 5, 2024
2 parents d278007 + f8ad966 commit f5c95d6
Show file tree
Hide file tree
Showing 16 changed files with 863 additions and 151 deletions.
1 change: 1 addition & 0 deletions docs/lvmd-protocol.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ Represents a logical volume.
| tags | [string](#string) | repeated | Tags to add to the volume during creation |
| size_bytes | [int64](#int64) | | Volume size in canonical CSI bytes. |
| path | [string](#string) | | Path to the lv as per lvm. |
| attr | [string](#string) | | Attributes of the lv. |



Expand Down
50 changes: 46 additions & 4 deletions internal/driver/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package driver
import (
"context"
"errors"
"fmt"
"io"
"os"
"path/filepath"
Expand All @@ -13,6 +14,7 @@ import (
"github.com/topolvm/topolvm"
"github.com/topolvm/topolvm/internal/driver/internal/k8s"
"github.com/topolvm/topolvm/internal/filesystem"
"github.com/topolvm/topolvm/internal/lvmd/command"
"github.com/topolvm/topolvm/pkg/lvmd/proto"
"golang.org/x/sys/unix"
"google.golang.org/grpc/codes"
Expand Down Expand Up @@ -368,10 +370,10 @@ func (s *nodeServerNoLocked) NodeGetVolumeStats(ctx context.Context, req *csi.No
}

var st unix.Stat_t
switch err := filesystem.Stat(volumePath, &st); err {
case unix.ENOENT:
switch err := filesystem.Stat(volumePath, &st); {
case errors.Is(err, unix.ENOENT):
return nil, status.Error(codes.NotFound, "Volume is not found at "+volumePath)
case nil:
case err == nil:
default:
return nil, status.Errorf(codes.Internal, "stat on %s was failed: %v", volumePath, err)
}
Expand Down Expand Up @@ -418,7 +420,26 @@ func (s *nodeServerNoLocked) NodeGetVolumeStats(ctx context.Context, req *csi.No
Available: int64(sfs.Ffree),
})
}
return &csi.NodeGetVolumeStatsResponse{Usage: usage}, nil

var lv *proto.LogicalVolume
lvr, err := s.k8sLVService.GetVolume(ctx, volumeID)
if err != nil {
return nil, err
}
lv, err = s.getLvFromContext(ctx, lvr.Spec.DeviceClass, volumeID)
if err != nil {
return nil, err
}
if lv == nil {
return nil, status.Errorf(codes.NotFound, "failed to find LV: %s", volumeID)
}

volumeCondition, err := getVolumeCondition(lv)
if err != nil {
return nil, status.Errorf(codes.Internal, err.Error())
}

return &csi.NodeGetVolumeStatsResponse{Usage: usage, VolumeCondition: volumeCondition}, nil
}

func (s *nodeServerNoLocked) NodeExpandVolume(ctx context.Context, req *csi.NodeExpandVolumeRequest) (*csi.NodeExpandVolumeResponse, error) {
Expand Down Expand Up @@ -499,6 +520,7 @@ func (s *nodeServerNoLocked) NodeGetCapabilities(context.Context, *csi.NodeGetCa
capabilities := []csi.NodeServiceCapability_RPC_Type{
csi.NodeServiceCapability_RPC_GET_VOLUME_STATS,
csi.NodeServiceCapability_RPC_EXPAND_VOLUME,
csi.NodeServiceCapability_RPC_VOLUME_CONDITION,
}

csiCaps := make([]*csi.NodeServiceCapability, len(capabilities))
Expand Down Expand Up @@ -527,3 +549,23 @@ func (s *nodeServerNoLocked) NodeGetInfo(ctx context.Context, req *csi.NodeGetIn
},
}, nil
}

func getVolumeCondition(lv *proto.LogicalVolume) (*csi.VolumeCondition, error) {
attr, err := command.ParsedLVAttr(lv.GetAttr())
if err != nil {
return nil, fmt.Errorf("failed to parse attributes returned from logical volume service: %w", err)
}
var volumeCondition csi.VolumeCondition
if err := attr.VerifyHealth(); err != nil {
volumeCondition = csi.VolumeCondition{
Abnormal: true,
Message: err.Error(),
}
} else {
volumeCondition = csi.VolumeCondition{
Abnormal: false,
Message: "volume is healthy and operating normally",
}
}
return &volumeCondition, nil
}
30 changes: 11 additions & 19 deletions internal/lvmd/command/lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ func (vg *VolumeGroup) convertLV(lv lv) *LogicalVolume {
size = lv.originSize
}

return newLogicalVolume(
return &LogicalVolume{
fullName(lv.name, vg),
lv.name,
lv.path,
vg,
Expand All @@ -192,7 +193,8 @@ func (vg *VolumeGroup) convertLV(lv lv) *LogicalVolume {
uint32(lv.major),
uint32(lv.minor),
lv.tags,
)
lv.attr,
}
}

// CreateVolume creates logical volume in this volume group.
Expand Down Expand Up @@ -429,22 +431,7 @@ type LogicalVolume struct {
devMajor uint32
devMinor uint32
tags []string
}

func newLogicalVolume(name, path string, vg *VolumeGroup, size uint64, origin, pool *string, major, minor uint32, tags []string) *LogicalVolume {
fullname := fullName(name, vg)
return &LogicalVolume{
fullname,
name,
path,
vg,
size,
origin,
pool,
major,
minor,
tags,
}
attr string
}

// Name returns a volume name.
Expand Down Expand Up @@ -508,11 +495,16 @@ func (l *LogicalVolume) MinorNumber() uint32 {
return l.devMinor
}

// Tags returns the tags member.
// Tags returns the tags of the logical volume.
func (l *LogicalVolume) Tags() []string {
return l.tags
}

// Attr returns the attr flag field of the logical volume.
func (l *LogicalVolume) Attr() string {
return l.attr
}

// ThinSnapshot takes a thin snapshot of a volume.
// The volume must be thinly-provisioned.
// snapshots can be created unconditionally.
Expand Down
Loading

0 comments on commit f5c95d6

Please sign in to comment.