@@ -18,12 +18,16 @@ package mountmanager
18
18
19
19
import (
20
20
"context"
21
+ "encoding/json"
21
22
"errors"
22
23
"fmt"
24
+ "io"
25
+ "net/http"
23
26
"os"
24
27
"path/filepath"
25
28
"strconv"
26
29
"strings"
30
+ "time"
27
31
28
32
diskapi "github.com/kubernetes-csi/csi-proxy/client/api/disk/v1"
29
33
diskclient "github.com/kubernetes-csi/csi-proxy/client/groups/disk/v1"
@@ -38,6 +42,16 @@ import (
38
42
mount "k8s.io/mount-utils"
39
43
)
40
44
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
+
41
55
// CSIProxyMounterV1 is the mounter implementation that uses the v1 API
42
56
type CSIProxyMounterV1 struct {
43
57
FsClient * fsclient.Client
@@ -182,6 +196,197 @@ func (mounter *CSIProxyMounterV1) Unmount(target string) error {
182
196
}
183
197
184
198
func (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 ) {
185
390
listRequest := & diskapi.ListDiskIDsRequest {}
186
391
diskIDsResponse , err := mounter .DiskClient .ListDiskIDs (context .Background (), listRequest )
187
392
if err != nil {
0 commit comments