diff --git a/examples/experimental/persistent-volume-provisioning/README.md b/examples/experimental/persistent-volume-provisioning/README.md index 16c7cbef242..280cffb097b 100644 --- a/examples/experimental/persistent-volume-provisioning/README.md +++ b/examples/experimental/persistent-volume-provisioning/README.md @@ -105,6 +105,22 @@ parameters: * `restuser` : Gluster REST service user who has access to create volumes in the Gluster Trusted Pool. * `restuserkey` : Gluster REST service user's password which will be used for authentication to the REST server. +#### OpenStack Cinder + +```yaml +kind: StorageClass +apiVersion: extensions/v1beta1 +metadata: + name: gold +provisioner: kubernetes.io/cinder +parameters: + type: fast + availability: nova +``` + +* `type`: [VolumeType](http://docs.openstack.org/admin-guide/dashboard-manage-volumes.html) created in Cinder. Default is empty. +* `availability`: Availability Zone. Default is empty. + ### User provisioning requests Users request dynamically provisioned storage by including a storage class in their `PersistentVolumeClaim`. diff --git a/pkg/cloudprovider/providers/openstack/openstack_test.go b/pkg/cloudprovider/providers/openstack/openstack_test.go index b9978a10433..2cd4f09e371 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_test.go +++ b/pkg/cloudprovider/providers/openstack/openstack_test.go @@ -268,7 +268,7 @@ func TestVolumes(t *testing.T) { tags := map[string]string{ "test": "value", } - vol, err := os.CreateVolume("kubernetes-test-volume-"+rand.String(10), 1, &tags) + vol, err := os.CreateVolume("kubernetes-test-volume-"+rand.String(10), 1, "", "", &tags) if err != nil { t.Fatalf("Cannot create a new Cinder volume: %v", err) } diff --git a/pkg/cloudprovider/providers/openstack/openstack_volumes.go b/pkg/cloudprovider/providers/openstack/openstack_volumes.go index 570913098b8..2485d759519 100644 --- a/pkg/cloudprovider/providers/openstack/openstack_volumes.go +++ b/pkg/cloudprovider/providers/openstack/openstack_volumes.go @@ -136,7 +136,7 @@ func (os *OpenStack) getVolume(diskName string) (volumes.Volume, error) { } // Create a volume of given size (in GiB) -func (os *OpenStack) CreateVolume(name string, size int, tags *map[string]string) (volumeName string, err error) { +func (os *OpenStack) CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (volumeName string, err error) { sClient, err := openstack.NewBlockStorageV1(os.provider, gophercloud.EndpointOpts{ Region: os.region, @@ -148,8 +148,10 @@ func (os *OpenStack) CreateVolume(name string, size int, tags *map[string]string } opts := volumes.CreateOpts{ - Name: name, - Size: size, + Name: name, + Size: size, + VolumeType: vtype, + Availability: availability, } if tags != nil { opts.Metadata = *tags diff --git a/pkg/cloudprovider/providers/rackspace/rackspace.go b/pkg/cloudprovider/providers/rackspace/rackspace.go index 6b3a27d07fe..69a26944cd6 100644 --- a/pkg/cloudprovider/providers/rackspace/rackspace.go +++ b/pkg/cloudprovider/providers/rackspace/rackspace.go @@ -475,7 +475,7 @@ func (os *Rackspace) GetZone() (cloudprovider.Zone, error) { } // Create a volume of given size (in GiB) -func (rs *Rackspace) CreateVolume(name string, size int, tags *map[string]string) (volumeName string, err error) { +func (rs *Rackspace) CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (volumeName string, err error) { return "", errors.New("unimplemented") } diff --git a/pkg/volume/cinder/attacher_test.go b/pkg/volume/cinder/attacher_test.go index dbc19273a82..0edb08c2a10 100644 --- a/pkg/volume/cinder/attacher_test.go +++ b/pkg/volume/cinder/attacher_test.go @@ -391,7 +391,7 @@ func (testcase *testcase) GetAttachmentDiskPath(instanceID string, diskName stri return expected.retPath, expected.ret } -func (testcase *testcase) CreateVolume(name string, size int, tags *map[string]string) (volumeName string, err error) { +func (testcase *testcase) CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (volumeName string, err error) { return "", errors.New("Not implemented") } diff --git a/pkg/volume/cinder/cinder.go b/pkg/volume/cinder/cinder.go index 24b9d073a19..9e441929706 100644 --- a/pkg/volume/cinder/cinder.go +++ b/pkg/volume/cinder/cinder.go @@ -45,7 +45,7 @@ type CinderProvider interface { AttachDisk(instanceID string, diskName string) (string, error) DetachDisk(instanceID string, partialDiskId string) error DeleteVolume(volumeName string) error - CreateVolume(name string, size int, tags *map[string]string) (volumeName string, err error) + CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (volumeName string, err error) GetDevicePath(diskId string) string InstanceID() (string, error) GetAttachmentDiskPath(instanceID string, diskName string) (string, error) diff --git a/pkg/volume/cinder/cinder_util.go b/pkg/volume/cinder/cinder_util.go index afd22d2cb33..69d83eba7ee 100644 --- a/pkg/volume/cinder/cinder_util.go +++ b/pkg/volume/cinder/cinder_util.go @@ -18,7 +18,9 @@ package cinder import ( "errors" + "fmt" "os" + "strings" "time" "github.com/golang/glog" @@ -139,7 +141,26 @@ func (util *CinderDiskUtil) CreateVolume(c *cinderVolumeProvisioner) (volumeID s // Cinder works with gigabytes, convert to GiB with rounding up volSizeGB := int(volume.RoundUpSize(volSizeBytes, 1024*1024*1024)) name := volume.GenerateVolumeName(c.options.ClusterName, c.options.PVName, 255) // Cinder volume name can have up to 255 characters - name, err = cloud.CreateVolume(name, volSizeGB, c.options.CloudTags) + vtype := "" + availability := "" + // Apply ProvisionerParameters (case-insensitive). We leave validation of + // the values to the cloud provider. + for k, v := range c.options.Parameters { + switch strings.ToLower(k) { + case "type": + vtype = v + case "availability": + availability = v + default: + return "", 0, fmt.Errorf("invalid option %q for volume plugin %s", k, c.plugin.GetPluginName()) + } + } + // TODO: implement c.options.ProvisionerSelector parsing + if c.options.Selector != nil { + return "", 0, fmt.Errorf("claim.Spec.Selector is not supported for dynamic provisioning on Cinder") + } + + name, err = cloud.CreateVolume(name, volSizeGB, vtype, availability, c.options.CloudTags) if err != nil { glog.V(2).Infof("Error creating cinder volume: %v", err) return "", 0, err