Skip to content

Commit

Permalink
Add support for HDD Storage Type
Browse files Browse the repository at this point in the history
  • Loading branch information
gkao123 committed Oct 14, 2020
1 parent 6b66b59 commit 9182300
Show file tree
Hide file tree
Showing 8 changed files with 296 additions and 20 deletions.
7 changes: 5 additions & 2 deletions examples/kubernetes/dynamic_provisioning/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,15 @@ parameters:
subnetId: subnet-056da83524edbe641
securityGroupIds: sg-086f61ea73388fb6b
deploymentType: PERSISTENT_1
storageType: HDD
```
* subnetId - the subnet ID that the FSx for Lustre filesystem should be created inside.
* securityGroupIds - a common separated list of security group IDs that should be attached to the filesystem
* deploymentType (Optional) - FSx for Lustre supports three deployment types, SCRATCH_1, SCRATCH_2 and PERSISTENT_1. Default: SCRATCH_1.
* kmsKeyId (Optional) - for deployment type PERSISTENT_1, customer can specify a KMS key to use.
* perUnitStorageThroughput (Optional) - for deployment type PERSISTENT_1, customer can specify the storage throughput. Default: "200". Note that customer has to specify as a string here like "200" or "100" etc.
* storageType (Optional) - for deployment type PERSISTENT_1, customer can specify the storage type, either SSD or HDD. Default: "SSD"
* driveCacheType (Required if storageType is "HDD") - for HDD PERSISTENT_1, specify the type of drive cache, either NONE or READ.

### Edit [Persistent Volume Claim Spec](./specs/claim.yaml)
```
Expand All @@ -32,9 +35,9 @@ spec:
storageClassName: fsx-sc
resources:
requests:
storage: 1200Gi
storage: 6000Gi
```
Update `spec.resource.requests.storage` with the storage capacity to request. The storage capacity value will be rounded up to 1200 GiB, 2400 GiB, or a multiple of 3600 GiB.
Update `spec.resource.requests.storage` with the storage capacity to request. The storage capacity value will be rounded up to 1200 GiB, 2400 GiB, or a multiple of 3600 GiB for SSD. If the storageType is specified as HDD, the storage capacity will be rounded up to 6000 GiB or a multiple of 6000 GiB if the perUnitStorageThroughput is 12, or rounded up to 1800 or a multiple of 1800 if the perUnitStorageThroughput is 40.

### Deploy the Application
Create PVC, storageclass and the pod that consumes the PV:
Expand Down
4 changes: 3 additions & 1 deletion examples/kubernetes/dynamic_provisioning_s3/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ parameters:
* deploymentType (Optional) - FSx for Lustre supports three deployment types, SCRATCH_1, SCRATCH_2 and PERSISTENT_1. Default: SCRATCH_1.
* kmsKeyId (Optional) - for deployment type PERSISTENT_1, customer can specify a KMS key to use.
* perUnitStorageThroughput (Optional) - for deployment type PERSISTENT_1, customer can specify the storage throughput. Default: "200". Note that customer has to specify as a string here like "200" or "100" etc.
* storageType (Optional) - for deployment type PERSISTENT_1, customer can specify the storage type, either SSD or HDD. Default: "SSD"
* driveCacheType (Required if storageType is "HDD") - for HDD PERSISTENT_1, specify the type of drive cache, either NONE or READ.

Note:
- S3 Bucket in s3ImportPath and s3ExportPath must be same, otherwise the driver can not create FSx for lustre successfully.
Expand All @@ -47,7 +49,7 @@ spec:
requests:
storage: 1200Gi
```
Update `spec.resource.requests.storage` with the storage capacity to request. The storage capacity value will be rounded up to 1200 GiB, 2400 GiB, or a multiple of 3600 GiB.
Update `spec.resource.requests.storage` with the storage capacity to request. The storage capacity value will be rounded up to 1200 GiB, 2400 GiB, or a multiple of 3600 GiB for SSD. If the storageType is specified as HDD, the storage capacity will be rounded up to 6000 GiB or a multiple of 6000 GiB if the perUnitStorageThroughput is 12, or rounded up to 1800 or a multiple of 1800 if the perUnitStorageThroughput is 40.

