Skip to content

Commit 5d207bb

Browse files
authored
NVMe LUKS Support
1 parent 11a3e5b commit 5d207bb

File tree

10 files changed

+218
-42
lines changed

10 files changed

+218
-42
lines changed

frontend/csi/controller_server.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2022 NetApp, Inc. All Rights Reserved.
1+
// Copyright 2024 NetApp, Inc. All Rights Reserved.
22

33
package csi
44

frontend/csi/node_server.go

Lines changed: 64 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,7 @@ func (p *Plugin) nodeStageISCSIVolume(
15361536
}
15371537

15381538
var luksDevice models.LUKSDeviceInterface
1539-
luksDevice, err = utils.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"])
1539+
luksDevice, err = p.devices.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"])
15401540
if err != nil {
15411541
return err
15421542
}
@@ -1836,11 +1836,13 @@ func (p *Plugin) nodePublishISCSIVolume(
18361836
luksDevice, err = p.devices.NewLUKSDeviceFromMappingPath(ctx, devicePath,
18371837
req.VolumeContext["internalName"])
18381838
} else {
1839-
luksDevice, err = utils.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"])
1839+
luksDevice, err = p.devices.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"])
18401840
}
1841+
18411842
if err != nil {
18421843
return nil, status.Error(codes.Internal, err.Error())
18431844
}
1845+
18441846
err = ensureLUKSVolumePassphrase(ctx, p.restClient, luksDevice, req.GetVolumeId(), req.GetSecrets(), false)
18451847
if err != nil {
18461848
Logc(ctx).WithError(err).Error("Failed to ensure current LUKS passphrase.")
@@ -2441,16 +2443,18 @@ func (p *Plugin) nodeStageNVMeVolume(
24412443
ctx context.Context, req *csi.NodeStageVolumeRequest,
24422444
publishInfo *models.VolumePublishInfo,
24432445
) error {
2444-
isLUKS := utils.ParseBool(publishInfo.LUKSEncryption)
2446+
isLUKS := utils.ParseBool(req.PublishContext["LUKSEncryption"])
24452447
publishInfo.LUKSEncryption = strconv.FormatBool(isLUKS)
24462448
publishInfo.MountOptions = req.PublishContext["mountOptions"]
24472449
publishInfo.NVMeSubsystemNQN = req.PublishContext["nvmeSubsystemNqn"]
24482450
publishInfo.NVMeNamespaceUUID = req.PublishContext["nvmeNamespaceUUID"]
24492451
publishInfo.NVMeTargetIPs = strings.Split(req.PublishContext["nvmeTargetIPs"], ",")
24502452
publishInfo.SANType = req.PublishContext["SANType"]
24512453

2452-
if err := utils.AttachNVMeVolumeRetry(ctx, req.VolumeContext["internalName"], "", publishInfo, nil,
2453-
utils.NVMeAttachTimeout); err != nil {
2454+
err := utils.AttachNVMeVolumeRetry(
2455+
ctx, req.VolumeContext["internalName"], "", publishInfo, req.GetSecrets(), utils.NVMeAttachTimeout,
2456+
)
2457+
if err != nil {
24542458
return err
24552459
}
24562460

@@ -2460,11 +2464,11 @@ func (p *Plugin) nodeStageNVMeVolume(
24602464
}
24612465

24622466
if isLUKS {
2463-
luksDevice, err := p.devices.NewLUKSDeviceFromMappingPath(ctx, publishInfo.DevicePath,
2464-
req.VolumeContext["internalName"])
2467+
luksDevice, err := p.devices.NewLUKSDevice(publishInfo.DevicePath, req.VolumeContext["internalName"])
24652468
if err != nil {
24662469
return err
24672470
}
2471+
24682472
// Ensure we update the passphrase in case it has never been set before
24692473
err = ensureLUKSVolumePassphrase(ctx, p.restClient, luksDevice, volumeId, req.GetSecrets(), true)
24702474
if err != nil {
@@ -2500,12 +2504,42 @@ func (p *Plugin) nodeUnstageNVMeVolume(
25002504
publishInfo.NVMeNamespaceUUID)
25012505

25022506
// Get the device using 'nvme-cli' commands. Flush the device IOs.
2507+
// Proceed further with unstage flow, if device is not found.
25032508
nvmeDev, err := p.nvmeHandler.NewNVMeDevice(ctx, publishInfo.NVMeNamespaceUUID)
2504-
// Proceed further with Unstage flow, if 'device is not found'.
25052509
if err != nil && !errors.IsNotFoundError(err) {
25062510
return nil, fmt.Errorf("error while getting NVMe device, %v", err)
25072511
}
25082512

2513+
var devicePath string
2514+
if nvmeDev != nil {
2515+
devicePath = nvmeDev.GetPath()
2516+
}
2517+
2518+
var luksMapperPath string
2519+
if utils.ParseBool(publishInfo.LUKSEncryption) && devicePath != "" {
2520+
fields := LogFields{
2521+
"lunID": publishInfo.IscsiLunNumber,
2522+
"publishedDevice": publishInfo.DevicePath,
2523+
"nvmeDevPath": nvmeDev.GetPath(),
2524+
}
2525+
2526+
luksMapperPath, err = p.devices.GetLUKSDeviceForMultipathDevice(devicePath)
2527+
if err != nil {
2528+
return &csi.NodeUnstageVolumeResponse{}, err
2529+
}
2530+
2531+
// Ensure the LUKS device is closed if the luksMapperPath is set.
2532+
if luksMapperPath != "" {
2533+
if err = p.devices.EnsureLUKSDeviceClosedWithMaxWaitLimit(ctx, luksMapperPath); err != nil {
2534+
if !errors.IsMaxWaitExceededError(err) {
2535+
Logc(ctx).WithFields(fields).WithError(err).Error("Failed to close LUKS device.")
2536+
return &csi.NodeUnstageVolumeResponse{}, err
2537+
}
2538+
Logc(ctx).WithFields(fields).WithError(err).Debug("LUKS close wait time exceeded, continuing with device removal.")
2539+
}
2540+
}
2541+
}
2542+
25092543
if !nvmeDev.IsNil() {
25102544
// If device is found, proceed to flush and clean up.
25112545
err := nvmeDev.FlushDevice(ctx, p.unsafeDetach, force)
@@ -2580,6 +2614,20 @@ func (p *Plugin) nodeUnstageNVMeVolume(
25802614
return nil, status.Error(codes.Internal, errStr)
25812615
}
25822616

2617+
// If the luks device still exists, it means the device was unable to be closed prior to removing the block
2618+
// device. This can happen if the LUN was deleted or offline. It should be removable by this point.
2619+
// It needs to be removed prior to removing the 'unmappedMpathDevice' device below.
2620+
if luksMapperPath != "" {
2621+
// EnsureLUKSDeviceClosed will not return an error if the device is already closed or removed.
2622+
if err = p.devices.EnsureLUKSDeviceClosed(ctx, luksMapperPath); err != nil {
2623+
Logc(ctx).WithFields(LogFields{
2624+
"devicePath": luksMapperPath,
2625+
}).WithError(err).Warning("Unable to remove LUKS mapper device.")
2626+
}
2627+
// Clear the time duration for the LUKS device.
2628+
utils.LuksCloseDurations.RemoveDurationTracking(luksMapperPath)
2629+
}
2630+
25832631
// Delete the device info we saved to the volume tracking info path so unstage can succeed.
25842632
if err := p.nodeHelper.DeleteTrackingInfo(ctx, volumeId); err != nil {
25852633
return nil, status.Error(codes.Internal, err.Error())
@@ -2608,36 +2656,39 @@ func (p *Plugin) nodePublishNVMeVolume(
26082656
publishInfo.MountOptions = utils.AppendToStringList(publishInfo.MountOptions, "ro", ",")
26092657
}
26102658

2659+
devicePath := publishInfo.DevicePath
26112660
if utils.ParseBool(publishInfo.LUKSEncryption) {
26122661
// Rotate the LUKS passphrase if needed, on failure, log and continue to publish
2613-
luksDevice, err := p.devices.NewLUKSDeviceFromMappingPath(ctx, publishInfo.DevicePath,
2614-
req.VolumeContext["internalName"])
2662+
luksDevice, err := p.devices.NewLUKSDevice(devicePath, req.VolumeContext["internalName"])
26152663
if err != nil {
26162664
return nil, status.Error(codes.Internal, err.Error())
26172665
}
2666+
26182667
err = ensureLUKSVolumePassphrase(ctx, p.restClient, luksDevice, req.GetVolumeId(), req.GetSecrets(), false)
26192668
if err != nil {
26202669
Logc(ctx).WithError(err).Error("Failed to ensure current LUKS passphrase.")
26212670
}
2671+
2672+
// At this point, we must reassign the device path to the luks mapper path for mounts to work.
2673+
devicePath = luksDevice.MappedDevicePath()
26222674
}
26232675

26242676
isRawBlock := publishInfo.FilesystemType == filesystem.Raw
26252677
if isRawBlock {
2626-
26272678
if len(publishInfo.MountOptions) > 0 {
26282679
publishInfo.MountOptions = utils.AppendToStringList(publishInfo.MountOptions, "bind", ",")
26292680
} else {
26302681
publishInfo.MountOptions = "bind"
26312682
}
26322683

26332684
// Place the block device at the target path for the raw-block.
2634-
err = p.mount.MountDevice(ctx, publishInfo.DevicePath, req.TargetPath, publishInfo.MountOptions, true)
2685+
err = p.mount.MountDevice(ctx, devicePath, req.TargetPath, publishInfo.MountOptions, true)
26352686
if err != nil {
26362687
return nil, status.Errorf(codes.Internal, "unable to bind mount raw device; %s", err)
26372688
}
26382689
} else {
26392690
// Mount the device.
2640-
err = p.mount.MountDevice(ctx, publishInfo.DevicePath, req.TargetPath, publishInfo.MountOptions, false)
2691+
err = p.mount.MountDevice(ctx, devicePath, req.TargetPath, publishInfo.MountOptions, false)
26412692
if err != nil {
26422693
return nil, status.Errorf(codes.Internal, "unable to mount device; %s", err)
26432694
}

storage_drivers/ontap/api/abstraction_rest.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2023 NetApp, Inc. All Rights Reserved.
1+
// Copyright 2024 NetApp, Inc. All Rights Reserved.
22

33
package api
44

@@ -3231,11 +3231,11 @@ func (d OntapAPIREST) NVMeSubsystemCreate(ctx context.Context, subsystemName str
32313231
fields := []string{"target_nqn"}
32323232
subsystem, err := d.api.NVMeSubsystemGetByName(ctx, subsystemName, fields)
32333233
if err != nil {
3234-
Logc(ctx).Infof("problem getting subsystem; %v", err)
3234+
Logc(ctx).Infof("Problem getting subsystem; %v", err)
32353235
return nil, err
32363236
}
32373237
if subsystem == nil {
3238-
Logc(ctx).Infof("subsystem doesn't exists, creating new subsystem %v now.", subsystemName)
3238+
Logc(ctx).Infof("Subsystem doesn't exist, creating new subsystem %v now.", subsystemName)
32393239
subsystem, err = d.api.NVMeSubsystemCreate(ctx, subsystemName)
32403240
if err != nil {
32413241
return nil, err

storage_drivers/ontap/ontap_san.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -357,6 +357,7 @@ func (d *SANStorageDriver) Create(
357357
if err != nil {
358358
return fmt.Errorf("could not convert volume size %s: %v", volConfig.Size, err)
359359
}
360+
360361
requestedSizeBytes, err := strconv.ParseUint(requestedSize, 10, 64)
361362
if err != nil {
362363
return fmt.Errorf("%v is an invalid volume size: %v", volConfig.Size, err)
@@ -367,8 +368,8 @@ func (d *SANStorageDriver) Create(
367368
// part of the LUN but is not reported to the orchestrator.
368369
reportedSize := lunSizeBytes
369370
lunSizeBytes = incrementWithLUKSMetadataIfLUKSEnabled(ctx, lunSizeBytes, luksEncryption)
370-
371371
lunSize := strconv.FormatUint(lunSizeBytes, 10)
372+
372373
// Get the flexvol size based on the snapshot reserve
373374
flexvolSize := drivers.CalculateVolumeSizeBytes(ctx, name, lunSizeBytes, snapshotReserveInt)
374375
// Add extra 10% to the Flexvol to account for LUN metadata

storage_drivers/ontap/ontap_san_nvme.go

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Copyright 2023 NetApp, Inc. All Rights Reserved.
1+
// Copyright 2024 NetApp, Inc. All Rights Reserved.
22

33
package ontap
44

@@ -298,9 +298,19 @@ func (d *NVMeStorageDriver) Create(
298298
if err != nil {
299299
return fmt.Errorf("could not convert volume size %s: %v", volConfig.Size, err)
300300
}
301-
requestedSizeBytes, _ := strconv.ParseUint(requestedSize, 10, 64)
301+
302+
requestedSizeBytes, err := strconv.ParseUint(requestedSize, 10, 64)
303+
if err != nil {
304+
return fmt.Errorf("%v is an invalid volume size: %v", volConfig.Size, err)
305+
}
302306
namespaceSizeBytes := GetVolumeSize(requestedSizeBytes, storagePool.InternalAttributes()[Size])
307+
308+
// Add a constant overhead for LUKS volumes to account for LUKS metadata. This overhead is
309+
// part of the LUN but is not reported to the orchestrator.
310+
reportedSize := namespaceSizeBytes
311+
namespaceSizeBytes = incrementWithLUKSMetadataIfLUKSEnabled(ctx, namespaceSizeBytes, luksEncryption)
303312
namespaceSize := strconv.FormatUint(namespaceSizeBytes, 10)
313+
304314
// Get the FlexVol size based on the snapshot reserve.
305315
flexVolSize := drivers.CalculateVolumeSizeBytes(ctx, name, namespaceSizeBytes, snapshotReserveInt)
306316
// Add extra 10% to the FlexVol to account for Namespace metadata.
@@ -335,7 +345,7 @@ func (d *NVMeStorageDriver) Create(
335345
}
336346

337347
// Update config to reflect values used to create volume.
338-
volConfig.Size = strconv.FormatUint(namespaceSizeBytes, 10)
348+
volConfig.Size = strconv.FormatUint(reportedSize, 10)
339349
volConfig.SpaceReserve = spaceReserve
340350
volConfig.SnapshotPolicy = snapshotPolicy
341351
volConfig.SnapshotReserve = snapshotReserve
@@ -617,7 +627,9 @@ func (d *NVMeStorageDriver) Import(ctx context.Context, volConfig *storage.Volum
617627
}
618628

619629
// Set the volume to LUKS if backend has LUKS true as default
620-
volConfig.LUKSEncryption = d.Config.LUKSEncryption
630+
if volConfig.LUKSEncryption == "" {
631+
volConfig.LUKSEncryption = d.Config.LUKSEncryption
632+
}
621633

622634
// Set the filesystem type to backend's, if it hasn't been set via annotation
623635
// in the provided pvc during import.
@@ -649,6 +661,16 @@ func (d *NVMeStorageDriver) Import(ctx context.Context, volConfig *storage.Volum
649661
// Use the Namespace size
650662
volConfig.Size = nsInfo.Size
651663

664+
// If the import is a LUKS encrypted volume, then remove the LUKS metadata overhead from the reported
665+
// size on the volConfig.
666+
if utils.ParseBool(volConfig.LUKSEncryption) {
667+
newSize, err := subtractUintFromSizeString(volConfig.Size, utils.LUKSMetadataSize)
668+
if err != nil {
669+
return err
670+
}
671+
volConfig.Size = newSize
672+
}
673+
652674
// Rename the volume if Trident will manage its lifecycle
653675
if !volConfig.ImportNotManaged {
654676
err = d.API.VolumeRename(ctx, originalName, volConfig.InternalName)
@@ -1314,6 +1336,9 @@ func (d *NVMeStorageDriver) Resize(
13141336
return fmt.Errorf("requested size %d is less than existing volume size %d", requestedSizeBytes, nsSizeBytes)
13151337
}
13161338

1339+
// Add a constant overhead for LUKS volumes to account for LUKS metadata.
1340+
requestedSizeBytes = incrementWithLUKSMetadataIfLUKSEnabled(ctx, requestedSizeBytes, volConfig.LUKSEncryption)
1341+
13171342
snapshotReserveInt, err := getSnapshotReserveFromOntap(ctx, name, d.API.VolumeInfo)
13181343
if err != nil {
13191344
Logc(ctx).WithField("name", name).Errorf("Could not get the snapshot reserve percentage for volume.")
@@ -1376,6 +1401,9 @@ func (d *NVMeStorageDriver) Resize(
13761401
}
13771402
}
13781403

1404+
// LUKS metadata size is not reported so remove it from LUN size
1405+
requestedSizeBytes = decrementWithLUKSMetadataIfLUKSEnabled(ctx, requestedSizeBytes, volConfig.LUKSEncryption)
1406+
13791407
// Setting the new size in the volume config.
13801408
volConfig.Size = strconv.FormatUint(requestedSizeBytes, 10)
13811409
return nil

storage_drivers/ontap/ontap_san_nvme_test.go

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
// Copyright 2023 NetApp, Inc. All Rights Reserved.
1+
// Copyright 2024 NetApp, Inc. All Rights Reserved.
22

33
package ontap
44

55
import (
66
"context"
77
"encoding/json"
88
"fmt"
9+
"strconv"
910
"testing"
1011

1112
"github.com/stretchr/testify/assert"
@@ -18,6 +19,7 @@ import (
1819
drivers "github.com/netapp/trident/storage_drivers"
1920
"github.com/netapp/trident/storage_drivers/ontap/api"
2021
"github.com/netapp/trident/storage_drivers/ontap/awsapi"
22+
"github.com/netapp/trident/utils"
2123
"github.com/netapp/trident/utils/errors"
2224
"github.com/netapp/trident/utils/filesystem"
2325
"github.com/netapp/trident/utils/models"
@@ -980,6 +982,21 @@ func TestNVMeCreate_NamespaceCreateAPIError(t *testing.T) {
980982
assert.ErrorContains(t, err, "failed to create namespace")
981983
}
982984

985+
func TestNVMeCreate_LUKSVolume(t *testing.T) {
986+
d, mAPI := newNVMeDriverAndMockApi(t)
987+
pool1, volConfig, volAttrs := getNVMeCreateArgs(d)
988+
989+
volConfig.LUKSEncryption = "true"
990+
mAPI.EXPECT().VolumeExists(ctx, volConfig.InternalName).Return(false, nil)
991+
mAPI.EXPECT().TieringPolicyValue(ctx).Return("TPolicy")
992+
mAPI.EXPECT().VolumeCreate(ctx, gomock.Any()).Return(nil)
993+
mAPI.EXPECT().NVMeNamespaceCreate(ctx, gomock.Any()).Return("nsUUID", nil)
994+
995+
err := d.Create(ctx, volConfig, pool1, volAttrs)
996+
997+
assert.NoError(t, err, "Failed to create NVMe volume.")
998+
}
999+
9831000
func TestNVMeCreate_Success(t *testing.T) {
9841001
d, mAPI := newNVMeDriverAndMockApi(t)
9851002
pool1, volConfig, volAttrs := getNVMeCreateArgs(d)
@@ -2248,6 +2265,52 @@ func TestImport(t *testing.T) {
22482265
assert.NoError(t, err)
22492266
}
22502267

2268+
func TestImport_LUKSNamespace(t *testing.T) {
2269+
d, mAPI := newNVMeDriverAndMockApi(t)
2270+
_, volConfig, _ := getNVMeCreateArgs(d)
2271+
originalName := "fakeOriginalName"
2272+
vol := &api.Volume{Aggregates: []string{"data"}}
2273+
ns := &api.NVMeNamespace{
2274+
Name: "/vol/cloneVol1/namespace0",
2275+
Size: "20GB",
2276+
UUID: "fakeUUID",
2277+
}
2278+
ns.State = "online"
2279+
2280+
vol.Comment = "fakeComment"
2281+
volConfig.LUKSEncryption = "true"
2282+
volConfig.ImportNotManaged = true
2283+
volConfig.Size = "20GB"
2284+
mAPI.EXPECT().VolumeInfo(ctx, gomock.Any()).Return(vol, nil)
2285+
mAPI.EXPECT().NVMeNamespaceGetByName(ctx, "/vol/"+originalName+"/*").Return(ns, nil)
2286+
mAPI.EXPECT().NVMeIsNamespaceMapped(ctx, "", ns.UUID).Return(false, nil)
2287+
// mAPI.EXPECT().VolumeRename(ctx, originalName, volConfig.InternalName).Return(nil)
2288+
2289+
beforeLUKSOverheadBytesStr, err := utils.ConvertSizeToBytes(volConfig.Size)
2290+
if err != nil {
2291+
t.Fatalf("failed to convert volume size")
2292+
}
2293+
beforeLUKSOverhead, err := strconv.ParseUint(beforeLUKSOverheadBytesStr, 10, 64)
2294+
if err != nil {
2295+
t.Fatalf("failed to convert volume size")
2296+
}
2297+
2298+
err = d.Import(ctx, volConfig, originalName)
2299+
2300+
afterLUKSOverheadBytesStr, err := utils.ConvertSizeToBytes(volConfig.Size)
2301+
if err != nil {
2302+
t.Fatalf("failed to convert volume size")
2303+
}
2304+
afterLUKSOverhead, err := strconv.ParseUint(afterLUKSOverheadBytesStr, 10, 64)
2305+
if err != nil {
2306+
t.Fatalf("failed to convert volume size")
2307+
}
2308+
2309+
assert.NoError(t, err)
2310+
assert.Less(t, afterLUKSOverhead, beforeLUKSOverhead)
2311+
assert.Equal(t, beforeLUKSOverhead, incrementWithLUKSMetadataIfLUKSEnabled(ctx, afterLUKSOverhead, "true"))
2312+
}
2313+
22512314
func TestImport_NameTemplate(t *testing.T) {
22522315
d, mAPI := newNVMeDriverAndMockApi(t)
22532316
_, volConfig, _ := getNVMeCreateArgs(d)

0 commit comments

Comments
 (0)