github.com/openebs/node-disk-manager@v1.9.1-0.20230225014141-4531f06ffa1e/cmd/ndm_daemonset/probe/uuid.go (about) 1 /* 2 Copyright 2020 The OpenEBS Authors 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package probe 18 19 import ( 20 "os" 21 22 "github.com/openebs/node-disk-manager/blockdevice" 23 "github.com/openebs/node-disk-manager/pkg/features" 24 "github.com/openebs/node-disk-manager/pkg/util" 25 26 "k8s.io/klog/v2" 27 ) 28 29 // generateUUID creates a new UUID based on the algorithm proposed in 30 // https://github.com/openebs/openebs/pull/2666 31 func generateUUID(bd blockdevice.BlockDevice) (string, bool) { 32 var ok bool 33 var uuidField, uuid string 34 35 // select the field which is to be used for generating UUID 36 // 37 // Serial number is not used directly for UUID generation. This is because serial number is not 38 // unique in some cloud environments. For example, in GCP the serial number is 39 // configurable by the --device-name flag while attaching the disk. 40 // If this flag is not provided, GCP automatically assigns the serial number 41 // which is unique only to the node. Therefore Serial number is used only in cases 42 // where the disk has a WWN. 43 // 44 // If disk has WWN, a combination of WWN+Serial will be used. This is done because there are cases 45 // where the disks has same WWN but different serial. It is seen in some storage arrays. 46 // All the LUNs will have same WWN, but different serial. 47 // 48 // PartitionTableUUID is not used for UUID generation in NDM. The only case where the disk has a PartitionTable 49 // and not partition is when, the user has manually created a partition table without writing any actual partitions. 50 // This means NDM will have to give its consumers the entire disk, i.e consumers will have access to the sectors 51 // where partition table is written. If consumers decide to reformat or erase the disk completely the partition 52 // table UUID is also lost, making NDM unable to identify the disk. Hence, even if a partition table is present 53 // NDM will rewrite it and create a new GPT table and a single partition. Thus consumers will have access only to 54 // the partition and the unique data will be stored in sectors where consumers do not have access. 55 56 switch { 57 case bd.DeviceAttributes.DeviceType == blockdevice.BlockDeviceTypeLoop: 58 // hostname and device name, i.e /dev/loopX will be used for generating uuid 59 hostName, _ := os.Hostname() 60 klog.Infof("device(%s) is a loop device, using node name: %s and path: %s", bd.DevPath, hostName, bd.DevPath) 61 uuidField = hostName + bd.DevPath 62 ok = true 63 case util.Contains(blockdevice.DeviceMapperDeviceTypes, bd.DeviceAttributes.DeviceType): 64 // if a DM device, use the DM uuid 65 klog.Infof("device(%s) is a dm device, using DM UUID: %s", bd.DevPath, bd.DMInfo.DMUUID) 66 // TODO add a check if DM uuid is present, else may need to add mitigation steps 67 uuidField = bd.DMInfo.DMUUID 68 ok = true 69 case bd.DeviceAttributes.DeviceType == blockdevice.BlockDeviceTypePartition: 70 // The partition entry UUID is used when a partition (/dev/sda1) is processed. The partition UUID should be used 71 // if available, other than the partition table UUID, because multiple partitions can have the same partition table 72 // UUID, but each partition will have a different UUID. 73 klog.Infof("device(%s) is a partition, using partition UUID: %s", bd.DevPath, bd.PartitionInfo.PartitionEntryUUID) 74 uuidField = bd.PartitionInfo.PartitionEntryUUID 75 ok = true 76 case len(bd.DeviceAttributes.WWN) > 0: 77 // if device has WWN, both WWN and Serial will be used for UUID generation. 78 klog.Infof("device(%s) has a WWN, using WWN: %s and Serial: %s", 79 bd.DevPath, 80 bd.DeviceAttributes.WWN, bd.DeviceAttributes.Serial) 81 uuidField = bd.DeviceAttributes.WWN + 82 bd.DeviceAttributes.Serial 83 ok = true 84 case len(bd.FSInfo.FileSystemUUID) > 0: 85 klog.Infof("device(%s) has a filesystem, using filesystem UUID: %s", bd.DevPath, bd.FSInfo.FileSystemUUID) 86 uuidField = bd.FSInfo.FileSystemUUID 87 ok = true 88 case features.FeatureGates.IsEnabled(features.PartitionTableUUID) && len(bd.PartitionInfo.PartitionTableType) > 0: 89 if len(bd.PartitionInfo.PartitionTableUUID) == 0 { 90 klog.Errorf("device(%s) has a partition table, but can not get partition table uuid", bd.DevPath) 91 break 92 } 93 94 klog.Infof("device(%s) has a partition table, use partition table uuid: %s", bd.DevPath, bd.PartitionInfo.PartitionTableUUID) 95 uuidField = bd.PartitionInfo.PartitionTableUUID 96 ok = true 97 } 98 99 if ok { 100 uuid = blockdevice.BlockDevicePrefix + util.Hash(uuidField) 101 klog.Infof("generated uuid: %s for device: %s", uuid, bd.DevPath) 102 } 103 104 return uuid, ok 105 } 106 107 // generate old UUID, returns true if the UUID has used path or hostname for generation. 108 func generateLegacyUUID(bd blockdevice.BlockDevice) (string, bool) { 109 localDiskModels := []string{ 110 "EphemeralDisk", 111 "Virtual_disk", 112 "QEMU_HARDDISK", 113 } 114 uid := bd.DeviceAttributes.WWN + 115 bd.DeviceAttributes.Model + 116 bd.DeviceAttributes.Serial + 117 bd.DeviceAttributes.Vendor 118 uuidUsesPath := false 119 if len(bd.DeviceAttributes.IDType) == 0 || util.Contains(localDiskModels, bd.DeviceAttributes.Model) { 120 host, _ := os.Hostname() 121 uid += host + bd.DevPath 122 uuidUsesPath = true 123 } 124 uuid := blockdevice.BlockDevicePrefix + util.Hash(uid) 125 126 return uuid, uuidUsesPath 127 } 128 129 // generateUUIDFromPartitionTable generates a blockdevice uuid from the partition table uuid. 130 // currently this is only used by zfs localPV 131 // 132 //TODO, this currently supports cases where a complete disk is used for ZFS localPV. If multiple 133 // partitions on the same disk are used for pools, each one should be shown as a separate BD. 134 // For achieving that partition uuid can be used, same as used in the generic UUID generation algorithm 135 func generateUUIDFromPartitionTable(bd blockdevice.BlockDevice) (string, bool) { 136 uuidField := bd.PartitionInfo.PartitionTableUUID 137 if len(uuidField) > 0 { 138 return blockdevice.BlockDevicePrefix + util.Hash(uuidField), true 139 } 140 return "", false 141 }