@@ -24,9 +24,11 @@ import (
2424 "strings"
2525 "time"
2626
27+ "k8s.io/apimachinery/pkg/api/resource"
2728 k8s_volume "k8s.io/kubernetes/pkg/volume"
2829
2930 "github.com/gophercloud/gophercloud"
31+ volumeexpand "github.com/gophercloud/gophercloud/openstack/blockstorage/extensions/volumeactions"
3032 volumes_v1 "github.com/gophercloud/gophercloud/openstack/blockstorage/v1/volumes"
3133 volumes_v2 "github.com/gophercloud/gophercloud/openstack/blockstorage/v2/volumes"
3234 "github.com/gophercloud/gophercloud/openstack/compute/v2/extensions/volumeattach"
@@ -39,6 +41,7 @@ type volumeService interface {
3941 createVolume (opts VolumeCreateOpts ) (string , string , error )
4042 getVolume (volumeID string ) (Volume , error )
4143 deleteVolume (volumeName string ) error
44+ expandVolume (volumeID string , newSize int ) error
4245}
4346
4447// Volumes implementation for v1
@@ -64,6 +67,8 @@ type Volume struct {
6467 Name string
6568 // Current status of the volume.
6669 Status string
70+ // Volume size in GB
71+ Size int
6772}
6873
6974type VolumeCreateOpts struct {
@@ -139,6 +144,7 @@ func (volumes *VolumesV1) getVolume(volumeID string) (Volume, error) {
139144 ID : volumeV1 .ID ,
140145 Name : volumeV1 .Name ,
141146 Status : volumeV1 .Status ,
147+ Size : volumeV1 .Size ,
142148 }
143149
144150 if len (volumeV1 .Attachments ) > 0 && volumeV1 .Attachments [0 ]["server_id" ] != nil {
@@ -162,6 +168,7 @@ func (volumes *VolumesV2) getVolume(volumeID string) (Volume, error) {
162168 ID : volumeV2 .ID ,
163169 Name : volumeV2 .Name ,
164170 Status : volumeV2 .Status ,
171+ Size : volumeV2 .Size ,
165172 }
166173
167174 if len (volumeV2 .Attachments ) > 0 {
@@ -188,6 +195,30 @@ func (volumes *VolumesV2) deleteVolume(volumeID string) error {
188195 return err
189196}
190197
198+ func (volumes * VolumesV1 ) expandVolume (volumeID string , newSize int ) error {
199+ startTime := time .Now ()
200+ create_opts := volumeexpand.ExtendSizeOpts {
201+ NewSize : newSize ,
202+ }
203+ err := volumeexpand .ExtendSize (volumes .blockstorage , volumeID , create_opts ).ExtractErr ()
204+ timeTaken := time .Since (startTime ).Seconds ()
205+ recordOpenstackOperationMetric ("expand_volume" , timeTaken , err )
206+
207+ return err
208+ }
209+
210+ func (volumes * VolumesV2 ) expandVolume (volumeID string , newSize int ) error {
211+ startTime := time .Now ()
212+ create_opts := volumeexpand.ExtendSizeOpts {
213+ NewSize : newSize ,
214+ }
215+ err := volumeexpand .ExtendSize (volumes .blockstorage , volumeID , create_opts ).ExtractErr ()
216+ timeTaken := time .Since (startTime ).Seconds ()
217+ recordOpenstackOperationMetric ("expand_volume" , timeTaken , err )
218+
219+ return err
220+ }
221+
191222func (os * OpenStack ) OperationPending (diskName string ) (bool , string , error ) {
192223 volume , err := os .getVolume (diskName )
193224 if err != nil {
@@ -274,6 +305,39 @@ func (os *OpenStack) DetachDisk(instanceID, volumeID string) error {
274305 return nil
275306}
276307
308+ // ExpandVolume expands the size of specific cinder volume (in GiB)
309+ func (os * OpenStack ) ExpandVolume (volumeID string , oldSize resource.Quantity , newSize resource.Quantity ) (resource.Quantity , error ) {
310+ volume , err := os .getVolume (volumeID )
311+ if err != nil {
312+ return oldSize , err
313+ }
314+ if volume .Status != VolumeAvailableStatus {
315+ // cinder volume can not be expanded if its status is not available
316+ return oldSize , fmt .Errorf ("volume status is not available" )
317+ }
318+
319+ volSizeBytes := newSize .Value ()
320+ // Cinder works with gigabytes, convert to GiB with rounding up
321+ volSizeGB := int (k8s_volume .RoundUpSize (volSizeBytes , 1024 * 1024 * 1024 ))
322+ newSizeQuant := resource .MustParse (fmt .Sprintf ("%dGi" , volSizeGB ))
323+
324+ // if volume size equals to or greater than the newSize, return nil
325+ if volume .Size >= volSizeGB {
326+ return newSizeQuant , nil
327+ }
328+
329+ volumes , err := os .volumeService ("" )
330+ if err != nil {
331+ return oldSize , err
332+ }
333+
334+ err = volumes .expandVolume (volumeID , volSizeGB )
335+ if err != nil {
336+ return oldSize , err
337+ }
338+ return newSizeQuant , nil
339+ }
340+
277341// getVolume retrieves Volume by its ID.
278342func (os * OpenStack ) getVolume (volumeID string ) (Volume , error ) {
279343 volumes , err := os .volumeService ("" )
0 commit comments