github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/api-service/node/services/listBlockDevice.go (about)

     1  /*
     2  Copyright 2020 The OpenEBS Authors
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6      http://www.apache.org/licenses/LICENSE-2.0
     7  Unless required by applicable law or agreed to in writing, software
     8  distributed under the License is distributed on an "AS IS" BASIS,
     9  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    10  See the License for the specific language governing permissions and
    11  limitations under the License.
    12  */
    13  
    14  package services
    15  
    16  import (
    17  	"context"
    18  	"strings"
    19  
    20  	"github.com/openebs/node-disk-manager/api-service/node"
    21  	"github.com/openebs/node-disk-manager/api/v1alpha1"
    22  	"github.com/openebs/node-disk-manager/cmd/ndm_daemonset/controller"
    23  	"github.com/openebs/node-disk-manager/pkg/sysfs"
    24  	"github.com/openebs/node-disk-manager/pkg/util"
    25  	protos "github.com/openebs/node-disk-manager/spec/ndm"
    26  
    27  	"google.golang.org/grpc/codes"
    28  	"google.golang.org/grpc/status"
    29  	"k8s.io/klog/v2"
    30  )
    31  
    32  // Node helps in using types defined in package Node
    33  type Node struct {
    34  	node.Node
    35  }
    36  
    37  // NewNode returns an instance of type Node
    38  func NewNode() *Node {
    39  	return &Node{}
    40  }
    41  
    42  type disks []protos.BlockDevice
    43  
    44  // AllBlockDevices contains all the relationships and device types
    45  type AllBlockDevices struct {
    46  	Parents    []string
    47  	Partitions []string
    48  	LVMs       []string
    49  	RAIDs      []string
    50  	Holders    []string
    51  	Slaves     []string
    52  	Loops      []string
    53  	Sparse     []string
    54  }
    55  
    56  var all AllBlockDevices // This variable would contain all devices found in the node and their relationships
    57  
    58  // ConfigFilePath refers to the config file for ndm
    59  const ConfigFilePath = "/host/node-disk-manager.config"
    60  
    61  // ListBlockDevices returns the block devices and their relationships
    62  func (n *Node) ListBlockDevices(ctx context.Context, null *protos.Null) (*protos.BlockDevices, error) {
    63  	klog.Info("Listing block devices")
    64  
    65  	ctrl, err := controller.NewController()
    66  	if err != nil {
    67  		klog.Errorf("Error creating a controller %v", err)
    68  		return nil, status.Errorf(codes.NotFound, "Namespace not found")
    69  	}
    70  
    71  	err = ctrl.SetControllerOptions(controller.NDMOptions{ConfigFilePath: ConfigFilePath})
    72  	if err != nil {
    73  		klog.Errorf("Error setting config to controller %v", err)
    74  		return nil, status.Errorf(codes.Internal, "Error setting config to controller")
    75  	}
    76  
    77  	blockDeviceList, err := ctrl.ListBlockDeviceResource(false)
    78  	if err != nil {
    79  		klog.Errorf("Error listing block devices %v", err)
    80  		return nil, status.Errorf(codes.Internal, "Error fetching list of disks")
    81  	}
    82  
    83  	if len(blockDeviceList.Items) == 0 {
    84  		klog.V(4).Info("No items found")
    85  	}
    86  
    87  	blockDevices := make([]*protos.BlockDevice, 0)
    88  
    89  	err = GetAllTypes(blockDeviceList)
    90  	if err != nil {
    91  		klog.Errorf("Error fetching Parent disks %v", err)
    92  	}
    93  
    94  	for _, name := range all.Parents {
    95  
    96  		blockDevices = append(blockDevices, &protos.BlockDevice{
    97  			Name:       name,
    98  			Type:       "Disk",
    99  			Partitions: FilterPartitions(name, all.Partitions),
   100  		})
   101  	}
   102  
   103  	for _, name := range all.LVMs {
   104  
   105  		blockDevices = append(blockDevices, &protos.BlockDevice{
   106  			Name: name,
   107  			Type: "LVM",
   108  		})
   109  	}
   110  
   111  	for _, name := range all.RAIDs {
   112  
   113  		blockDevices = append(blockDevices, &protos.BlockDevice{
   114  			Name: name,
   115  			Type: "RAID",
   116  		})
   117  	}
   118  
   119  	for _, name := range all.Loops {
   120  
   121  		blockDevices = append(blockDevices, &protos.BlockDevice{
   122  			Name:       name,
   123  			Type:       "Loop",
   124  			Partitions: FilterPartitions(name, all.Partitions),
   125  		})
   126  	}
   127  
   128  	for _, name := range all.Sparse {
   129  
   130  		blockDevices = append(blockDevices, &protos.BlockDevice{
   131  			Name: name,
   132  			Type: "Sparse",
   133  		})
   134  	}
   135  
   136  	return &protos.BlockDevices{
   137  		Blockdevices: blockDevices,
   138  	}, nil
   139  }
   140  
   141  // GetAllTypes updates the list of all block devices found on nodes and their relationships
   142  func GetAllTypes(BL *v1alpha1.BlockDeviceList) error {
   143  	ParentDeviceNames := make([]string, 0)
   144  	HolderDeviceNames := make([]string, 0)
   145  	SlaveDeviceNames := make([]string, 0)
   146  	PartitionNames := make([]string, 0)
   147  	LoopNames := make([]string, 0)
   148  	SparseNames := make([]string, 0)
   149  	LVMNames := make([]string, 0)
   150  	RAIDNames := make([]string, 0)
   151  
   152  	for _, bd := range BL.Items {
   153  		klog.V(4).Infof("Device %v of type %v ", bd.Spec.Path, bd.Spec.Details.DeviceType)
   154  
   155  		if bd.Spec.Details.DeviceType == "sparse" {
   156  			SparseNames = append(SparseNames, bd.Spec.Path)
   157  			continue
   158  		}
   159  
   160  		// GetDependents should not be called on sparse devices, hence this block comes later.
   161  		sysfsDevice, err := sysfs.NewSysFsDeviceFromDevPath(bd.Spec.Path)
   162  		if err != nil {
   163  			klog.Errorf("could not get sysfs device for %s, err: %v", bd.Spec.Path, err)
   164  			continue
   165  		}
   166  		depDevices, err := sysfsDevice.GetDependents()
   167  		if err != nil {
   168  			klog.Errorf("Error fetching dependents of the disk name: %v, err: %v", bd.Spec.Path, err)
   169  			continue
   170  		}
   171  
   172  		if bd.Spec.Details.DeviceType == "loop" {
   173  			LoopNames = append(LoopNames, bd.Spec.Path)
   174  			PartitionNames = append(PartitionNames, depDevices.Partitions...)
   175  			continue
   176  		}
   177  
   178  		// This will run when GPTbasedUUID is enabled
   179  		if bd.Spec.Details.DeviceType == "partition" {
   180  			// We add the partition only if it doesn't already exist
   181  			PartitionNames = util.AddUniqueStringtoSlice(PartitionNames, bd.Spec.Path)
   182  			// We add the parent if it doesn't already exist
   183  			ParentDeviceNames = util.AddUniqueStringtoSlice(ParentDeviceNames, depDevices.Parent)
   184  			// Since partitions can also be holders
   185  			HolderDeviceNames = append(HolderDeviceNames, depDevices.Holders...)
   186  
   187  			// Since we found it's a partition and got it's dependents, we can continue with next device
   188  
   189  			continue
   190  		}
   191  
   192  		// This will run when GPTbasedUUID is disabled
   193  		if bd.Spec.Details.DeviceType == "disk" {
   194  			// We add the parent if it doesn't exist
   195  			ParentDeviceNames = util.AddUniqueStringtoSlice(ParentDeviceNames, bd.Spec.Path)
   196  			// Since there isn't a way we come across this BD again, we can add partitions all at once without checking they exist already
   197  			PartitionNames = append(PartitionNames, depDevices.Partitions...)
   198  			continue
   199  		}
   200  
   201  		if bd.Spec.Details.DeviceType == "lvm" {
   202  			// Add the lvm if it doesn't already exist
   203  			LVMNames = util.AddUniqueStringtoSlice(LVMNames, bd.Spec.Path)
   204  			// if we encounter a lvm say dm-0, we add it's slaves(sda1, sdb1)
   205  			SlaveDeviceNames = append(SlaveDeviceNames, depDevices.Slaves...)
   206  			// if we encounter a lvm say dm-1 which is a partition of dm-0, then dm-0 would be a holder of dm-1
   207  			HolderDeviceNames = append(HolderDeviceNames, depDevices.Holders...)
   208  			continue
   209  		}
   210  
   211  		if strings.Contains(bd.Spec.Details.DeviceType, "raid") {
   212  			// Add the RAID if it doesn't already exist
   213  			RAIDNames = util.AddUniqueStringtoSlice(RAIDNames, bd.Spec.Path)
   214  			// if we encounter a RAID device md-0, we add it's slaves(sda1, sdb1)
   215  			SlaveDeviceNames = append(SlaveDeviceNames, depDevices.Slaves...)
   216  			// if we encounter a raid say md-1 which is a partition of md-0, then md-0 would be a holder of md-1
   217  			HolderDeviceNames = append(HolderDeviceNames, depDevices.Holders...)
   218  			continue
   219  		}
   220  
   221  	}
   222  	klog.V(4).Infof("Parent Devices found are: %v", ParentDeviceNames)
   223  	klog.V(4).Infof("Partitions  found are: %v", PartitionNames)
   224  
   225  	klog.V(4).Infof("LVM found are: %v", LVMNames)
   226  	klog.V(4).Infof("RAID disks found are: %v", RAIDNames)
   227  
   228  	klog.V(4).Infof("Holder Devices found are: %v", HolderDeviceNames)
   229  	klog.V(4).Infof("Slaves found are: %v", SlaveDeviceNames)
   230  
   231  	klog.V(4).Infof("Loop Devices found are: %v", LoopNames)
   232  	klog.V(4).Infof("Sparse disks found are: %v", SparseNames)
   233  
   234  	all.Parents = ParentDeviceNames
   235  	all.Partitions = PartitionNames
   236  	all.Holders = HolderDeviceNames
   237  	all.Slaves = SlaveDeviceNames
   238  	all.LVMs = LVMNames
   239  	all.RAIDs = RAIDNames
   240  	all.Loops = LoopNames
   241  	all.Sparse = SparseNames
   242  	return nil
   243  
   244  }
   245  
   246  // FilterPartitions gets the name of the partitions given a block device.
   247  // Given a disk name /dev/sdb and slice of partition names : ["/dev/sdb1", "/dev/sdb2", "/dev/sdc1"],
   248  //it should return ["/dev/sdb1", "/dev/sdb2"]
   249  func FilterPartitions(name string, pns []string) []string {
   250  	fpns := make([]string, 0)
   251  
   252  	if len(pns) == 0 {
   253  		pns = append(pns, name)
   254  		return pns
   255  	}
   256  
   257  	for _, pn := range pns {
   258  		if strings.Contains(pn, name) {
   259  			fpns = append(fpns, pn)
   260  		}
   261  	}
   262  
   263  	return fpns
   264  }