diff --git a/docs/lvmd-protocol.md b/docs/lvmd-protocol.md index b19602a97..e394c8034 100644 --- a/docs/lvmd-protocol.md +++ b/docs/lvmd-protocol.md @@ -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. | diff --git a/internal/driver/node.go b/internal/driver/node.go index 7da5ad125..0a12c8ebd 100644 --- a/internal/driver/node.go +++ b/internal/driver/node.go @@ -3,6 +3,7 @@ package driver import ( "context" "errors" + "fmt" "io" "os" "path/filepath" @@ -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" @@ -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) } @@ -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) { @@ -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)) @@ -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 +} diff --git a/internal/lvmd/command/lvm.go b/internal/lvmd/command/lvm.go index c6af33ad7..3a2fe6271 100644 --- a/internal/lvmd/command/lvm.go +++ b/internal/lvmd/command/lvm.go @@ -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, @@ -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. @@ -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. @@ -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. diff --git a/internal/lvmd/command/lvm_lv_attr.go b/internal/lvmd/command/lvm_lv_attr.go new file mode 100644 index 000000000..81d2f2f6c --- /dev/null +++ b/internal/lvmd/command/lvm_lv_attr.go @@ -0,0 +1,283 @@ +package command + +import ( + "errors" + "fmt" +) + +var ( + ErrPartialActivation = errors.New("found partial activation of physical volumes, one or more physical volumes are setup incorrectly") + ErrUnknownVolumeHealth = errors.New("unknown volume health reported, verification on the host system is required") + ErrWriteCacheError = errors.New("write cache error signifies that dm-writecache reports an error") + ErrThinPoolFailed = errors.New("thin pool encounters serious failures and hence no further I/O is permitted at all") + ErrThinPoolOutOfDataSpace = errors.New("thin pool is out of data space, no further data can be written to the thin pool without extension") + ErrThinPoolMetadataReadOnly = errors.New("metadata read only signifies that thin pool encounters certain types of failures, but it's still possible to do data reads. However, no metadata changes are allowed") + ErrThinVolumeFailed = errors.New("the underlying thin pool entered a failed state and no further I/O is permitted") + ErrRAIDRefreshNeeded = errors.New("RAID volume requires a refresh, one or more Physical Volumes have suffered a write error. This could be due to temporary failure of the Physical Volume or an indication it is failing. The device should be refreshed or replaced") + ErrRAIDMismatchesExist = errors.New("RAID volume has portions of the array that are not coherent. Inconsistencies are detected by initiating a check RAID logical volume. The scrubbing operations, \"check\" and \"repair\", can be performed on a RAID volume via the \"lvchange\" command") + ErrRAIDReshaping = errors.New("RAID volume is currently reshaping. Reshaping signifies a RAID Logical Volume is either undergoing a stripe addition/removal, a stripe size or RAID algorithm change") + ErrRAIDReshapeRemoved = errors.New("RAID volume signifies freed raid images after reshaping") + ErrRAIDWriteMostly = errors.New("RAID volume is marked as write-mostly. this signifies the devices in a RAID 1 logical volume have been marked write-mostly. This means that reading from this device will be avoided, and other devices will be preferred for reading (unless no other devices are available). This minimizes the I/O to the specified device") + ErrLogicalVolumeSuspended = errors.New("logical volume is in a suspended state, no I/O is permitted") + ErrInvalidSnapshot = errors.New("logical volume is an invalid snapshot, no I/O is permitted") + ErrSnapshotMergeFailed = errors.New("snapshot merge failed, no I/O is permitted") + ErrMappedDevicePresentWithInactiveTables = errors.New("mapped device present with inactive tables, no I/O is permitted") + ErrMappedDevicePresentWithoutTables = errors.New("mapped device present without tables, no I/O is permitted") + ErrThinPoolCheckNeeded = errors.New("a thin pool check is needed") + ErrUnknownVolumeState = errors.New("unknown volume state, verification on the host system is required") + ErrHistoricalVolumeState = errors.New("historical volume state (volume no longer exists but is kept around in logs), verification on the host system is required") + ErrLogicalVolumeUnderlyingDeviceStateUnknown = errors.New("logical volume underlying device state is unknown, verification on the host system is required") +) + +type VolumeType rune + +const ( + VolumeTypeMirrored VolumeType = 'm' + VolumeTypeMirroredNoInitialSync VolumeType = 'M' + VolumeTypeOrigin VolumeType = 'o' + VolumeTypeOriginWithMergingSnapshot VolumeType = 'O' + VolumeTypeRAID VolumeType = 'r' + VolumeTypeRAIDNoInitialSync VolumeType = 'R' + VolumeTypeSnapshot VolumeType = 's' + VolumeTypeMergingSnapshot VolumeType = 'S' + VolumeTypePVMove VolumeType = 'p' + VolumeTypeVirtual VolumeType = 'v' + VolumeTypeMirrorOrRAIDImage VolumeType = 'i' + VolumeTypeMirrorOrRAIDImageOutOfSync VolumeType = 'I' + VolumeTypeMirrorLogDevice VolumeType = 'l' + VolumeTypeUnderConversion VolumeType = 'c' + VolumeTypeThinVolume VolumeType = 'V' + VolumeTypeThinPool VolumeType = 't' + VolumeTypeThinPoolData VolumeType = 'T' + VolumeTypeThinPoolMetadata VolumeType = 'e' + VolumeTypeDefault VolumeType = '-' +) + +type Permissions rune + +const ( + PermissionsWriteable Permissions = 'w' + PermissionsReadOnly Permissions = 'r' + PermissionsReadOnlyActivationOfNonReadOnlyVolume Permissions = 'R' + PermissionsNone Permissions = '-' +) + +type AllocationPolicy rune + +const ( + AllocationPolicyAnywhere AllocationPolicy = 'a' + AllocationPolicyAnywhereLocked AllocationPolicy = 'A' + AllocationPolicyContiguous AllocationPolicy = 'c' + AllocationPolicyContiguousLocked AllocationPolicy = 'C' + AllocationPolicyInherited AllocationPolicy = 'i' + AllocationPolicyInheritedLocked AllocationPolicy = 'I' + AllocationPolicyCling AllocationPolicy = 'l' + AllocationPolicyClingLocked AllocationPolicy = 'L' + AllocationPolicyNormal AllocationPolicy = 'n' + AllocationPolicyNormalLocked AllocationPolicy = 'N' + AllocationPolicyNone AllocationPolicy = '-' +) + +type Minor rune + +const ( + MinorTrue Minor = 'm' + MinorFalse Minor = '-' +) + +type State rune + +const ( + StateActive State = 'a' + StateSuspended State = 's' + StateInvalidSnapshot State = 'I' + StateSuspendedSnapshot State = 'S' + StateSnapshotMergeFailed State = 'm' + StateSuspendedSnapshotMergeFailed State = 'M' + StateMappedDevicePresentWithoutTables State = 'd' + StateMappedDevicePresentWithInactiveTables State = 'i' + StateNone State = '-' + StateHistorical State = 'h' + StateThinPoolCheckNeeded State = 'c' + StateSuspendedThinPoolCheckNeeded State = 'C' + StateUnknown State = 'X' +) + +type Open rune + +const ( + OpenTrue Open = 'o' + OpenFalse Open = '-' + OpenUnknown Open = 'X' +) + +type OpenTarget rune + +const ( + OpenTargetMirror OpenTarget = 'm' + OpenTargetRaid OpenTarget = 'r' + OpenTargetSnapshot OpenTarget = 's' + OpenTargetThin OpenTarget = 't' + OpenTargetUnknown OpenTarget = 'u' + OpenTargetVirtual OpenTarget = 'v' + OpenTargetNone OpenTarget = '-' +) + +type Zero rune + +const ( + ZeroTrue Zero = 'z' + ZeroFalse Zero = '-' +) + +type VolumeHealth rune + +const ( + VolumeHealthPartialActivation VolumeHealth = 'p' + VolumeHealthUnknown VolumeHealth = 'X' + VolumeHealthOK VolumeHealth = '-' + VolumeHealthRAIDRefreshNeeded VolumeHealth = 'r' + VolumeHealthRAIDMismatchesExist VolumeHealth = 'm' + VolumeHealthRAIDWriteMostly VolumeHealth = 'w' + VolumeHealthRAIDReshaping VolumeHealth = 's' + VolumeHealthRAIDReshapeRemoved VolumeHealth = 'R' + VolumeHealthThinFailed VolumeHealth = 'F' + VolumeHealthThinPoolOutOfDataSpace VolumeHealth = 'D' + VolumeHealthThinPoolMetadataReadOnly VolumeHealth = 'M' + VolumeHealthWriteCacheError VolumeHealth = 'E' +) + +type SkipActivation rune + +const ( + SkipActivationTrue SkipActivation = 'k' + SkipActivationFalse SkipActivation = '-' +) + +// LVAttr has mapped lv_attr information, see https://linux.die.net/man/8/lvs +// It is a complete parsing of the entire attribute byte flags that is attached to each LV. +// This is useful when attaching logic to the state of an LV as the state of an LV can be determined +// from the Attributes, e.g. for determining whether an LV is considered a Thin-Pool or not. +type LVAttr struct { + VolumeType + Permissions + AllocationPolicy + Minor + State + Open + OpenTarget + Zero + VolumeHealth + SkipActivation +} + +const lvAttrLength = 10 + +func ParsedLVAttr(raw string) (*LVAttr, error) { + if len(raw) != lvAttrLength { + return nil, fmt.Errorf("%s is an invalid length lv_attr, expected %v, but got %v", + raw, lvAttrLength, len(raw)) + } + return &LVAttr{ + VolumeType(raw[0]), + Permissions(raw[1]), + AllocationPolicy(raw[2]), + Minor(raw[3]), + State(raw[4]), + Open(raw[5]), + OpenTarget(raw[6]), + Zero(raw[7]), + VolumeHealth(raw[8]), + SkipActivation(raw[9]), + }, nil +} + +func (l *LVAttr) String() string { + return fmt.Sprintf( + "%c%c%c%c%c%c%c%c%c%c", + l.VolumeType, + l.Permissions, + l.AllocationPolicy, + l.Minor, + l.State, + l.Open, + l.OpenTarget, + l.Zero, + l.VolumeHealth, + l.SkipActivation, + ) +} + +// VerifyHealth checks the health of the logical volume based on the attributes, mainly +// bit 9 (volume health indicator) based on bit 1 (volume type indicator) +// All failed known states are reported with an error message. +func (l *LVAttr) VerifyHealth() error { + if l.VolumeHealth == VolumeHealthPartialActivation { + return ErrPartialActivation + } + if l.VolumeHealth == VolumeHealthUnknown { + return ErrUnknownVolumeHealth + } + if l.VolumeHealth == VolumeHealthWriteCacheError { + return ErrWriteCacheError + } + + if l.VolumeType == VolumeTypeThinPool { + switch l.VolumeHealth { + case VolumeHealthThinFailed: + return ErrThinPoolFailed + case VolumeHealthThinPoolOutOfDataSpace: + return ErrThinPoolOutOfDataSpace + case VolumeHealthThinPoolMetadataReadOnly: + return ErrThinPoolMetadataReadOnly + } + } + + if l.VolumeType == VolumeTypeThinVolume { + switch l.VolumeHealth { + case VolumeHealthThinFailed: + return ErrThinVolumeFailed + } + } + + if l.VolumeType == VolumeTypeRAID || l.VolumeType == VolumeTypeRAIDNoInitialSync { + switch l.VolumeHealth { + case VolumeHealthRAIDRefreshNeeded: + return ErrRAIDRefreshNeeded + case VolumeHealthRAIDMismatchesExist: + return ErrRAIDMismatchesExist + case VolumeHealthRAIDReshaping: + return ErrRAIDReshaping + case VolumeHealthRAIDReshapeRemoved: + return ErrRAIDReshapeRemoved + case VolumeHealthRAIDWriteMostly: + return ErrRAIDWriteMostly + } + } + + switch l.State { + case StateSuspended, StateSuspendedSnapshot: + return ErrLogicalVolumeSuspended + case StateInvalidSnapshot: + return ErrInvalidSnapshot + case StateSnapshotMergeFailed, StateSuspendedSnapshotMergeFailed: + return ErrSnapshotMergeFailed + case StateMappedDevicePresentWithInactiveTables: + return ErrMappedDevicePresentWithInactiveTables + case StateMappedDevicePresentWithoutTables: + return ErrMappedDevicePresentWithoutTables + case StateThinPoolCheckNeeded, StateSuspendedThinPoolCheckNeeded: + return ErrThinPoolCheckNeeded + case StateUnknown: + return ErrUnknownVolumeState + case StateHistorical: + return ErrHistoricalVolumeState + } + + switch l.Open { + case OpenUnknown: + return ErrLogicalVolumeUnderlyingDeviceStateUnknown + } + + return nil +} diff --git a/internal/lvmd/command/lvm_lv_attr_test.go b/internal/lvmd/command/lvm_lv_attr_test.go new file mode 100644 index 000000000..04c0c47be --- /dev/null +++ b/internal/lvmd/command/lvm_lv_attr_test.go @@ -0,0 +1,246 @@ +package command + +import ( + "errors" + "fmt" + "strings" + "testing" +) + +func TestParsedLVAttr(t *testing.T) { + noError := func(t *testing.T, err error, args ...string) bool { + if err != nil { + t.Helper() + out := fmt.Sprintf("received unexpected error: %v", err) + if len(args) > 0 { + out = fmt.Sprintf("%s, %v", out, strings.Join(args, ",")) + } + t.Error(out) + } + + return true + } + + type args struct { + raw string + } + tests := []struct { + name string + args args + want LVAttr + wantErr func(*testing.T, error, ...string) bool + }{ + { + "Basic LV", + args{raw: "-wi-a-----"}, + LVAttr{ + VolumeType: VolumeTypeDefault, + Permissions: PermissionsWriteable, + AllocationPolicy: AllocationPolicyInherited, + Minor: MinorFalse, + State: StateActive, + Open: OpenFalse, + OpenTarget: OpenTargetNone, + Zero: ZeroFalse, + VolumeHealth: VolumeHealthOK, + SkipActivation: SkipActivationFalse, + }, + noError, + }, + { + "RAID Config without Initial Sync", + args{raw: "Rwi-a-r---"}, + LVAttr{ + VolumeType: VolumeTypeRAIDNoInitialSync, + Permissions: PermissionsWriteable, + AllocationPolicy: AllocationPolicyInherited, + Minor: MinorFalse, + State: StateActive, + Open: OpenFalse, + OpenTarget: OpenTargetRaid, + Zero: ZeroFalse, + VolumeHealth: VolumeHealthOK, + SkipActivation: SkipActivationFalse, + }, + noError, + }, + { + "ThinPool with Zeroing", + args{raw: "twi-a-tz--"}, + LVAttr{ + VolumeType: VolumeTypeThinPool, + Permissions: PermissionsWriteable, + AllocationPolicy: AllocationPolicyInherited, + Minor: MinorFalse, + State: StateActive, + Open: OpenFalse, + OpenTarget: OpenTargetThin, + Zero: ZeroTrue, + VolumeHealth: VolumeHealthOK, + SkipActivation: SkipActivationFalse, + }, + noError, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := ParsedLVAttr(tt.args.raw) + if !tt.wantErr(t, err, fmt.Sprintf("ParsedLVAttr(%v)", tt.args.raw)) { + return + } + if (&tt.want).String() != got.String() { + t.Errorf("ParsedLVAttr() = %v, want %v, raw %v", got, tt.want, tt.args.raw) + } + }) + } +} + +func TestVerifyHealth(t *testing.T) { + tests := []struct { + name string + rawAttr string + wantErr error + }{ + { + name: "Partial Activation", + rawAttr: "--------p-", + wantErr: ErrPartialActivation, + }, + { + name: "Unknown Volume Health", + rawAttr: "--------X-", + wantErr: ErrUnknownVolumeHealth, + }, + { + name: "Write Cache Error", + rawAttr: "--------E-", + wantErr: ErrWriteCacheError, + }, + { + name: "Thin Pool Failed", + rawAttr: "t-------F-", + wantErr: ErrThinPoolFailed, + }, + { + name: "Thin Pool Out of Data Space", + rawAttr: "t-------D-", + wantErr: ErrThinPoolOutOfDataSpace, + }, + { + name: "Thin Volume Failed", + rawAttr: "V-------F-", + wantErr: ErrThinVolumeFailed, + }, + { + name: "RAID Refresh Needed", + rawAttr: "r-------r-", + wantErr: ErrRAIDRefreshNeeded, + }, + { + name: "RAID Mismatches Exist", + rawAttr: "r-------m-", + wantErr: ErrRAIDMismatchesExist, + }, + { + name: "RAID Reshaping", + rawAttr: "r-------s-", + wantErr: ErrRAIDReshaping, + }, + { + name: "RAID Reshape Removed", + rawAttr: "r-------R-", + wantErr: ErrRAIDReshapeRemoved, + }, + { + name: "RAID Write Mostly", + rawAttr: "r-------w-", + wantErr: ErrRAIDWriteMostly, + }, + { + name: "Logical Volume Suspended", + rawAttr: "----s-----", + wantErr: ErrLogicalVolumeSuspended, + }, + { + name: "Invalid Snapshot", + rawAttr: "----I-----", + wantErr: ErrInvalidSnapshot, + }, + { + name: "Snapshot Merge Failed", + rawAttr: "----m-----", + wantErr: ErrSnapshotMergeFailed, + }, + { + name: "Mapped Device Present With Inactive Tables", + rawAttr: "----i-----", + wantErr: ErrMappedDevicePresentWithInactiveTables, + }, + { + name: "Mapped Device Present Without Tables", + rawAttr: "----d-----", + wantErr: ErrMappedDevicePresentWithoutTables, + }, + { + name: "Thin Pool Check Needed", + rawAttr: "----c-----", + wantErr: ErrThinPoolCheckNeeded, + }, + { + name: "Unknown Volume State", + rawAttr: "----X-----", + wantErr: ErrUnknownVolumeState, + }, + { + name: "Historical Volume State", + rawAttr: "----h-----", + wantErr: ErrHistoricalVolumeState, + }, + { + name: "Logical Volume Underlying Device State Unknown", + rawAttr: "-----X----", + wantErr: ErrLogicalVolumeUnderlyingDeviceStateUnknown, + }, + { + name: "Healthy Volume", + rawAttr: "-wi-a-----", + wantErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + lvAttr, err := ParsedLVAttr(tt.rawAttr) + if err != nil { + t.Fatalf("ParsedLVAttr() error = %v", err) + } + if err := lvAttr.VerifyHealth(); !errors.Is(err, tt.wantErr) { + t.Errorf("VerifyHealth() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} + +func TestLVAttrString(t *testing.T) { + for _, tt := range []struct { + name string + rawAttr string + want string + }{ + { + name: "Basic LV", + rawAttr: "-wi-a-----", + want: "-wi-a-----", + }, + } { + t.Run(tt.name, func(t *testing.T) { + attr, err := ParsedLVAttr(tt.rawAttr) + if err != nil { + t.Fatalf("ParsedLvAttr() error = %v", err) + } + if got := attr.String(); got != tt.want { + t.Errorf("String() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/internal/lvmd/vgservice.go b/internal/lvmd/vgservice.go index 20e6638c9..e5c9ee2ea 100644 --- a/internal/lvmd/vgservice.go +++ b/internal/lvmd/vgservice.go @@ -77,6 +77,7 @@ func (s *vgService) GetLVList(ctx context.Context, req *proto.GetLVListRequest) // do not send thin lvs if request is on TypeThick continue } + vols = append(vols, &proto.LogicalVolume{ Name: lv.Name(), SizeGb: (lv.Size() + (1 << 30) - 1) >> 30, @@ -85,6 +86,7 @@ func (s *vgService) GetLVList(ctx context.Context, req *proto.GetLVListRequest) DevMinor: lv.MinorNumber(), Tags: lv.Tags(), Path: lv.Path(), + Attr: lv.Attr(), }) } return &proto.GetLVListResponse{Volumes: vols}, nil diff --git a/pkg/lvmd/proto/lvmd.pb.go b/pkg/lvmd/proto/lvmd.pb.go index 900922204..4e1def330 100644 --- a/pkg/lvmd/proto/lvmd.pb.go +++ b/pkg/lvmd/proto/lvmd.pb.go @@ -8,7 +8,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: // protoc-gen-go v1.33.0 -// protoc v5.26.1 +// protoc v5.27.2 // source: pkg/lvmd/proto/lvmd.proto package proto @@ -79,6 +79,7 @@ type LogicalVolume struct { Tags []string `protobuf:"bytes,5,rep,name=tags,proto3" json:"tags,omitempty"` // Tags to add to the volume during creation SizeBytes int64 `protobuf:"varint,6,opt,name=size_bytes,json=sizeBytes,proto3" json:"size_bytes,omitempty"` // Volume size in canonical CSI bytes. Path string `protobuf:"bytes,7,opt,name=path,proto3" json:"path,omitempty"` // Path to the lv as per lvm. + Attr string `protobuf:"bytes,8,opt,name=attr,proto3" json:"attr,omitempty"` // Attributes of the lv. } func (x *LogicalVolume) Reset() { @@ -163,6 +164,13 @@ func (x *LogicalVolume) GetPath() string { return "" } +func (x *LogicalVolume) GetAttr() string { + if x != nil { + return x.Attr + } + return "" +} + // Represents the input for CreateLV. type CreateLVRequest struct { state protoimpl.MessageState @@ -973,7 +981,7 @@ var File_pkg_lvmd_proto_lvmd_proto protoreflect.FileDescriptor var file_pkg_lvmd_proto_lvmd_proto_rawDesc = []byte{ 0x0a, 0x19, 0x70, 0x6b, 0x67, 0x2f, 0x6c, 0x76, 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x6c, 0x76, 0x6d, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, - 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xc1, 0x01, 0x0a, 0x0d, + 0x74, 0x6f, 0x22, 0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x22, 0xd5, 0x01, 0x0a, 0x0d, 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x67, 0x62, 0x18, 0x02, 0x20, 0x01, @@ -985,130 +993,131 @@ var file_pkg_lvmd_proto_lvmd_proto_rawDesc = []byte{ 0x18, 0x05, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x70, - 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x22, - 0xcc, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x73, 0x69, 0x7a, 0x65, 0x5f, - 0x67, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x73, 0x69, - 0x7a, 0x65, 0x47, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x03, 0x20, 0x03, - 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x32, 0x0a, 0x15, 0x6c, - 0x76, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x63, - 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, 0x6c, 0x76, 0x63, 0x72, - 0x65, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, - 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, - 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x40, - 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, - 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x61, 0x74, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, + 0x12, 0x0a, 0x04, 0x61, 0x74, 0x74, 0x72, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x61, + 0x74, 0x74, 0x72, 0x22, 0xcc, 0x01, 0x0a, 0x0f, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x73, + 0x69, 0x7a, 0x65, 0x5f, 0x67, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, + 0x52, 0x06, 0x73, 0x69, 0x7a, 0x65, 0x47, 0x62, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, + 0x18, 0x03, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, + 0x32, 0x0a, 0x15, 0x6c, 0x76, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x5f, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x13, + 0x6c, 0x76, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x43, 0x6c, + 0x61, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, + 0x73, 0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, + 0x65, 0x73, 0x22, 0x40, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, + 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x06, 0x76, 0x6f, + 0x6c, 0x75, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4c, 0x56, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, + 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0xe6, + 0x01, 0x0a, 0x17, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x53, 0x6e, 0x61, 0x70, 0x73, + 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, + 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, + 0x67, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, + 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, + 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, + 0x75, 0x72, 0x63, 0x65, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x73, 0x69, + 0x7a, 0x65, 0x5f, 0x67, 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, + 0x06, 0x73, 0x69, 0x7a, 0x65, 0x47, 0x62, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, + 0x73, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x63, + 0x63, 0x65, 0x73, 0x73, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, + 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, + 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x4c, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x4c, 0x56, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, + 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, + 0x67, 0x69, 0x63, 0x61, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x08, 0x73, 0x6e, 0x61, + 0x70, 0x73, 0x68, 0x6f, 0x74, 0x22, 0x84, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, + 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, + 0x07, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x67, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, + 0x18, 0x01, 0x52, 0x06, 0x73, 0x69, 0x7a, 0x65, 0x47, 0x62, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, + 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, + 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, + 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x43, 0x0a, 0x11, + 0x47, 0x65, 0x74, 0x4c, 0x56, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, + 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x63, - 0x61, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x06, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, - 0x22, 0x48, 0x0a, 0x0f, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, - 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, - 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0xe6, 0x01, 0x0a, 0x17, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, - 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x61, - 0x67, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 0x61, 0x67, 0x73, 0x12, 0x21, - 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, - 0x73, 0x12, 0x23, 0x0a, 0x0d, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x76, 0x6f, 0x6c, 0x75, - 0x6d, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, - 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x67, - 0x62, 0x18, 0x05, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, 0x73, 0x69, 0x7a, - 0x65, 0x47, 0x62, 0x12, 0x1f, 0x0a, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x79, - 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, - 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, - 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, - 0x74, 0x65, 0x73, 0x22, 0x4c, 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x53, - 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x30, 0x0a, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0b, 0x32, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x61, - 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x08, 0x73, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, - 0x74, 0x22, 0x84, 0x01, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x4c, 0x56, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1b, 0x0a, 0x07, 0x73, 0x69, 0x7a, - 0x65, 0x5f, 0x67, 0x62, 0x18, 0x02, 0x20, 0x01, 0x28, 0x04, 0x42, 0x02, 0x18, 0x01, 0x52, 0x06, - 0x73, 0x69, 0x7a, 0x65, 0x47, 0x62, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x03, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, - 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x43, 0x0a, 0x11, 0x47, 0x65, 0x74, 0x4c, - 0x56, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2e, 0x0a, - 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x4c, 0x6f, 0x67, 0x69, 0x63, 0x61, 0x6c, 0x56, 0x6f, - 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x73, 0x22, 0x35, 0x0a, - 0x14, 0x47, 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x65, 0x65, 0x5f, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x72, 0x65, 0x65, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x22, 0x35, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, 0x56, 0x4c, 0x69, 0x73, - 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, - 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, - 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x38, 0x0a, 0x13, 0x47, - 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, - 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, - 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, - 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x56, 0x0a, 0x0d, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, - 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x65, 0x65, 0x5f, 0x62, - 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, 0x72, 0x65, 0x65, - 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x02, - 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x61, 0x74, - 0x63, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0xac, 0x01, - 0x0a, 0x0c, 0x54, 0x68, 0x69, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x21, - 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, - 0x74, 0x12, 0x29, 0x0a, 0x10, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x65, - 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0f, 0x6d, 0x65, 0x74, - 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x2f, 0x0a, 0x13, - 0x6f, 0x76, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x62, 0x79, - 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x6f, 0x76, 0x65, 0x72, 0x70, - 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x1d, 0x0a, - 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x04, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x9e, 0x01, 0x0a, - 0x09, 0x57, 0x61, 0x74, 0x63, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, + 0x61, 0x6c, 0x56, 0x6f, 0x6c, 0x75, 0x6d, 0x65, 0x52, 0x07, 0x76, 0x6f, 0x6c, 0x75, 0x6d, 0x65, + 0x73, 0x22, 0x35, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x65, + 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x66, + 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x22, 0x35, 0x0a, 0x10, 0x47, 0x65, 0x74, 0x4c, + 0x56, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, + 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, + 0x38, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, + 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, + 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x22, 0x56, 0x0a, 0x0d, 0x57, 0x61, 0x74, + 0x63, 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x66, 0x72, 0x65, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, - 0x66, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x76, - 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x12, 0x1d, 0x0a, 0x0a, - 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, - 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x30, 0x0a, 0x09, 0x74, - 0x68, 0x69, 0x6e, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x13, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x68, 0x69, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x49, - 0x74, 0x65, 0x6d, 0x52, 0x08, 0x74, 0x68, 0x69, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x32, 0x81, 0x02, - 0x0a, 0x09, 0x4c, 0x56, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x3b, 0x0a, 0x08, 0x43, - 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, - 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x08, 0x52, 0x65, 0x6d, 0x6f, - 0x76, 0x65, 0x4c, 0x56, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x6d, - 0x6f, 0x76, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x30, 0x0a, 0x08, 0x52, 0x65, - 0x73, 0x69, 0x7a, 0x65, 0x4c, 0x56, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x52, - 0x65, 0x73, 0x69, 0x7a, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x0c, - 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x53, 0x0a, 0x10, - 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, - 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, - 0x56, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, - 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, - 0x56, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, - 0x65, 0x32, 0xc3, 0x01, 0x0a, 0x09, 0x56, 0x47, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, - 0x3e, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4c, 0x56, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x17, 0x2e, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x56, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, - 0x74, 0x4c, 0x56, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, - 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, - 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, - 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1b, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, 0x57, 0x61, 0x74, 0x63, - 0x68, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, - 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x52, 0x65, 0x73, - 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x2b, 0x5a, 0x29, 0x67, 0x69, 0x74, 0x68, 0x75, - 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x76, 0x6d, 0x2f, 0x74, 0x6f, - 0x70, 0x6f, 0x6c, 0x76, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6c, 0x76, 0x6d, 0x64, 0x2f, 0x70, - 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x66, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x26, 0x0a, 0x05, 0x69, 0x74, 0x65, + 0x6d, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x10, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x57, 0x61, 0x74, 0x63, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, + 0x73, 0x22, 0xac, 0x01, 0x0a, 0x0c, 0x54, 0x68, 0x69, 0x6e, 0x50, 0x6f, 0x6f, 0x6c, 0x49, 0x74, + 0x65, 0x6d, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, + 0x6e, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x01, 0x52, 0x0b, 0x64, 0x61, 0x74, 0x61, 0x50, 0x65, + 0x72, 0x63, 0x65, 0x6e, 0x74, 0x12, 0x29, 0x0a, 0x10, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, + 0x61, 0x5f, 0x70, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x01, 0x52, + 0x0f, 0x6d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x50, 0x65, 0x72, 0x63, 0x65, 0x6e, 0x74, + 0x12, 0x2f, 0x0a, 0x13, 0x6f, 0x76, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, + 0x6e, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, 0x20, 0x01, 0x28, 0x04, 0x52, 0x12, 0x6f, + 0x76, 0x65, 0x72, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x79, 0x74, 0x65, + 0x73, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, + 0x22, 0x9e, 0x01, 0x0a, 0x09, 0x57, 0x61, 0x74, 0x63, 0x68, 0x49, 0x74, 0x65, 0x6d, 0x12, 0x1d, + 0x0a, 0x0a, 0x66, 0x72, 0x65, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x04, 0x52, 0x09, 0x66, 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x21, 0x0a, + 0x0c, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x5f, 0x63, 0x6c, 0x61, 0x73, 0x73, 0x18, 0x02, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x76, 0x69, 0x63, 0x65, 0x43, 0x6c, 0x61, 0x73, 0x73, + 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x69, 0x7a, 0x65, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x03, + 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x73, 0x69, 0x7a, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, + 0x30, 0x0a, 0x09, 0x74, 0x68, 0x69, 0x6e, 0x5f, 0x70, 0x6f, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, + 0x28, 0x0b, 0x32, 0x13, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x54, 0x68, 0x69, 0x6e, 0x50, + 0x6f, 0x6f, 0x6c, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x08, 0x74, 0x68, 0x69, 0x6e, 0x50, 0x6f, 0x6f, + 0x6c, 0x32, 0x81, 0x02, 0x0a, 0x09, 0x4c, 0x56, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, + 0x3b, 0x0a, 0x08, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x12, 0x16, 0x2e, 0x70, 0x72, + 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, + 0x74, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x30, 0x0a, 0x08, + 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4c, 0x56, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, + 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x12, 0x30, + 0x0a, 0x08, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x4c, 0x56, 0x12, 0x16, 0x2e, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x2e, 0x52, 0x65, 0x73, 0x69, 0x7a, 0x65, 0x4c, 0x56, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, + 0x12, 0x53, 0x0a, 0x10, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4c, 0x56, 0x53, 0x6e, 0x61, 0x70, + 0x73, 0x68, 0x6f, 0x74, 0x12, 0x1e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4c, 0x56, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, + 0x61, 0x74, 0x65, 0x4c, 0x56, 0x53, 0x6e, 0x61, 0x70, 0x73, 0x68, 0x6f, 0x74, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xc3, 0x01, 0x0a, 0x09, 0x56, 0x47, 0x53, 0x65, 0x72, 0x76, + 0x69, 0x63, 0x65, 0x12, 0x3e, 0x0a, 0x09, 0x47, 0x65, 0x74, 0x4c, 0x56, 0x4c, 0x69, 0x73, 0x74, + 0x12, 0x17, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x56, 0x4c, 0x69, + 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x4c, 0x56, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x12, 0x47, 0x0a, 0x0c, 0x47, 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, 0x79, + 0x74, 0x65, 0x73, 0x12, 0x1a, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x46, + 0x72, 0x65, 0x65, 0x42, 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1b, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x47, 0x65, 0x74, 0x46, 0x72, 0x65, 0x65, 0x42, + 0x79, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2d, 0x0a, 0x05, + 0x57, 0x61, 0x74, 0x63, 0x68, 0x12, 0x0c, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x45, 0x6d, + 0x70, 0x74, 0x79, 0x1a, 0x14, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x57, 0x61, 0x74, 0x63, + 0x68, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x30, 0x01, 0x42, 0x2b, 0x5a, 0x29, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x76, + 0x6d, 0x2f, 0x74, 0x6f, 0x70, 0x6f, 0x6c, 0x76, 0x6d, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x6c, 0x76, + 0x6d, 0x64, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/lvmd/proto/lvmd.proto b/pkg/lvmd/proto/lvmd.proto index c07f1ac2f..898494597 100644 --- a/pkg/lvmd/proto/lvmd.proto +++ b/pkg/lvmd/proto/lvmd.proto @@ -21,6 +21,7 @@ message LogicalVolume { repeated string tags = 5; // Tags to add to the volume during creation int64 size_bytes = 6; // Volume size in canonical CSI bytes. string path = 7; // Path to the lv as per lvm. + string attr = 8; // Attributes of the lv. } // Represents the input for CreateLV. diff --git a/pkg/lvmd/proto/lvmd_grpc.pb.go b/pkg/lvmd/proto/lvmd_grpc.pb.go index ecd1da8c1..070876e61 100644 --- a/pkg/lvmd/proto/lvmd_grpc.pb.go +++ b/pkg/lvmd/proto/lvmd_grpc.pb.go @@ -8,7 +8,7 @@ // Code generated by protoc-gen-go-grpc. DO NOT EDIT. // versions: // - protoc-gen-go-grpc v1.3.0 -// - protoc v4.25.2 +// - protoc v5.27.2 // source: pkg/lvmd/proto/lvmd.proto package proto diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 29958da16..b2bd9ad5e 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -19,7 +19,7 @@ HELM := ../../bin/helm KIND := ../../bin/kind KIND_CONFIG := topolvm-cluster.yaml KUBECTL := $(BINDIR)/kubectl-$(KUBERNETES_VERSION) -MINIKUBE_FEATURE_GATES="ReadWriteOncePod=true" +MINIKUBE_FEATURE_GATES="ReadWriteOncePod=true,CSIVolumeHealth=true" MINIKUBE_HOME = $(BINDIR) SUDO := sudo @@ -240,13 +240,25 @@ common/create-vg: $(SUDO) losetup -f $(BACKING_STORE)/backing_store$(ID)_7 $(SUDO) vgcreate -y node$(ID)-thin1 $$($(SUDO) losetup -j $(BACKING_STORE)/backing_store$(ID)_7 | cut -d: -f1) $(SUDO) lvcreate -T -n pool0 -L 4G node$(ID)-thin1 + # Create Volume Group for Volume Health Monitoring Failure Check + truncate --size=2G $(BACKING_STORE)/backing_store$(ID)_8 + truncate --size=2G $(BACKING_STORE)/backing_store$(ID)_9 + # Setup loop devices + $(SUDO) losetup -f $(BACKING_STORE)/backing_store$(ID)_8; \ + $(SUDO) losetup -f $(BACKING_STORE)/backing_store$(ID)_9; \ + echo test | $(SUDO) cryptsetup open --type plain \ + $$($(SUDO) losetup -j $(BACKING_STORE)/backing_store$(ID)_8 | cut -d: -f1) crypt-$(ID) + $(SUDO) vgcreate -y node$(ID)-volume-health \ + /dev/mapper/crypt-$(ID) \ + $$($(SUDO) losetup -j $(BACKING_STORE)/backing_store$(ID)_9 | cut -d: -f1) .PHONY: common/remove-vg common/remove-vg: - for name in thick1 thick2 raid1-1 raid1-2 thin1; do \ + for name in thick1 thick2 raid1-1 raid1-2 thin1 volume-health; do \ $(SUDO) vgremove -ffy node$(ID)-$${name}; \ done; \ - for i in $$(seq 7); do \ + $(SUDO) dmsetup remove -f /dev/mapper/crypt-$(ID) || true + for i in $$(seq 9); do \ $(SUDO) pvremove -ffy $$($(SUDO) losetup -j $(BACKING_STORE)/backing_store$(ID)_$${i} | cut -d: -f1); \ $(SUDO) losetup -d $$($(SUDO) losetup -j $(BACKING_STORE)/backing_store$(ID)_$${i} | cut -d: -f1); \ rm -f $(BACKING_STORE)/backing_store$(ID)_$${i}; \ diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 47392355f..d541b56b1 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -323,6 +323,111 @@ func testE2E() { }).Should(Succeed()) }) + It("should react to failing devices with VolumeConditionAbnormal", func(ctx SpecContext) { + storageClass := "topolvm-provisioner-volumehealth" + By(fmt.Sprintf("deploying Pod with PVC based on StorageClass: %s", storageClass)) + claimYAML := []byte(fmt.Sprintf(pvcTemplateYAML, "topo-pvc", "Filesystem", 200, storageClass)) + podYaml := []byte(fmt.Sprintf(podVolumeMountTemplateYAML, "ubuntu", "topo-pvc")) + + _, err := kubectlWithInput(claimYAML, "apply", "-n", ns, "-f", "-") + Expect(err).ShouldNot(HaveOccurred()) + _, err = kubectlWithInput(podYaml, "apply", "-n", ns, "-f", "-") + Expect(err).ShouldNot(HaveOccurred()) + + By("confirming that the lv correspond to LogicalVolume resource is registered in LVM") + Eventually(func() error { + var pvc corev1.PersistentVolumeClaim + if err = getObjects(&pvc, "pvc", "-n", ns, "topo-pvc"); err != nil { + return err + } + return checkLVIsRegisteredInLVM(pvc.Spec.VolumeName) + }).Should(Succeed()) + + By("confirming the PVC is bound") + Eventually(func() error { + var pvc corev1.PersistentVolumeClaim + err := getObjects(&pvc, "pvc", "-n", ns, "topo-pvc") + if err != nil { + return fmt.Errorf("failed to get PVC. err: %w", err) + } + if pvc.Status.Phase != corev1.ClaimBound { + return errors.New("PVC is not bound") + } + return nil + }).Should(Succeed()) + + By("confirming that the specified device is mounted in the Pod") + Eventually(func() error { + return verifyMountExists(ns, "ubuntu", "/test1") + }).Should(Succeed()) + + By("triggering a volume health partial activation failure") + var failureDeviceCount int + if nonControlPlaneNodeCount == 0 { + failureDeviceCount = 1 + } else { + failureDeviceCount = nonControlPlaneNodeCount + } + for i := 0; i < failureDeviceCount; i++ { + out, err := execAtLocal("sudo", + nil, + "dmsetup", + "remove", + "-f", + // This is the device name that is used in the volume health test vg for the crypt setup + fmt.Sprintf("/dev/mapper/crypt-%v", i+1), + ) + Expect(err).To(Or( + Not(HaveOccurred()), + MatchError( + ContainSubstring(fmt.Sprintf("remove ioctl on crypt-%v failed: Device or resource busy", i+1)), + ), + ), "The only accepted error for the dmsetup remove command is a busy device "+ + "due to removing it while it is active") + if len(out) > 0 { + GinkgoT().Logf("dmsetup stdout=%s", string(out)) + } + } + + By("confirming that a VolumeConditionAbnormal event has occurred") + fieldSelector := "reason=VolumeConditionAbnormal" + Eventually(func() error { + var events corev1.EventList + if err = getObjects(&events, "events", "-n", ns, "--field-selector="+fieldSelector); err != nil { + return err + } + if len(events.Items) == 0 { + return errors.New("no events found, there should be at least one event regarding an abnormal volume condition") + } + return nil + }).WithTimeout(2 * time.Minute).Should(Succeed()) + + By("deleting the Pod and PVC") + _, err = kubectlWithInput(podYaml, "delete", "--now=true", "-n", ns, "-f", "-") + Expect(err).ShouldNot(HaveOccurred()) + _, err = kubectlWithInput(claimYAML, "delete", "-n", ns, "-f", "-") + Expect(err).ShouldNot(HaveOccurred()) + + By("confirming that the PV is deleted") + Eventually(func() error { + var pv corev1.PersistentVolume + err := getObjects(&pv, "pv", volName) + switch { + case errors.Is(err, ErrObjectNotFound): + return nil + case err != nil: + return fmt.Errorf("failed to get pv/%s. err: %w", volName, err) + default: + return fmt.Errorf("target pv exists %s", volName) + } + }).Should(Succeed()) + + By("confirming that the lv correspond to LogicalVolume is deleted") + Eventually(func() error { + return checkLVIsDeletedInLVM(volName) + }).Should(Succeed()) + }) + It("should create a block device for Pod", func() { By("deploying ubuntu Pod with PVC to mount a block device") podYAML := []byte(fmt.Sprintf(podVolumeDeviceTemplateYAML, deviceFile)) diff --git a/test/e2e/lvmd1.yaml b/test/e2e/lvmd1.yaml index 6134392f0..0fc53a98b 100644 --- a/test/e2e/lvmd1.yaml +++ b/test/e2e/lvmd1.yaml @@ -21,6 +21,9 @@ device-classes: thin-pool: name: "pool0" overprovision-ratio: 5.0 + - name: "volume-health" + volume-group: "node1-volume-health" + spare-gb: 1 lvcreate-option-classes: - name: "raid1" options: diff --git a/test/e2e/lvmd2.yaml b/test/e2e/lvmd2.yaml index 598af44d4..04170dd93 100644 --- a/test/e2e/lvmd2.yaml +++ b/test/e2e/lvmd2.yaml @@ -20,6 +20,9 @@ device-classes: thin-pool: name: "pool0" overprovision-ratio: 5.0 + - name: "volume-health" + volume-group: "node2-volume-health" + spare-gb: 1 lvcreate-option-classes: - name: "raid1" options: diff --git a/test/e2e/lvmd3.yaml b/test/e2e/lvmd3.yaml index 59123dc74..7f22138eb 100644 --- a/test/e2e/lvmd3.yaml +++ b/test/e2e/lvmd3.yaml @@ -20,6 +20,9 @@ device-classes: thin-pool: name: "pool0" overprovision-ratio: 5.0 + - name: "volume-health" + volume-group: "node3-volume-health" + spare-gb: 1 lvcreate-option-classes: - name: "raid1" options: diff --git a/test/e2e/manifests/values/base.yaml b/test/e2e/manifests/values/base.yaml index 1b7a6815d..40e11c817 100644 --- a/test/e2e/manifests/values/base.yaml +++ b/test/e2e/manifests/values/base.yaml @@ -40,6 +40,9 @@ lvmd: thin-pool: name: "pool0" overprovision-ratio: 5.0 + - name: "volume-health" + volume-group: "node1-volume-health" + spare-gb: 1 lvcreateOptionClasses: - name: "raid1" options: @@ -125,6 +128,12 @@ storageClasses: additionalParameters: '{{ include "topolvm.pluginName" . }}/device-class': "option-class-raid1" '{{ include "topolvm.pluginName" . }}/lvcreate-option-class': "raid1" + - name: topolvm-provisioner-volumehealth + storageClass: + isDefaultClass: false + volumeBindingMode: WaitForFirstConsumer + additionalParameters: + '{{ include "topolvm.pluginName" . }}/device-class': "volume-health" cert-manager: enabled: true diff --git a/test/e2e/topolvm-cluster.yaml b/test/e2e/topolvm-cluster.yaml index b2b1419a2..8c8288d7b 100644 --- a/test/e2e/topolvm-cluster.yaml +++ b/test/e2e/topolvm-cluster.yaml @@ -2,6 +2,7 @@ apiVersion: kind.x-k8s.io/v1alpha4 kind: Cluster featureGates: ReadWriteOncePod: true + CSIVolumeHealth: true # patch the generated kubeadm config with some extra settings kubeadmConfigPatches: - |