From adc71854e2c58394333abe15af29e964ab183760 Mon Sep 17 00:00:00 2001 From: Harsh Desai Date: Fri, 16 Mar 2018 14:36:21 -0700 Subject: [PATCH] Add support to resize Portworx volume Closes #62305 Signed-off-by: Harsh Desai update comment and variable references to GiB Signed-off-by: Harsh Desai explicitly check volume size after resize and fix size volume spec Signed-off-by: Harsh Desai If Portworx volume is already greater than new size, skip resize Signed-off-by: Harsh Desai Allow updated volume to be greater than requested size Signed-off-by: Harsh Desai --- pkg/volume/portworx/BUILD | 1 + pkg/volume/portworx/portworx.go | 27 +++++++- pkg/volume/portworx/portworx_test.go | 7 ++- pkg/volume/portworx/portworx_util.go | 63 +++++++++++++++++-- .../persistentvolume/resize/admission.go | 2 +- 5 files changed, 90 insertions(+), 10 deletions(-) diff --git a/pkg/volume/portworx/BUILD b/pkg/volume/portworx/BUILD index 321306515b2..40e6e6541bb 100644 --- a/pkg/volume/portworx/BUILD +++ b/pkg/volume/portworx/BUILD @@ -15,6 +15,7 @@ go_test( "//pkg/volume:go_default_library", "//pkg/volume/testing:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/client-go/util/testing:go_default_library", ], diff --git a/pkg/volume/portworx/portworx.go b/pkg/volume/portworx/portworx.go index 70b6ece9366..c0cde9fde9e 100644 --- a/pkg/volume/portworx/portworx.go +++ b/pkg/volume/portworx/portworx.go @@ -50,6 +50,7 @@ var _ volume.VolumePlugin = &portworxVolumePlugin{} var _ volume.PersistentVolumePlugin = &portworxVolumePlugin{} var _ volume.DeletableVolumePlugin = &portworxVolumePlugin{} var _ volume.ProvisionableVolumePlugin = &portworxVolumePlugin{} +var _ volume.ExpandableVolumePlugin = &portworxVolumePlugin{} const ( portworxVolumePluginName = "kubernetes.io/portworx-volume" @@ -171,6 +172,24 @@ func (plugin *portworxVolumePlugin) newProvisionerInternal(options volume.Volume }, nil } +func (plugin *portworxVolumePlugin) RequiresFSResize() bool { + return false +} + +func (plugin *portworxVolumePlugin) ExpandVolumeDevice( + spec *volume.Spec, + newSize resource.Quantity, + oldSize resource.Quantity) (resource.Quantity, error) { + glog.V(4).Infof("Expanding: %s from %v to %v", spec.Name(), oldSize, newSize) + err := plugin.util.ResizeVolume(spec, newSize, plugin.host) + if err != nil { + return oldSize, err + } + + glog.V(4).Infof("Successfully resized %s to %v", spec.Name(), newSize) + return newSize, nil +} + func (plugin *portworxVolumePlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) { portworxVolume := &v1.Volume{ Name: volumeName, @@ -206,7 +225,7 @@ func getVolumeSource( // Abstract interface to PD operations. type portworxManager interface { // Creates a volume - CreateVolume(provisioner *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int, labels map[string]string, err error) + CreateVolume(provisioner *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int64, labels map[string]string, err error) // Deletes a volume DeleteVolume(deleter *portworxVolumeDeleter) error // Attach a volume @@ -217,6 +236,8 @@ type portworxManager interface { MountVolume(mounter *portworxVolumeMounter, mountDir string) error // Unmount a volume UnmountVolume(unmounter *portworxVolumeUnmounter, mountDir string) error + // Resize a volume + ResizeVolume(spec *volume.Spec, newSize resource.Quantity, host volume.VolumeHost) error } // portworxVolume volumes are portworx block devices @@ -362,7 +383,7 @@ func (c *portworxVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes()) } - volumeID, sizeGB, labels, err := c.manager.CreateVolume(c) + volumeID, sizeGiB, labels, err := c.manager.CreateVolume(c) if err != nil { return nil, err } @@ -379,7 +400,7 @@ func (c *portworxVolumeProvisioner) Provision() (*v1.PersistentVolume, error) { PersistentVolumeReclaimPolicy: c.options.PersistentVolumeReclaimPolicy, AccessModes: c.options.PVC.Spec.AccessModes, Capacity: v1.ResourceList{ - v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)), + v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGiB)), }, PersistentVolumeSource: v1.PersistentVolumeSource{ PortworxVolume: &v1.PortworxVolumeSource{ diff --git a/pkg/volume/portworx/portworx_test.go b/pkg/volume/portworx/portworx_test.go index 074f25c47da..8757dff2551 100644 --- a/pkg/volume/portworx/portworx_test.go +++ b/pkg/volume/portworx/portworx_test.go @@ -23,6 +23,7 @@ import ( "testing" "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/types" utiltesting "k8s.io/client-go/util/testing" "k8s.io/kubernetes/pkg/util/mount" @@ -106,7 +107,7 @@ func (fake *fakePortworxManager) UnmountVolume(c *portworxVolumeUnmounter, mount return nil } -func (fake *fakePortworxManager) CreateVolume(c *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int, labels map[string]string, err error) { +func (fake *fakePortworxManager) CreateVolume(c *portworxVolumeProvisioner) (volumeID string, volumeSizeGB int64, labels map[string]string, err error) { labels = make(map[string]string) labels["fakeportworxmanager"] = "yes" return PortworxTestVolume, 100, labels, nil @@ -119,6 +120,10 @@ func (fake *fakePortworxManager) DeleteVolume(cd *portworxVolumeDeleter) error { return nil } +func (fake *fakePortworxManager) ResizeVolume(spec *volume.Spec, newSize resource.Quantity, volumeHost volume.VolumeHost) error { + return nil +} + func TestPlugin(t *testing.T) { tmpDir, err := utiltesting.MkTmpdir("portworxVolumeTest") if err != nil { diff --git a/pkg/volume/portworx/portworx_util.go b/pkg/volume/portworx/portworx_util.go index 5faed5f1fc3..22003983a23 100644 --- a/pkg/volume/portworx/portworx_util.go +++ b/pkg/volume/portworx/portworx_util.go @@ -17,6 +17,8 @@ limitations under the License. package portworx import ( + "fmt" + "github.com/golang/glog" osdapi "github.com/libopenstorage/openstorage/api" osdclient "github.com/libopenstorage/openstorage/api/client" @@ -24,6 +26,7 @@ import ( osdspec "github.com/libopenstorage/openstorage/api/spec" volumeapi "github.com/libopenstorage/openstorage/volume" "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/volume" @@ -45,7 +48,7 @@ type PortworxVolumeUtil struct { } // CreateVolume creates a Portworx volume. -func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (string, int, map[string]string, error) { +func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (string, int64, map[string]string, error) { driver, err := util.getPortworxDriver(p.plugin.host, false /*localOnly*/) if err != nil || driver == nil { glog.Errorf("Failed to get portworx driver. Err: %v", err) @@ -55,8 +58,8 @@ func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (stri glog.Infof("Creating Portworx volume for PVC: %v", p.options.PVC.Name) capacity := p.options.PVC.Spec.Resources.Requests[v1.ResourceName(v1.ResourceStorage)] - // Portworx Volumes are specified in GB - requestGB := int(volutil.RoundUpSize(capacity.Value(), 1024*1024*1024)) + // Portworx Volumes are specified in GiB + requestGiB := volutil.RoundUpToGiB(capacity) // Perform a best-effort parsing of parameters. Portworx 1.2.9 and later parses volume parameters from // spec.VolumeLabels. So even if below SpecFromOpts() fails to parse certain parameters or @@ -71,7 +74,8 @@ func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (stri // Pass all parameters as volume labels for Portworx server-side processing. spec.VolumeLabels = p.options.Parameters // Update the requested size in the spec - spec.Size = uint64(requestGB * 1024 * 1024 * 1024) + spec.Size = uint64(requestGiB * volutil.GIB) + // Change the Portworx Volume name to PV name if locator == nil { locator = &osdapi.VolumeLocator{ @@ -99,7 +103,7 @@ func (util *PortworxVolumeUtil) CreateVolume(p *portworxVolumeProvisioner) (stri } glog.Infof("Successfully created Portworx volume for PVC: %v", p.options.PVC.Name) - return volumeID, requestGB, nil, err + return volumeID, requestGiB, nil, err } // DeleteVolume deletes a Portworx volume @@ -182,6 +186,55 @@ func (util *PortworxVolumeUtil) UnmountVolume(u *portworxVolumeUnmounter, mountP return nil } +func (util *PortworxVolumeUtil) ResizeVolume(spec *volume.Spec, newSize resource.Quantity, volumeHost volume.VolumeHost) error { + driver, err := util.getPortworxDriver(volumeHost, false /*localOnly*/) + if err != nil || driver == nil { + glog.Errorf("Failed to get portworx driver. Err: %v", err) + return err + } + + vols, err := driver.Inspect([]string{spec.Name()}) + if err != nil { + return err + } + + if len(vols) != 1 { + return fmt.Errorf("failed to inspect Portworx volume: %s. Found: %d volumes", spec.Name(), len(vols)) + } + + vol := vols[0] + newSizeInBytes := uint64(volutil.RoundUpToGiB(newSize) * volutil.GIB) + if vol.Spec.Size >= newSizeInBytes { + glog.Infof("Portworx volume: %s already at size: %d greater than or equal to new "+ + "requested size: %d. Skipping resize.", vol.Spec.Size, newSizeInBytes) + return nil + } + + vol.Spec.Size = newSizeInBytes + err = driver.Set(spec.Name(), vol.Locator, vol.Spec) + if err != nil { + return err + } + + // check if the volume's size actually got updated + vols, err = driver.Inspect([]string{spec.Name()}) + if err != nil { + return err + } + + if len(vols) != 1 { + return fmt.Errorf("failed to inspect resized Portworx volume: %s. Found: %d volumes", spec.Name(), len(vols)) + } + + updatedVol := vols[0] + if updatedVol.Spec.Size < vol.Spec.Size { + return fmt.Errorf("Portworx volume: %s doesn't match expected size after resize. expected:%v actual:%v", + spec.Name(), vol.Spec.Size, updatedVol.Spec.Size) + } + + return nil +} + func isClientValid(client *osdclient.Client) (bool, error) { if client == nil { return false, nil diff --git a/plugin/pkg/admission/storage/persistentvolume/resize/admission.go b/plugin/pkg/admission/storage/persistentvolume/resize/admission.go index 3d04ae812dd..6d6b500aa8c 100644 --- a/plugin/pkg/admission/storage/persistentvolume/resize/admission.go +++ b/plugin/pkg/admission/storage/persistentvolume/resize/admission.go @@ -149,7 +149,7 @@ func (pvcr *persistentVolumeClaimResize) allowResize(pvc, oldPvc *api.Persistent // checkVolumePlugin checks whether the volume plugin supports resize func (pvcr *persistentVolumeClaimResize) checkVolumePlugin(pv *api.PersistentVolume) bool { - if pv.Spec.Glusterfs != nil || pv.Spec.Cinder != nil || pv.Spec.RBD != nil { + if pv.Spec.Glusterfs != nil || pv.Spec.Cinder != nil || pv.Spec.RBD != nil || pv.Spec.PortworxVolume != nil { return true }