Files
my-rook-config/pkg/clusterd/inventory/disk.go
T

293 lines
8.2 KiB
Go

package inventory
import (
"errors"
"fmt"
"log"
"path"
"strconv"
"strings"
etcd "github.com/coreos/etcd/client"
"github.com/quantum/castle/pkg/util"
"github.com/quantum/castle/pkg/util/exec"
"github.com/quantum/castle/pkg/util/sys"
ctx "golang.org/x/net/context"
)
const (
DiskNameKey = "name"
DiskUUIDKey = "uuid"
DiskSizeKey = "size"
DiskRotationalKey = "rotational"
DiskReadonlyKey = "readonly"
DiskFileSystemKey = "filesystem"
DiskTypeKey = "type"
DiskParentKey = "parent"
DiskHasChildrenKey = "children"
DiskMountPointKey = "mountpoint"
)
func DiskTypeToStr(diskType DiskType) string {
switch diskType {
case Disk:
return "disk"
case Part:
return "part"
default:
return "unknown"
}
}
func StrToDiskType(diskType string) (DiskType, error) {
diskType = strings.ToLower(diskType)
switch diskType {
case "disk":
return Disk, nil
case "part":
return Part, nil
default:
return -1, errors.New(fmt.Sprintf("unknown disk type: %s", diskType))
}
}
func GetSerialFromDevice(device, nodeID string, etcdClient etcd.KeysAPI) (string, error) {
disksKey := path.Join(GetNodeConfigKey(nodeID), DisksKey)
serials, err := util.GetDirChildKeys(etcdClient, disksKey)
if err != nil {
return "", err
}
serialResult := ""
for s := range serials.Iter() {
resp, err := etcdClient.Get(ctx.Background(), path.Join(disksKey, s, DiskNameKey), nil)
if err != nil || resp == nil || resp.Node == nil {
continue
}
if resp.Node.Value == device {
serialResult = s
break
}
}
if serialResult == "" {
return "", fmt.Errorf("serial for device %s not found", device)
}
return serialResult, nil
}
func GetDiskInfo(diskInfo *etcd.Node) (*DiskConfig, error) {
disk := &DiskConfig{}
disk.Serial = util.GetLeafKeyPath(diskInfo.Key)
// iterate over all properties of the disk
for _, diskProperty := range diskInfo.Nodes {
diskPropertyName := util.GetLeafKeyPath(diskProperty.Key)
switch diskPropertyName {
case DiskNameKey:
disk.Name = diskProperty.Value
case DiskUUIDKey:
disk.UUID = diskProperty.Value
case DiskSizeKey:
size, err := strconv.ParseUint(diskProperty.Value, 10, 64)
if err != nil {
return nil, err
} else {
disk.Size = size
}
case DiskRotationalKey:
rotational, err := strconv.ParseInt(diskProperty.Value, 10, 64)
if err != nil {
return nil, err
} else {
disk.Rotational = itob(rotational)
}
case DiskReadonlyKey:
readonly, err := strconv.ParseInt(diskProperty.Value, 10, 64)
if err != nil {
return nil, err
} else {
disk.Readonly = itob(readonly)
}
case DiskFileSystemKey:
disk.FileSystem = diskProperty.Value
case DiskMountPointKey:
disk.MountPoint = diskProperty.Value
case DiskTypeKey:
diskType, err := StrToDiskType(diskProperty.Value)
if err != nil {
return nil, err
} else {
disk.Type = diskType
}
case DiskParentKey:
disk.Parent = diskProperty.Value
case DiskHasChildrenKey:
hasChildren, err := strconv.ParseInt(diskProperty.Value, 10, 64)
if err != nil {
return nil, err
} else {
disk.HasChildren = itob(hasChildren)
}
default:
log.Printf("unknown disk property key %s, skipping...", diskPropertyName)
}
}
return disk, nil
}
func btoi(b bool) int {
if b {
return 1
} else {
return 0
}
}
func itob(i int64) bool {
if i == 0 {
return false
} else {
return true
}
}
func discoverDisks(nodeConfigKey string, etcdClient etcd.KeysAPI, executor exec.Executor) error {
disksKey := path.Join(nodeConfigKey, DisksKey)
cmd := "lsblk all"
devices, err := executor.ExecuteCommandWithOutput(cmd, "lsblk", "--all", "-n", "-l", "--output", "KNAME")
if err != nil {
return fmt.Errorf("failed to list all devices: %+v", err)
}
for _, d := range strings.Split(devices, "\n") {
cmd := fmt.Sprintf("lsblk /dev/%s", d)
diskPropsRaw, err := executor.ExecuteCommandWithOutput(cmd, "lsblk", fmt.Sprintf("/dev/%s", d),
"-b", "-d", "-P", "-o", "SERIAL,UUID,SIZE,ROTA,RO,TYPE,PKNAME")
if err != nil {
// try to get more information about the command error
cmdErr, ok := err.(*exec.CommandError)
if ok && cmdErr.ExitStatus() == 32 {
// certain device types (such as loop) return exit status 32 when probed further,
// ignore and continue without logging
continue
}
log.Printf("failed to get properties of device %s: %+v", d, err)
continue
}
diskPropMap := parseKeyValuePairString(diskPropsRaw)
serial, ok := diskPropMap["SERIAL"]
if !ok || serial == "" {
// disk doesn't have a serial, just continue
continue
}
diskType, ok := diskPropMap["TYPE"]
if !ok || (diskType != "ssd" && diskType != "disk" && diskType != "part") {
// unsupported disk type, just continue
continue
}
fs, err := sys.GetDeviceFilesystem(d, executor)
if err != nil {
return err
}
mountPoint, err := sys.GetDeviceMountPoint(d, executor)
if err != nil {
return err
}
hasChildren, err := sys.DoesDeviceHaveChildren(d, executor)
if err != nil {
return err
}
dkey := path.Join(disksKey, serial)
if _, err := etcdClient.Set(ctx.Background(), path.Join(dkey, DiskNameKey), d, nil); err != nil {
return err
}
if err := setSimpleDiskProperty("UUID", DiskUUIDKey, dkey, diskPropMap, etcdClient); err != nil {
return err
}
if err := setSimpleDiskProperty("SIZE", DiskSizeKey, dkey, diskPropMap, etcdClient); err != nil {
return err
}
if err := setSimpleDiskProperty("ROTA", DiskRotationalKey, dkey, diskPropMap, etcdClient); err != nil {
return err
}
if err := setSimpleDiskProperty("RO", DiskReadonlyKey, dkey, diskPropMap, etcdClient); err != nil {
return err
}
if err := setSimpleDiskProperty("PKNAME", DiskParentKey, dkey, diskPropMap, etcdClient); err != nil {
return err
}
if _, err := etcdClient.Set(ctx.Background(), path.Join(dkey, DiskHasChildrenKey), strconv.Itoa(btoi(hasChildren)), nil); err != nil {
return err
}
if _, err := etcdClient.Set(ctx.Background(), path.Join(dkey, DiskFileSystemKey), fs, nil); err != nil {
return err
}
if _, err := etcdClient.Set(ctx.Background(), path.Join(dkey, DiskMountPointKey), mountPoint, nil); err != nil {
return err
}
}
return nil
}
func setSimpleDiskProperty(propName, keyName, diskKey string, diskPropMap map[string]string, etcdClient etcd.KeysAPI) error {
val, ok := diskPropMap[propName]
if !ok {
return fmt.Errorf("disk property %s not found in map: %+v", propName, diskPropMap)
}
if _, err := etcdClient.Set(ctx.Background(), path.Join(diskKey, keyName), val, nil); err != nil {
return err
}
return nil
}
// test usage only
func TestSetDiskInfo(etcdClient *util.MockEtcdClient, hardwareKey string, serial string, name string, uuid string, size uint64, rotational bool, readonly bool,
filesystem string, mountPoint string, diskType DiskType, parent string, hasChildren bool) DiskConfig {
disksKey := path.Join(hardwareKey, DisksKey)
etcdClient.Set(ctx.Background(), disksKey, "", &etcd.SetOptions{Dir: true})
diskKey := path.Join(disksKey, serial)
etcdClient.Set(ctx.Background(), diskKey, "", &etcd.SetOptions{Dir: true})
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskNameKey), name, nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskUUIDKey), uuid, nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskSizeKey), strconv.FormatUint(size, 10), nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskRotationalKey), strconv.Itoa(btoi(rotational)), nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskReadonlyKey), strconv.Itoa(btoi(readonly)), nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskFileSystemKey), filesystem, nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskMountPointKey), mountPoint, nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskTypeKey), DiskTypeToStr(diskType), nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskParentKey), parent, nil)
etcdClient.Set(ctx.Background(), path.Join(diskKey, DiskHasChildrenKey), strconv.Itoa(btoi(hasChildren)), nil)
return DiskConfig{
Serial: serial,
Name: name,
UUID: uuid,
Size: size,
Rotational: rotational,
Readonly: readonly,
FileSystem: filesystem,
MountPoint: mountPoint,
Type: diskType,
Parent: parent,
HasChildren: hasChildren,
}
}