@@ -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