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 }