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  }