diff --git a/cmd/pmem-csi-driver/main.go b/cmd/pmem-csi-driver/main.go index ed3d4b0fdc..35754a48b9 100644 --- a/cmd/pmem-csi-driver/main.go +++ b/cmd/pmem-csi-driver/main.go @@ -28,6 +28,7 @@ var ( registryEndpoint = flag.String("registryEndpoint", "", "endpoint to connect/listen resgistery server") /* node mode options */ controllerEndpoint = flag.String("controllerEndpoint", "", "internal node controller endpoint") + deviceManager = flag.String("deviceManager", "lvm", "device manager to use to manage pmem devices. supported types: 'lvm' or 'ndctl'") ) func main() { @@ -40,6 +41,7 @@ func main() { Mode: pmemcsidriver.DriverMode(*mode), RegistryEndpoint: *registryEndpoint, ControllerEndpoint: *controllerEndpoint, + DeviceManager: *deviceManager, }) if err != nil { fmt.Printf("Failed to Initialized driver: %s", err.Error()) diff --git a/pkg/pmem-csi-driver/controllerserver.go b/pkg/pmem-csi-driver/controllerserver.go index 6ca38c006c..2d5ee83764 100644 --- a/pkg/pmem-csi-driver/controllerserver.go +++ b/pkg/pmem-csi-driver/controllerserver.go @@ -7,11 +7,8 @@ SPDX-License-Identifier: Apache-2.0 package pmemcsidriver import ( - "encoding/json" "fmt" - "os/exec" "strconv" - "strings" "github.com/golang/glog" "github.com/google/uuid" @@ -21,8 +18,8 @@ import ( "github.com/container-storage-interface/spec/lib/go/csi/v0" - "github.com/intel/pmem-csi/pkg/ndctl" "github.com/intel/pmem-csi/pkg/pmem-common" + pmdmanager "github.com/intel/pmem-csi/pkg/pmem-device-manager" "github.com/intel/pmem-csi/pkg/pmem-grpc" ) @@ -60,14 +57,14 @@ type controllerServer struct { *DefaultControllerServer mode DriverMode rs *registryServer - ctx *ndctl.Context + dm pmdmanager.PmemDeviceManager pmemVolumes map[string]pmemVolume //Controller: map of reqID:VolumeInfo publishVolumeInfo map[string]string //Node: map of reqID:VolumeName } var _ csi.ControllerServer = &controllerServer{} -func NewControllerServer(driver *CSIDriver, mode DriverMode, rs *registryServer, ctx *ndctl.Context) csi.ControllerServer { +func NewControllerServer(driver *CSIDriver, mode DriverMode, rs *registryServer, dm pmdmanager.PmemDeviceManager) csi.ControllerServer { serverCaps := []csi.ControllerServiceCapability_RPC_Type{} switch mode { case Controller: @@ -86,7 +83,7 @@ func NewControllerServer(driver *CSIDriver, mode DriverMode, rs *registryServer, DefaultControllerServer: NewDefaultControllerServer(driver), mode: mode, rs: rs, - ctx: ctx, + dm: dm, pmemVolumes: map[string]pmemVolume{}, publishVolumeInfo: map[string]string{}, } @@ -159,7 +156,7 @@ func (cs *controllerServer) CreateVolume(ctx context.Context, req *csi.CreateVol volumeID := id.String() if cs.mode == Unified || cs.mode == Node { glog.Infof("CreateVolume: Special create volume in Unified mode") - if err := cs.createVolume(volumeID, asked); err != nil { + if err := cs.dm.CreateDevice(volumeID, asked); err != nil { return nil, status.Errorf(codes.Internal, "CreateVolume: failed to create volume: %s", err.Error()) } } else /*if cs.mode == Unified */ { @@ -224,7 +221,7 @@ func (cs *controllerServer) DeleteVolume(ctx context.Context, req *csi.DeleteVol if cs.mode == Unified || cs.mode == Node { glog.Infof("DeleteVolume: Special Delete in Unified mode") if vol, ok := cs.pmemVolumes[req.GetVolumeId()]; ok { - if err := cs.deleteVolume(req.GetVolumeId(), vol.Erase); err != nil { + if err := cs.dm.DeleteDevice(req.VolumeId, vol.Erase); err != nil { return nil, status.Errorf(codes.Internal, "Failed to delete volume: %s", err.Error()) } } @@ -307,11 +304,6 @@ func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *cs return nil, status.Error(codes.InvalidArgument, "ControllerPublishVolume Volume capability must be provided") } - vol := cs.GetVolumeByID(req.GetVolumeId()) - if vol == nil { - return nil, status.Error(codes.NotFound, "Volume not created by this controller") - } - if cs.mode == Controller { vol, ok := cs.pmemVolumes[req.VolumeId] if !ok { @@ -370,7 +362,7 @@ func (cs *controllerServer) ControllerPublishVolume(ctx context.Context, req *cs volumeSize = vol.Size } /* Node/Unified */ - if err := cs.createVolume(req.VolumeId, volumeSize); err != nil { + if err := cs.dm.CreateDevice(req.VolumeId, volumeSize); err != nil { return nil, status.Errorf(codes.Internal, "Failed to create volume: %s", err.Error()) } @@ -425,7 +417,7 @@ func (cs *controllerServer) ControllerUnpublishVolume(ctx context.Context, req * glog.Infof("ControllerUnpublish: use stored EraseAfter: %v", vol.Erase) erase = vol.Erase } - if err := cs.deleteVolume(req.GetVolumeId(), erase); err != nil { + if err := cs.dm.DeleteDevice(req.GetVolumeId(), erase); err != nil { return nil, status.Errorf(codes.Internal, "Failed to delete volume: %s", err.Error()) } @@ -433,161 +425,3 @@ func (cs *controllerServer) ControllerUnpublishVolume(ctx context.Context, req * return &csi.ControllerUnpublishVolumeResponse{}, nil } - -func (cs *controllerServer) listVolumes() (map[string]pmemVolume, error) { - volumes := map[string]pmemVolume{} - if lvmode() == true { - output, err := exec.Command("lvs", "--noheadings", "--nosuffix", "--options", "lv_name,lv_size", "--units", "B").CombinedOutput() - if err != nil { - return nil, status.Error(codes.InvalidArgument, "lvs failed"+string(output)) - } - lines := strings.Split(string(output), "\n") - for _, line := range lines { - fields := strings.Split(strings.TrimSpace(line), " ") - if len(fields) == 2 { - size, _ := strconv.ParseUint(fields[1], 10, 64) //nolint: gosec - vol := pmemVolume{ - ID: fields[0], - Size: size, - Status: Attached, - NodeID: cs.Driver.nodeID, - } - volumes[vol.ID] = vol - } - } - } else { - nss := cs.ctx.GetActiveNamespaces() - - for _, ns := range nss { - data, _ := json.MarshalIndent(ns, "", " ") - glog.Info("Namespace:", string(data[:])) - //glog.Infof("namespace BlockDevName: %v, Size: %v", ns.BlockDeviceName(), ns.Size()) - vol := pmemVolume{ - ID: ns.Name(), - Name: ns.Name(), - Size: ns.Size(), - Status: Attached, - NodeID: cs.Driver.nodeID, - } - volumes[vol.ID] = vol - } - } - - return volumes, nil -} - -func (cs *controllerServer) createVolume(name string, size uint64) error { - glog.Infof("createVolume: name: %s size: %v", name, size) - - if lvmode() == true { - // pick a region, few possible strategies: - // 1. pick first with enough available space: simplest, regions get filled in order; - // 2. pick first with largest available space: regions get used round-robin, i.e. load-balanced, but does not leave large unused; - // 3. pick first with smallest available which satisfies the request: ordered initially, but later leaves bigger free available; - // Let's implement strategy 1 for now, simplest to code as no need to compare sizes in all regions - // NOTE: We walk buses and regions in ndctl context, but avail.size we check in LV context - for _, bus := range cs.ctx.GetBuses() { - glog.Infof("CreateVolume: Bus: %v", bus.DeviceName()) - for _, r := range bus.ActiveRegions() { - glog.Infof("CreateVolume: Region: %v", r.DeviceName()) - vgName := vgName(bus, r) - glog.Infof("CreateVolume: vgName: %v", vgName) - output, err := exec.Command("vgs", "--noheadings", "--nosuffix", "--options", "vg_free", "--units", "B", vgName).CombinedOutput() - if err != nil { - return err - } - vgAvailStr := strings.TrimSpace(string(output)) - vgAvail, _ := strconv.ParseUint(vgAvailStr, 10, 64) - glog.Infof("CreateVolume: vgAvail in %v: [%v]", vgName, vgAvail) - if vgAvail >= size { - // lvcreate takes size in MBytes if no unit. - // We use MBytes here to avoid problems with byte-granularity, as lvcreate - // may refuse to create some arbitrary sizes. - // Division by 1M should not result in smaller-than-asked here - // as lvcreate will round up to next 4MB boundary. - sizeM := int(size / (1024 * 1024)) - sz := strconv.Itoa(sizeM) - output, err := exec.Command("lvcreate", "-L", sz, "-n", name, vgName).CombinedOutput() - glog.Infof("lvcreate output: %s\n", string(output)) - if err != nil { - glog.Infof("CreateVolume: lvcreate failed: %v", string(output)) - } else { - glog.Infof("CreateVolume: LVol %v with size=%v MB created", name, sz) - } - // return in all cases, otherwise loop will create LVs in other regions - return err - } else { - glog.Infof("This volime size %v is not having enough space required(%v)", vgAvail, size) - } - } - } - } else { - ns, err := cs.ctx.CreateNamespace(ndctl.CreateNamespaceOpts{ - Name: name, - Size: size, - }) - if err != nil { - return err - } - data, _ := ns.MarshalJSON() //nolint: gosec - glog.Infof("Namespace crated: %v", data) - } - - return nil -} - -func (cs *controllerServer) deleteVolume(volumeID string, erase bool) error { - - if lvmode() { - lvpath, err := lvPath(volumeID) - if err != nil { - return err - } - glog.Infof("deleteVolume: Matching LVpath: %v erase:%v", lvpath, erase) - if erase { - // erase data on block device, if not disabled by driver option - // use one iteration instead of shred's default=3 for speed - glog.Infof("deleteVolume: Based on EraseAfter=true, wipe data using [shred %v]", lvpath) - output, err := exec.Command("shred", "--iterations=1", lvpath).CombinedOutput() - if err != nil { - glog.Infof("deleteVolume: shred failed: %v", string(output)) - return err - } - } - var output []byte - output, err = exec.Command("lvremove", "-fy", lvpath).CombinedOutput() - glog.Infof("lvremove output: %s\n", string(output)) - return err - } else { - // TODO: name lookup added as arg was changed to be VolumeID, - // but whole direct-nvdimm mode will need re-engineering - name := cs.publishVolumeInfo[volumeID] - return cs.ctx.DestroyNamespaceByName(name) - } - - return nil -} - -// Return device path for based on LV name -func lvPath(volumeID string) (string, error) { - output, err := exec.Command("lvs", "--noheadings", "--options", "lv_name,lv_path").CombinedOutput() - if err != nil { - return "", status.Error(codes.InvalidArgument, "lvs failed"+string(output)) - } - lines := strings.Split(string(output), "\n") - for _, line := range lines { - // we have a line like this here: [nspace1 /dev/ndbus0region1/nspace1] - glog.Infof("lvPath: Line from lvs: [%v]", line) - fields := strings.Fields(line) - if len(fields) == 2 { - if volumeID == fields[0] { - return fields[1], nil - } - } - } - return "", status.Error(codes.InvalidArgument, "no such volume") -} - -func vgName(bus *ndctl.Bus, region *ndctl.Region) string { - return bus.DeviceName() + region.DeviceName() -} diff --git a/pkg/pmem-csi-driver/nodeserver.go b/pkg/pmem-csi-driver/nodeserver.go index 87785fe276..843e845971 100644 --- a/pkg/pmem-csi-driver/nodeserver.go +++ b/pkg/pmem-csi-driver/nodeserver.go @@ -20,25 +20,25 @@ import ( "k8s.io/kubernetes/pkg/util/mount" "github.com/golang/glog" - "github.com/intel/pmem-csi/pkg/ndctl" "github.com/intel/pmem-csi/pkg/pmem-common" + pmdmanager "github.com/intel/pmem-csi/pkg/pmem-device-manager" ) type nodeServer struct { *DefaultNodeServer - ctx *ndctl.Context + dm pmdmanager.PmemDeviceManager volInfo map[string]string } var _ csi.NodeServer = &nodeServer{} -func NewNodeServer(driver *CSIDriver, ctx *ndctl.Context) csi.NodeServer { +func NewNodeServer(driver *CSIDriver, dm pmdmanager.PmemDeviceManager) csi.NodeServer { driver.AddNodeServiceCapabilities([]csi.NodeServiceCapability_RPC_Type{ csi.NodeServiceCapability_RPC_STAGE_UNSTAGE_VOLUME, }) return &nodeServer{ DefaultNodeServer: NewDefaultNodeServer(driver), - ctx: ctx, + dm: dm, volInfo: map[string]string{}, } } @@ -152,26 +152,14 @@ func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol glog.Infof("NodeStageVolume: Staging target path is %v", stagingtargetPath) glog.Infof("NodeStageVolume: Requested fsType is %v", requestedFsType) - var devicepath string - var err error - if lvmode() == true { - devicepath, err = lvPath(req.VolumeId) - if err != nil { - return nil, status.Error(codes.InvalidArgument, "No such volume") - } - glog.Infof("NodeStageVolume: devicepath: %v", devicepath) - } else { - namespace, err := ns.ctx.GetNamespaceByName(attrs["name"]) - if err != nil { - pmemcommon.Infof(3, ctx, "NodeStageVolume: did not find volume %s", attrs["name"]) - return nil, err - } - glog.Infof("NodeStageVolume: Existing namespace: blockdev is %v with size %v", namespace.BlockDeviceName(), namespace.Size()) - devicepath = "/dev/" + namespace.BlockDeviceName() + device, err := ns.dm.GetDevice(req.VolumeId) + if err != nil { + pmemcommon.Infof(3, ctx, "NodeStageVolume: did not find volume %s", attrs["name"]) + return nil, err } // Check does devicepath already contain a filesystem? - existingFsType, err := determineFilesystemType(devicepath) + existingFsType, err := determineFilesystemType(device.Path) if err != nil { glog.Infof("NodeStageVolume: determine failed: %v", err) return nil, err @@ -183,21 +171,21 @@ func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol glog.Infof("NodeStageVolume: Found existing %v filesystem", existingFsType) // Is existing filesystem type same as requested? if existingFsType == requestedFsType { - glog.Infof("Skip mkfs as %v file system already exists on %v", existingFsType, devicepath) + glog.Infof("Skip mkfs as %v file system already exists on %v", existingFsType, device.Path) } else { pmemcommon.Infof(3, ctx, "NodeStageVolume: File system with different type %v exist on %v", - existingFsType, devicepath) + existingFsType, device.Path) return nil, status.Error(codes.InvalidArgument, "File system with different type exists") } } else { // no existing file system, make fs // Empty FsType means "unspecified" and we pick default, currently hard-codes to ext4 if requestedFsType == "ext4" || requestedFsType == "" { - glog.Infof("NodeStageVolume: mkfs.ext4 -F %s", devicepath) - output, err = exec.Command("mkfs.ext4", "-F", devicepath).CombinedOutput() + glog.Infof("NodeStageVolume: mkfs.ext4 -F %s", device.Path) + output, err = exec.Command("mkfs.ext4", "-F", device.Path).CombinedOutput() } else if requestedFsType == "xfs" { - glog.Infof("NodeStageVolume: mkfs.xfs -f %s", devicepath) - output, err = exec.Command("mkfs.xfs", "-f", devicepath).CombinedOutput() + glog.Infof("NodeStageVolume: mkfs.xfs -f %s", device.Path) + output, err = exec.Command("mkfs.xfs", "-f", device.Path).CombinedOutput() } else { glog.Infof("NodeStageVolume: Unsupported fstype: [%v]", requestedFsType) return nil, status.Error(codes.InvalidArgument, "xfs, ext4 are supported as file system types") @@ -218,7 +206,7 @@ func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol // then the mount here will fail. I guess it's ok to not check explicitly for existing mount, // as end result after mount attempt will be same: no new mount and existing mount remains. // TODO: cleaner is to explicitly check (although CSI spec may tell that out-of-order call is illegal (check it)) - glog.Infof("NodeStageVolume: mount %s %s", devicepath, stagingtargetPath) + glog.Infof("NodeStageVolume: mount %s %s", device.Path, stagingtargetPath) /* THIS is how it could go with using "mount" package options := []string{""} @@ -230,7 +218,7 @@ func (ns *nodeServer) NodeStageVolume(ctx context.Context, req *csi.NodeStageVol // added -c makes canonical mount, resulting in mounted path matching what LV thinks is lvpath. // Without -c mounted path will look like /dev/mapper/... and its more difficult to match it to lvpath when unmounting // TODO: perhaps this thing can be revisited-cleaned somehow - output, err = exec.Command("mount", "-c", devicepath, stagingtargetPath).CombinedOutput() + output, err = exec.Command("mount", "-c", device.Path, stagingtargetPath).CombinedOutput() if err != nil { return nil, status.Error(codes.InvalidArgument, "mount failed"+string(output)) } @@ -260,19 +248,10 @@ func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstag // by spec, we have to return OK if asked volume is not mounted on asked path, // so we look up the current device by volumeID and see is that device // mounted on staging target path - if lvmode() == true { - devicepath, err := lvPath(req.VolumeId) - if err != nil { - return nil, status.Error(codes.InvalidArgument, "No such volume") - } - glog.Infof("NodeUnstageVolume: devicepath: %v", devicepath) - } else { - namespace, err := ns.ctx.GetNamespaceByName(volName) - if err != nil { - pmemcommon.Infof(3, ctx, "NodeUnstageVolume: did not find volume %s", req.GetVolumeId()) - return nil, err - } - glog.Infof("NodeUnstageVolume: Existing namespace: blockdev: %v with size %v", namespace.BlockDeviceName(), namespace.Size()) + _, err := ns.dm.GetDevice(req.VolumeId) + if err != nil { + pmemcommon.Infof(3, ctx, "NodeUnstageVolume: did not find volume %s", req.GetVolumeId()) + return nil, err } // Find out device name for mounted path @@ -291,10 +270,7 @@ func (ns *nodeServer) NodeUnstageVolume(ctx context.Context, req *csi.NodeUnstag glog.Infof("NodeUnstageVolume: Umount failed: %v", err) return nil, err } - //glog.Infof("NodeUnStageVolume: removing staging directory: %s", stagingtargetPath) - //if err := os.Remove(stagingtargetPath); err != nil { - // pmemcommon.Infof(3, ctx, "failed to remove directory %v: %v", stagingtargetPath, err) - //} + return &csi.NodeUnstageVolumeResponse{}, nil } @@ -341,32 +317,3 @@ func determineFilesystemType(devicePath string) (string, error) { } return "", fmt.Errorf("no filesystem type detected for %s", devicePath) } - -// TODO: clean this up, likely can be deleted. -// This is method to determine "LV mode" dynamically in a setup where -// we want run-time detection. -// It was implemented when we faked LVs on top of regular block device in VM-devel mode. -// Right now lvMode=true is had-coded here and all lvmode checks in code will return true -// The code in else-parts of these blocks would serve ndctl-managed namespaces directly, -// but this seems not viable way forward, and is incomplete and nto in good shape. -// For now, keeping the code in else-parts for historic reference. - -//var lvMode bool = false -var lvMode bool = true - -//var lvModeSet bool = false -func lvmode() bool { - /* if lvModeSet == false { - lvModeSet = true - glog.Infof("LVmode not set, try to determine...") - _, err := exec.Command("vgdisplay", lvgroup).CombinedOutput() - if err != nil { - lvMode = false - glog.Infof("No LV group: %v found, LV mode false", lvgroup) - } else { - lvMode = true - glog.Infof("LV group: %v is found, LV mode is true", lvgroup) - } - }*/ - return lvMode -} diff --git a/pkg/pmem-csi-driver/pmem-csi-driver.go b/pkg/pmem-csi-driver/pmem-csi-driver.go index ae8d2ca6fd..fca538ea65 100644 --- a/pkg/pmem-csi-driver/pmem-csi-driver.go +++ b/pkg/pmem-csi-driver/pmem-csi-driver.go @@ -10,14 +10,11 @@ package pmemcsidriver import ( "context" "fmt" - "os/exec" - "strconv" - "strings" "time" "github.com/container-storage-interface/spec/lib/go/csi/v0" "github.com/golang/glog" - "github.com/intel/pmem-csi/pkg/ndctl" + pmdmanager "github.com/intel/pmem-csi/pkg/pmem-device-manager" pmemgrpc "github.com/intel/pmem-csi/pkg/pmem-grpc" registry "github.com/intel/pmem-csi/pkg/pmem-registry" "google.golang.org/grpc" @@ -55,12 +52,13 @@ type Config struct { RegistryEndpoint string //ControllerEndpoint exported node controller endpoint ControllerEndpoint string + //DeviceManager device manager to use + DeviceManager string } type pmemDriver struct { driver *CSIDriver cfg Config - ctx *ndctl.Context ids csi.IdentityServer ns csi.NodeServer cs csi.ControllerServer @@ -94,15 +92,9 @@ func GetPMEMDriver(cfg Config) (*pmemDriver, error) { csi.VolumeCapability_AccessMode_SINGLE_NODE_WRITER, }) - ctx, err := ndctl.NewContext() - if err != nil { - return nil, fmt.Errorf("Failed to initialize pmem context: %s", err.Error()) - } - return &pmemDriver{ cfg: cfg, driver: driver, - ctx: ctx, }, nil } @@ -113,7 +105,7 @@ func (pmemd *pmemDriver) Run() error { if pmemd.cfg.Mode == Controller { pmemd.rs = NewRegistryServer() - pmemd.cs = NewControllerServer(pmemd.driver, pmemd.cfg.Mode, pmemd.rs, pmemd.ctx) + pmemd.cs = NewControllerServer(pmemd.driver, pmemd.cfg.Mode, pmemd.rs, nil) if pmemd.cfg.Endpoint != pmemd.cfg.RegistryEndpoint { if err := s.Start(pmemd.cfg.Endpoint, func(server *grpc.Server) { @@ -137,8 +129,12 @@ func (pmemd *pmemDriver) Run() error { } } } else { - pmemd.ns = NewNodeServer(pmemd.driver, pmemd.ctx) - pmemd.cs = NewControllerServer(pmemd.driver, pmemd.cfg.Mode, nil, pmemd.ctx) + dm, err := newDeviceManager(pmemd.cfg.DeviceManager) + if err != nil { + return err + } + pmemd.ns = NewNodeServer(pmemd.driver, dm) + pmemd.cs = NewControllerServer(pmemd.driver, pmemd.cfg.Mode, nil, dm) if pmemd.cfg.Mode == Node { if pmemd.cfg.Endpoint != pmemd.cfg.ControllerEndpoint { @@ -163,7 +159,7 @@ func (pmemd *pmemDriver) Run() error { return err } } - if err := pmemd.registerNodeController(); err != nil { + if err := pmemd.registerNodeController(dm); err != nil { return err } } else /* if pmemd.cfg.Mode == Unified */ { @@ -183,7 +179,7 @@ func (pmemd *pmemDriver) Run() error { return nil } -func (pmemd *pmemDriver) registerNodeController() error { +func (pmemd *pmemDriver) registerNodeController(dm pmdmanager.PmemDeviceManager) error { fmt.Printf("Connecting to Registry at : %s\n", pmemd.cfg.RegistryEndpoint) var err error var conn *grpc.ClientConn @@ -199,7 +195,7 @@ func (pmemd *pmemDriver) registerNodeController() error { time.Sleep(10 * time.Second) } client := registry.NewRegistryClient(conn) - capacity, err := pmemd.getNodeCapacity() + capacity, err := dm.GetCapacity() if err != nil { glog.Warningf("Error while preparing node capacity: %s", err.Error()) } @@ -218,30 +214,12 @@ func (pmemd *pmemDriver) registerNodeController() error { return nil } -func (pmemd *pmemDriver) getNodeCapacity() (uint64, error) { - var capacity uint64 - vgsArgs := []string{"--noheadings", "--nosuffix", "--options", "vg_free", "--units", "B"} - pmemVolumeGroups := []string{} - - for _, bus := range pmemd.ctx.GetBuses() { - for _, r := range bus.ActiveRegions() { - pmemVolumeGroups = append(pmemVolumeGroups, vgName(bus, r)) - } +func newDeviceManager(dmType string) (pmdmanager.PmemDeviceManager, error) { + switch dmType { + case "lvm": + return pmdmanager.NewPmemDeviceManagerLVM() + case "ndctl": + return pmdmanager.NewPmemDeviceManagerNdctl() } - - args := append(vgsArgs, pmemVolumeGroups...) - output, err := exec.Command("vgs", args...).CombinedOutput() - if err != nil { - return 0, err - } - - lines := strings.Split(string(output), "\n") - for _, line := range lines { - vgAvail, _ := strconv.ParseUint(strings.TrimSpace(line), 10, 64) - capacity += vgAvail - } - - glog.Infof("Available Node capacity: %v", capacity) - - return capacity, nil + return nil, fmt.Errorf("Unsupported device manager type '%s", dmType) }