### Deploy the Application
Create PVC, storageclass and the pod that consumes the PV:
Expand Down
9 changes: 9 additions & 0 deletions pkg/cloud/cloud.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,8 @@ type FileSystemOptions struct {
DeploymentType string
KmsKeyId string
PerUnitStorageThroughput int64
StorageType string
DriveCacheType string
}

// FSx abstracts FSx client to facilitate its mocking.
Expand Down Expand Up @@ -132,6 +134,10 @@ func (c *cloud) CreateFileSystem(ctx context.Context, volumeName string, fileSys
lustreConfiguration.SetDeploymentType(fileSystemOptions.DeploymentType)
}

if fileSystemOptions.DriveCacheType != "" {
lustreConfiguration.SetDriveCacheType(fileSystemOptions.DriveCacheType)
}

if fileSystemOptions.PerUnitStorageThroughput != 0 {
lustreConfiguration.SetPerUnitStorageThroughput(fileSystemOptions.PerUnitStorageThroughput)
}
Expand All @@ -151,6 +157,9 @@ func (c *cloud) CreateFileSystem(ctx context.Context, volumeName string, fileSys
},
}

if fileSystemOptions.StorageType != "" {
input.StorageType = aws.String(fileSystemOptions.StorageType)
}
if fileSystemOptions.KmsKeyId != "" {
input.KmsKeyId = aws.String(fileSystemOptions.KmsKeyId)
}
Expand Down
143 changes: 143 additions & 0 deletions pkg/cloud/cloud_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,149 @@ func TestCreateFileSystem(t *testing.T) {
mockCtl.Finish()
},
},
{
name: "success: normal with deploymentType and storageTypeSsd",
testFunc: func(t *testing.T) {
mockCtl := gomock.NewController(t)
mockFSx := mocks.NewMockFSx(mockCtl)
c := &cloud{
fsx: mockFSx,
}

req := &FileSystemOptions{
CapacityGiB: volumeSizeGiB,
SubnetId: subnetId,
SecurityGroupIds: securityGroupIds,
DeploymentType: deploymentType,
StorageType: fsx.StorageTypeSsd,
}

output := &fsx.CreateFileSystemOutput{
FileSystem: &fsx.FileSystem{
FileSystemId: aws.String(fileSystemId),
StorageCapacity: aws.Int64(volumeSizeGiB),
DNSName: aws.String(dnsname),
LustreConfiguration: &fsx.LustreFileSystemConfiguration{
MountName: aws.String(mountName),
},
},
}
ctx := context.Background()
mockFSx.EXPECT().CreateFileSystemWithContext(gomock.Eq(ctx), gomock.Any()).Return(output, nil)
resp, err := c.CreateFileSystem(ctx, volumeName, req)
if err != nil {
t.Fatalf("CreateFileSystem is failed: %v", err)
}

if resp == nil {
t.Fatal("resp is nil")
}

if resp.FileSystemId != fileSystemId {
t.Fatalf("FileSystemId mismatches. actual: %v expected: %v", resp.FileSystemId, fileSystemId)
}

if resp.CapacityGiB != volumeSizeGiB {
t.Fatalf("CapacityGiB mismatches. actual: %v expected: %v", resp.CapacityGiB, volumeSizeGiB)
}

if resp.DnsName != dnsname {
t.Fatalf("DnsName mismatches. actual: %v expected: %v", resp.DnsName, dnsname)
}

if resp.MountName != mountName {
t.Fatalf("MountName mismatches. actual: %v expected: %v", resp.MountName, mountName)
}

mockCtl.Finish()
},
},
{
name: "success: normal with deploymentType and storageTypeHdd",
testFunc: func(t *testing.T) {
mockCtl := gomock.NewController(t)
mockFSx := mocks.NewMockFSx(mockCtl)
c := &cloud{
fsx: mockFSx,
}

req := &FileSystemOptions{
CapacityGiB: volumeSizeGiB,
SubnetId: subnetId,
SecurityGroupIds: securityGroupIds,
DeploymentType: fsx.LustreDeploymentTypePersistent1,
StorageType: fsx.StorageTypeHdd,
DriveCacheType: fsx.DriveCacheTypeNone,
}

output := &fsx.CreateFileSystemOutput{
FileSystem: &fsx.FileSystem{
FileSystemId: aws.String(fileSystemId),
StorageCapacity: aws.Int64(volumeSizeGiB),
DNSName: aws.String(dnsname),
LustreConfiguration: &fsx.LustreFileSystemConfiguration{
MountName: aws.String(mountName),
DeploymentType: aws.String(fsx.LustreDeploymentTypePersistent1),
DriveCacheType: aws.String(fsx.DriveCacheTypeNone),
},
},
}
ctx := context.Background()
mockFSx.EXPECT().CreateFileSystemWithContext(gomock.Eq(ctx), gomock.Any()).Return(output, nil)
resp, err := c.CreateFileSystem(ctx, volumeName, req)
if err != nil {
t.Fatalf("CreateFileSystem is failed: %v", err)
}

if resp == nil {
t.Fatal("resp is nil")
}

if resp.FileSystemId != fileSystemId {
t.Fatalf("FileSystemId mismatches. actual: %v expected: %v", resp.FileSystemId, fileSystemId)
}

if resp.CapacityGiB != volumeSizeGiB {
t.Fatalf("CapacityGiB mismatches. actual: %v expected: %v", resp.CapacityGiB, volumeSizeGiB)
}

if resp.DnsName != dnsname {
t.Fatalf("DnsName mismatches. actual: %v expected: %v", resp.DnsName, dnsname)
}

if resp.MountName != mountName {
t.Fatalf("MountName mismatches. actual: %v expected: %v", resp.MountName, mountName)
}

mockCtl.Finish()
},
},
{
name: "failure: incompatible deploymentType and storageTypeHdd",
testFunc: func(t *testing.T) {
mockCtl := gomock.NewController(t)
mockFSx := mocks.NewMockFSx(mockCtl)
c := &cloud{
fsx: mockFSx,
}

req := &FileSystemOptions{
CapacityGiB: volumeSizeGiB,
SubnetId: subnetId,
SecurityGroupIds: securityGroupIds,
DeploymentType: deploymentType,
StorageType: fsx.StorageTypeHdd,
}
ctx := context.Background()
mockFSx.EXPECT().CreateFileSystemWithContext(gomock.Eq(ctx), gomock.Any()).Return(nil, errors.New("CreateFileSystemWithContext failed"))
_, err := c.CreateFileSystem(ctx, volumeName, req)
if err == nil {
t.Fatalf("CreateFileSystem is not failed")
}

mockCtl.Finish()
},
},
{
name: "success: S3 data repository",
testFunc: func(t *testing.T) {
Expand Down
12 changes: 11 additions & 1 deletion pkg/driver/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ const (
volumeParamsDeploymentType = "deploymentType"
volumeParamsKmsKeyId = "kmsKeyId"
volumeParamsPerUnitStorageThroughput = "perUnitStorageThroughput"
volumeParamsStorageType = "storageType"
volumeParamsDriveCacheType = "driveCacheType"
)

func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest) (*csi.CreateVolumeResponse, error) {
Expand Down Expand Up @@ -96,6 +98,14 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
fsOptions.KmsKeyId = val
}

if val, ok := volumeParams[volumeParamsStorageType]; ok {
fsOptions.StorageType = val
}

if val, ok := volumeParams[volumeParamsDriveCacheType]; ok {
fsOptions.DriveCacheType = val
}

if val, ok := volumeParams[volumeParamsPerUnitStorageThroughput]; ok {
n, err := strconv.ParseInt(val, 10, 64)
if err != nil {
Expand All @@ -108,7 +118,7 @@ func (d *Driver) CreateVolume(ctx context.Context, req *csi.CreateVolumeRequest)
if capRange == nil {
fsOptions.CapacityGiB = cloud.DefaultVolumeSize
} else {
fsOptions.CapacityGiB = util.RoundUpVolumeSize(capRange.GetRequiredBytes(), fsOptions.DeploymentType)
fsOptions.CapacityGiB = util.RoundUpVolumeSize(capRange.GetRequiredBytes(), fsOptions.DeploymentType, fsOptions.StorageType, fsOptions.PerUnitStorageThroughput)
}

fs, err := d.cloud.CreateFileSystem(ctx, volName, fsOptions)
Expand Down
2 changes: 2 additions & 0 deletions pkg/driver/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ func TestCreateVolume(t *testing.T) {
volumeParamsSubnetId: subnetId,
volumeParamsSecurityGroupIds: securityGroupIds,
volumeParamsDeploymentType: fsx.LustreDeploymentTypeScratch2,
volumeParamsStorageType: fsx.StorageTypeSsd,
},
}

Expand Down Expand Up @@ -215,6 +216,7 @@ func TestCreateVolume(t *testing.T) {
volumeParamsDeploymentType: fsx.LustreDeploymentTypePersistent1,
volumeParamsKmsKeyId: "arn:aws:kms:us-east-1:215474938041:key/48313a27-7d88-4b51-98a4-fdf5bc80dbbe",
volumeParamsPerUnitStorageThroughput: "200",
volumeParamsStorageType: fsx.StorageTypeSsd,
},
}

Expand Down
37 changes: 24 additions & 13 deletions pkg/util/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,34 @@ const (
GiB = 1024 * 1024 * 1024
)

// RoundUpVolumeSize rounds up the volume size in bytes upto
// 1200 GiB, 2400 GiB, or multiplications of 3600 GiB in the
// unit of GiB for DeploymentType SCRATCH_1, or multiplications
// of 2400 GiB for other DeploymentType
func RoundUpVolumeSize(volumeSizeBytes int64, deploymentType string) int64 {
if deploymentType == fsx.LustreDeploymentTypeScratch1 ||
deploymentType == "" {
if volumeSizeBytes < 3600*GiB {
return roundUpSize(volumeSizeBytes, 1200*GiB) * 1200
// RoundUpVolumeSize rounds the volume size in bytes up to
// 1200 GiB, 2400 GiB, or multiples of 3600 GiB for DeploymentType SCRATCH_1,
// to 1200 GiB or multiples of 2400 GiB for DeploymentType SCRATCH_2 or for
// DeploymentType PERSISTENT_1 and StorageType SSD, multiples of 6000 GiB for
// DeploymentType PERSISTENT_1, StorageType HDD, and PerUnitStorageThroughput 12,
// and multiples of 1800 GiB for DeploymentType PERSISTENT_1, StorageType HDD, and
// PerUnitStorageThroughput 40.
func RoundUpVolumeSize(volumeSizeBytes int64, deploymentType string, storageType string, perUnitStorageThroughput int64) int64 {
if storageType == fsx.StorageTypeHdd {
if perUnitStorageThroughput == 12 {
return roundUpSize(volumeSizeBytes, 6000*GiB) * 6000
} else {
return roundUpSize(volumeSizeBytes, 3600*GiB) * 3600
return roundUpSize(volumeSizeBytes, 1800*GiB) * 1800
}
} else {
if volumeSizeBytes < 2400*GiB {
return roundUpSize(volumeSizeBytes, 1200*GiB) * 1200
if deploymentType == fsx.LustreDeploymentTypeScratch1 ||
deploymentType == "" {
if volumeSizeBytes < 3600*GiB {
return roundUpSize(volumeSizeBytes, 1200*GiB) * 1200
} else {
return roundUpSize(volumeSizeBytes, 3600*GiB) * 3600
}
} else {
return roundUpSize(volumeSizeBytes, 2400*GiB) * 2400
if volumeSizeBytes < 2400*GiB {
return roundUpSize(volumeSizeBytes, 1200*GiB) * 1200
} else {
return roundUpSize(volumeSizeBytes, 2400*GiB) * 2400
}
}
}
}
Expand Down

0 comments on commit 9182300

Please sign in to comment.