@@ -18,12 +18,16 @@ package mountmanager
1818
1919import  (
2020	"context" 
21+ 	"encoding/json" 
2122	"errors" 
2223	"fmt" 
24+ 	"io" 
25+ 	"net/http" 
2326	"os" 
2427	"path/filepath" 
2528	"strconv" 
2629	"strings" 
30+ 	"time" 
2731
2832	diskapi "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1" 
2933	diskclient "github.com/kubernetes-csi/csi-proxy/client/groups/disk/v1" 
@@ -38,6 +42,16 @@ import (
3842	mount "k8s.io/mount-utils" 
3943)
4044
45+ // GoogleCloudDisk represents a disk from Google Cloud metadata 
46+ type  GoogleCloudDisk  struct  {
47+ 	DeviceName               string  `json:"deviceName"` 
48+ 	Index                    int     `json:"index"` 
49+ 	Interface                string  `json:"interface"` 
50+ 	Mode                     string  `json:"mode"` 
51+ 	NvmeNamespaceIdentifier  uint64  `json:"nvmeNamespaceIdentifier"` 
52+ 	Type                     string  `json:"type"` 
53+ }
54+ 
4155// CSIProxyMounterV1 is the mounter implementation that uses the v1 API 
4256type  CSIProxyMounterV1  struct  {
4357	FsClient      * fsclient.Client 
@@ -182,6 +196,197 @@ func (mounter *CSIProxyMounterV1) Unmount(target string) error {
182196}
183197
184198func  (mounter  * CSIProxyMounterV1 ) GetDiskNumber (deviceName  string , partition  string , volumeKey  string ) (string , error ) {
199+ 	// First, get Google Cloud metadata to find the nvmeNamespaceIdentifier for this device 
200+ 	googleDisks , err  :=  mounter .getGoogleCloudDisks ()
201+ 	if  err  !=  nil  {
202+ 		klog .V (4 ).Infof ("Failed to get Google Cloud metadata, falling back to legacy method: %v" , err )
203+ 		return  mounter .getDiskNumberLegacy (deviceName )
204+ 	}
205+ 
206+ 	// Find the nvmeNamespaceIdentifier for the given deviceName 
207+ 	var  targetNamespaceId  uint64 
208+ 	var  diskInterface  string 
209+ 	found  :=  false 
210+ 	for  _ , disk  :=  range  googleDisks  {
211+ 		if  disk .DeviceName  ==  deviceName  {
212+ 			targetNamespaceId  =  disk .NvmeNamespaceIdentifier 
213+ 			diskInterface  =  disk .Interface 
214+ 			found  =  true 
215+ 			klog .V (4 ).Infof ("Found target namespace ID %d for device %s with interface %s" , targetNamespaceId , deviceName , diskInterface )
216+ 			break 
217+ 		}
218+ 	}
219+ 
220+ 	if  ! found  {
221+ 		klog .V (4 ).Infof ("Device %s not found in Google Cloud metadata, falling back to legacy method" , deviceName )
222+ 		return  mounter .getDiskNumberLegacy (deviceName )
223+ 	}
224+ 
225+ 	// Check if device is NVME - if not, use legacy method 
226+ 	if  diskInterface  !=  "NVME"  {
227+ 		klog .V (4 ).Infof ("Device %s is %s interface (not NVME), falling back to legacy method" , deviceName , diskInterface )
228+ 		return  mounter .getDiskNumberLegacy (deviceName )
229+ 	}
230+ 
231+ 	// Get Windows disk information 
232+ 	listRequest  :=  & diskapi.ListDiskIDsRequest {}
233+ 	diskIDsResponse , err  :=  mounter .DiskClient .ListDiskIDs (context .Background (), listRequest )
234+ 	if  err  !=  nil  {
235+ 		return  "" , err 
236+ 	}
237+ 	diskIDsMap  :=  diskIDsResponse .GetDiskIDs ()
238+ 
239+ 	// Iterate through Windows disks and convert EUI to decimal for matching 
240+ 	for  diskNum , diskInfo  :=  range  diskIDsMap  {
241+ 		klog .V (4 ).Infof ("found disk number %d, disk info %v" , diskNum , diskInfo )
242+ 
243+ 		// Check if this disk has an EUI identifier 
244+ 		euiValue  :=  mounter .extractEUIFromDiskInfo (diskInfo )
245+ 		if  euiValue  ==  ""  {
246+ 			continue 
247+ 		}
248+ 
249+ 		// Convert EUI hex to decimal 
250+ 		decimalValue , err  :=  mounter .convertEUIToDecimal (euiValue )
251+ 		if  err  !=  nil  {
252+ 			klog .V (4 ).Infof ("Failed to convert EUI %s to decimal: %v" , euiValue , err )
253+ 			continue 
254+ 		}
255+ 
256+ 		klog .V (4 ).Infof ("Disk %d: EUI %s converts to decimal %d" , diskNum , euiValue , decimalValue )
257+ 
258+ 		// Check if this matches our target namespace identifier 
259+ 		if  decimalValue  ==  targetNamespaceId  {
260+ 			klog .V (4 ).Infof ("Found matching disk: Windows disk %d matches Google namespace ID %d" , diskNum , targetNamespaceId )
261+ 			return  strconv .FormatUint (uint64 (diskNum ), 10 ), nil 
262+ 		}
263+ 	}
264+ 
265+ 	// Final fallback: if NVME matching failed, try legacy method 
266+ 	klog .V (4 ).Infof ("Could not find NVME match for device %s with namespace ID %d, falling back to legacy method" , deviceName , targetNamespaceId )
267+ 	return  mounter .getDiskNumberLegacy (deviceName )
268+ }
269+ 
270+ // Helper function to extract EUI from disk info (v1 API format) 
271+ func  (mounter  * CSIProxyMounterV1 ) extractEUIFromDiskInfo (diskInfo  * diskapi.DiskIDs ) string  {
272+ 	klog .V (4 ).Infof ("extractEUIFromDiskInfo called for disk with Page83=%s, SerialNumber=%s" , diskInfo .Page83 , diskInfo .SerialNumber )
273+ 
274+ 	// Check if Page83 contains an EUI format 
275+ 	if  diskInfo .Page83  !=  ""  &&  strings .HasPrefix (diskInfo .Page83 , "eui." ) {
276+ 		klog .V (4 ).Infof ("Found EUI in Page83: %s" , diskInfo .Page83 )
277+ 		return  diskInfo .Page83 
278+ 	}
279+ 
280+ 	// For NVMe disks, check SerialNumber field and convert to EUI format 
281+ 	if  diskInfo .SerialNumber  !=  ""  {
282+ 		klog .V (4 ).Infof ("Attempting to convert serial number %s to EUI" , diskInfo .SerialNumber )
283+ 		// Convert serial number format like "10CC_9636_B6E3_CE3B_0000_0000_0000_0000." to EUI format 
284+ 		eui  :=  mounter .convertSerialToEUI (diskInfo .SerialNumber )
285+ 		if  eui  !=  ""  {
286+ 			klog .V (4 ).Infof ("Successfully converted serial number %s to EUI %s" , diskInfo .SerialNumber , eui )
287+ 			return  eui 
288+ 		} else  {
289+ 			klog .V (4 ).Infof ("Failed to convert serial number %s to EUI" , diskInfo .SerialNumber )
290+ 		}
291+ 	} else  {
292+ 		klog .V (4 ).Infof ("No serial number found for disk" )
293+ 	}
294+ 
295+ 	klog .V (4 ).Infof ("No EUI found for disk" )
296+ 	return  "" 
297+ }
298+ 
299+ // Helper function to convert serial number to EUI format 
300+ func  (mounter  * CSIProxyMounterV1 ) convertSerialToEUI (serialNumber  string ) string  {
301+ 	klog .V (4 ).Infof ("convertSerialToEUI: input=%s" , serialNumber )
302+ 
303+ 	// Remove trailing period and underscores from serial number 
304+ 	// Format: "10CC_9636_B6E3_CE3B_0000_0000_0000_0000." -> "10CC9636B6E3CE3B0000000000000000" 
305+ 	cleaned  :=  strings .TrimSuffix (serialNumber , "." )
306+ 	cleaned  =  strings .ReplaceAll (cleaned , "_" , "" )
307+ 	klog .V (4 ).Infof ("convertSerialToEUI: cleaned=%s, length=%d" , cleaned , len (cleaned ))
308+ 
309+ 	// Validate that it's a valid hex string of expected length 
310+ 	if  len (cleaned ) !=  32  {
311+ 		klog .V (4 ).Infof ("convertSerialToEUI: invalid length %d, expected 32" , len (cleaned ))
312+ 		return  "" 
313+ 	}
314+ 
315+ 	// Check if it's valid hex (just test parsing the first 16 chars to avoid overflow) 
316+ 	if  _ , err  :=  strconv .ParseUint (cleaned [:16 ], 16 , 64 ); err  !=  nil  {
317+ 		klog .V (4 ).Infof ("convertSerialToEUI: invalid hex in first 16 chars: %v" , err )
318+ 		return  "" 
319+ 	}
320+ 
321+ 	// Return in EUI format 
322+ 	result  :=  "eui."  +  cleaned 
323+ 	klog .V (4 ).Infof ("convertSerialToEUI: result=%s" , result )
324+ 	return  result 
325+ }
326+ 
327+ // Helper function to convert EUI hex to decimal 
328+ func  (mounter  * CSIProxyMounterV1 ) convertEUIToDecimal (euiValue  string ) (uint64 , error ) {
329+ 	// Extract hex part from EUI (first 16 characters after "eui.") 
330+ 	if  ! strings .HasPrefix (euiValue , "eui." ) {
331+ 		return  0 , fmt .Errorf ("invalid EUI format: %s" , euiValue )
332+ 	}
333+ 
334+ 	hexPart  :=  strings .TrimPrefix (euiValue , "eui." )
335+ 	if  len (hexPart ) <  16  {
336+ 		return  0 , fmt .Errorf ("EUI hex part too short: %s" , hexPart )
337+ 	}
338+ 
339+ 	// Take first 16 hex characters 
340+ 	hexPart  =  hexPart [:16 ]
341+ 
342+ 	// Convert to decimal 
343+ 	decimalValue , err  :=  strconv .ParseUint (hexPart , 16 , 64 )
344+ 	if  err  !=  nil  {
345+ 		return  0 , fmt .Errorf ("failed to parse hex %s: %v" , hexPart , err )
346+ 	}
347+ 
348+ 	return  decimalValue , nil 
349+ }
350+ 
351+ // Helper function to get Google Cloud metadata 
352+ func  (mounter  * CSIProxyMounterV1 ) getGoogleCloudDisks () ([]GoogleCloudDisk , error ) {
353+ 	client  :=  & http.Client {
354+ 		Timeout : 10  *  time .Second ,
355+ 	}
356+ 
357+ 	req , err  :=  http .NewRequest ("GET" , "http://metadata.google.internal/computeMetadata/v1/instance/disks/?recursive=true" , nil )
358+ 	if  err  !=  nil  {
359+ 		return  nil , fmt .Errorf ("failed to create request: %v" , err )
360+ 	}
361+ 
362+ 	req .Header .Set ("Metadata-Flavor" , "Google" )
363+ 
364+ 	resp , err  :=  client .Do (req )
365+ 	if  err  !=  nil  {
366+ 		return  nil , fmt .Errorf ("failed to call metadata service: %v" , err )
367+ 	}
368+ 	defer  resp .Body .Close ()
369+ 
370+ 	if  resp .StatusCode  !=  http .StatusOK  {
371+ 		return  nil , fmt .Errorf ("metadata service returned status %d" , resp .StatusCode )
372+ 	}
373+ 
374+ 	body , err  :=  io .ReadAll (resp .Body )
375+ 	if  err  !=  nil  {
376+ 		return  nil , fmt .Errorf ("failed to read response body: %v" , err )
377+ 	}
378+ 
379+ 	var  disks  []GoogleCloudDisk 
380+ 	if  err  :=  json .Unmarshal (body , & disks ); err  !=  nil  {
381+ 		return  nil , fmt .Errorf ("failed to parse JSON response: %v" , err )
382+ 	}
383+ 
384+ 	klog .V (4 ).Infof ("Retrieved %d disks from Google Cloud metadata" , len (disks ))
385+ 	return  disks , nil 
386+ }
387+ 
388+ // Legacy method for backward compatibility 
389+ func  (mounter  * CSIProxyMounterV1 ) getDiskNumberLegacy (deviceName  string ) (string , error ) {
185390	listRequest  :=  & diskapi.ListDiskIDsRequest {}
186391	diskIDsResponse , err  :=  mounter .DiskClient .ListDiskIDs (context .Background (), listRequest )
187392	if  err  !=  nil  {
0 commit comments