cinder.go 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package cinder
  14. import (
  15. "errors"
  16. "fmt"
  17. "os"
  18. "path"
  19. "path/filepath"
  20. "k8s.io/api/core/v1"
  21. "k8s.io/apimachinery/pkg/api/resource"
  22. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  23. "k8s.io/apimachinery/pkg/types"
  24. utilfeature "k8s.io/apiserver/pkg/util/feature"
  25. cloudprovider "k8s.io/cloud-provider"
  26. "k8s.io/klog"
  27. "k8s.io/kubernetes/pkg/cloudprovider/providers/openstack"
  28. "k8s.io/kubernetes/pkg/features"
  29. "k8s.io/kubernetes/pkg/util/mount"
  30. "k8s.io/kubernetes/pkg/volume"
  31. "k8s.io/kubernetes/pkg/volume/util"
  32. "k8s.io/utils/keymutex"
  33. utilstrings "k8s.io/utils/strings"
  34. )
  35. const (
  36. // DefaultCloudConfigPath is the default path for cloud configuration
  37. DefaultCloudConfigPath = "/etc/kubernetes/cloud-config"
  38. )
  39. // ProbeVolumePlugins is the primary entrypoint for volume plugins.
  40. func ProbeVolumePlugins() []volume.VolumePlugin {
  41. return []volume.VolumePlugin{&cinderPlugin{}}
  42. }
  43. // BlockStorageProvider is the interface for accessing cinder functionality.
  44. type BlockStorageProvider interface {
  45. AttachDisk(instanceID, volumeID string) (string, error)
  46. DetachDisk(instanceID, volumeID string) error
  47. DeleteVolume(volumeID string) error
  48. CreateVolume(name string, size int, vtype, availability string, tags *map[string]string) (string, string, string, bool, error)
  49. GetDevicePath(volumeID string) string
  50. InstanceID() (string, error)
  51. GetAttachmentDiskPath(instanceID, volumeID string) (string, error)
  52. OperationPending(diskName string) (bool, string, error)
  53. DiskIsAttached(instanceID, volumeID string) (bool, error)
  54. DiskIsAttachedByName(nodeName types.NodeName, volumeID string) (bool, string, error)
  55. DisksAreAttachedByName(nodeName types.NodeName, volumeIDs []string) (map[string]bool, error)
  56. ShouldTrustDevicePath() bool
  57. Instances() (cloudprovider.Instances, bool)
  58. ExpandVolume(volumeID string, oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error)
  59. }
  60. type cinderPlugin struct {
  61. host volume.VolumeHost
  62. // Guarding SetUp and TearDown operations
  63. volumeLocks keymutex.KeyMutex
  64. }
  65. var _ volume.VolumePlugin = &cinderPlugin{}
  66. var _ volume.PersistentVolumePlugin = &cinderPlugin{}
  67. var _ volume.DeletableVolumePlugin = &cinderPlugin{}
  68. var _ volume.ProvisionableVolumePlugin = &cinderPlugin{}
  69. const (
  70. cinderVolumePluginName = "kubernetes.io/cinder"
  71. )
  72. func getPath(uid types.UID, volName string, host volume.VolumeHost) string {
  73. return host.GetPodVolumeDir(uid, utilstrings.EscapeQualifiedName(cinderVolumePluginName), volName)
  74. }
  75. func (plugin *cinderPlugin) Init(host volume.VolumeHost) error {
  76. plugin.host = host
  77. plugin.volumeLocks = keymutex.NewHashed(0)
  78. return nil
  79. }
  80. func (plugin *cinderPlugin) GetPluginName() string {
  81. return cinderVolumePluginName
  82. }
  83. func (plugin *cinderPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  84. volumeID, _, _, err := getVolumeInfo(spec)
  85. if err != nil {
  86. return "", err
  87. }
  88. return volumeID, nil
  89. }
  90. func (plugin *cinderPlugin) CanSupport(spec *volume.Spec) bool {
  91. return (spec.Volume != nil && spec.Volume.Cinder != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Cinder != nil)
  92. }
  93. func (plugin *cinderPlugin) IsMigratedToCSI() bool {
  94. return utilfeature.DefaultFeatureGate.Enabled(features.CSIMigration) &&
  95. utilfeature.DefaultFeatureGate.Enabled(features.CSIMigrationOpenStack)
  96. }
  97. func (plugin *cinderPlugin) RequiresRemount() bool {
  98. return false
  99. }
  100. func (plugin *cinderPlugin) SupportsMountOption() bool {
  101. return true
  102. }
  103. func (plugin *cinderPlugin) SupportsBulkVolumeVerification() bool {
  104. return false
  105. }
  106. var _ volume.VolumePluginWithAttachLimits = &cinderPlugin{}
  107. func (plugin *cinderPlugin) GetVolumeLimits() (map[string]int64, error) {
  108. volumeLimits := map[string]int64{
  109. util.CinderVolumeLimitKey: util.DefaultMaxCinderVolumes,
  110. }
  111. cloud := plugin.host.GetCloudProvider()
  112. // if we can't fetch cloudprovider we return an error
  113. // hoping external CCM or admin can set it. Returning
  114. // default values from here will mean, no one can
  115. // override them.
  116. if cloud == nil {
  117. return nil, fmt.Errorf("No cloudprovider present")
  118. }
  119. if cloud.ProviderName() != openstack.ProviderName {
  120. return nil, fmt.Errorf("Expected Openstack cloud, found %s", cloud.ProviderName())
  121. }
  122. openstackCloud, ok := cloud.(*openstack.OpenStack)
  123. if ok && openstackCloud.NodeVolumeAttachLimit() > 0 {
  124. volumeLimits[util.CinderVolumeLimitKey] = int64(openstackCloud.NodeVolumeAttachLimit())
  125. }
  126. return volumeLimits, nil
  127. }
  128. func (plugin *cinderPlugin) VolumeLimitKey(spec *volume.Spec) string {
  129. return util.CinderVolumeLimitKey
  130. }
  131. func (plugin *cinderPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
  132. return []v1.PersistentVolumeAccessMode{
  133. v1.ReadWriteOnce,
  134. }
  135. }
  136. func (plugin *cinderPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
  137. return plugin.newMounterInternal(spec, pod.UID, &DiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
  138. }
  139. func (plugin *cinderPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager cdManager, mounter mount.Interface) (volume.Mounter, error) {
  140. pdName, fsType, readOnly, err := getVolumeInfo(spec)
  141. if err != nil {
  142. return nil, err
  143. }
  144. return &cinderVolumeMounter{
  145. cinderVolume: &cinderVolume{
  146. podUID: podUID,
  147. volName: spec.Name(),
  148. pdName: pdName,
  149. mounter: mounter,
  150. manager: manager,
  151. plugin: plugin,
  152. MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, spec.Name(), plugin.host)),
  153. },
  154. fsType: fsType,
  155. readOnly: readOnly,
  156. blockDeviceMounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
  157. mountOptions: util.MountOptionFromSpec(spec),
  158. }, nil
  159. }
  160. func (plugin *cinderPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
  161. return plugin.newUnmounterInternal(volName, podUID, &DiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
  162. }
  163. func (plugin *cinderPlugin) newUnmounterInternal(volName string, podUID types.UID, manager cdManager, mounter mount.Interface) (volume.Unmounter, error) {
  164. return &cinderVolumeUnmounter{
  165. &cinderVolume{
  166. podUID: podUID,
  167. volName: volName,
  168. manager: manager,
  169. mounter: mounter,
  170. plugin: plugin,
  171. MetricsProvider: volume.NewMetricsStatFS(getPath(podUID, volName, plugin.host)),
  172. }}, nil
  173. }
  174. func (plugin *cinderPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
  175. return plugin.newDeleterInternal(spec, &DiskUtil{})
  176. }
  177. func (plugin *cinderPlugin) newDeleterInternal(spec *volume.Spec, manager cdManager) (volume.Deleter, error) {
  178. if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.Cinder == nil {
  179. return nil, fmt.Errorf("spec.PersistentVolumeSource.Cinder is nil")
  180. }
  181. return &cinderVolumeDeleter{
  182. &cinderVolume{
  183. volName: spec.Name(),
  184. pdName: spec.PersistentVolume.Spec.Cinder.VolumeID,
  185. manager: manager,
  186. plugin: plugin,
  187. }}, nil
  188. }
  189. func (plugin *cinderPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
  190. return plugin.newProvisionerInternal(options, &DiskUtil{})
  191. }
  192. func (plugin *cinderPlugin) newProvisionerInternal(options volume.VolumeOptions, manager cdManager) (volume.Provisioner, error) {
  193. return &cinderVolumeProvisioner{
  194. cinderVolume: &cinderVolume{
  195. manager: manager,
  196. plugin: plugin,
  197. },
  198. options: options,
  199. }, nil
  200. }
  201. func (plugin *cinderPlugin) getCloudProvider() (BlockStorageProvider, error) {
  202. cloud := plugin.host.GetCloudProvider()
  203. if cloud == nil {
  204. if _, err := os.Stat(DefaultCloudConfigPath); err == nil {
  205. var config *os.File
  206. config, err = os.Open(DefaultCloudConfigPath)
  207. if err != nil {
  208. return nil, fmt.Errorf("unable to load OpenStack configuration from default path : %v", err)
  209. }
  210. defer config.Close()
  211. cloud, err = cloudprovider.GetCloudProvider(openstack.ProviderName, config)
  212. if err != nil {
  213. return nil, fmt.Errorf("unable to create OpenStack cloud provider from default path : %v", err)
  214. }
  215. } else {
  216. return nil, fmt.Errorf("OpenStack cloud provider was not initialized properly : %v", err)
  217. }
  218. }
  219. switch cloud := cloud.(type) {
  220. case *openstack.OpenStack:
  221. return cloud, nil
  222. default:
  223. return nil, errors.New("invalid cloud provider: expected OpenStack")
  224. }
  225. }
  226. func (plugin *cinderPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
  227. mounter := plugin.host.GetMounter(plugin.GetPluginName())
  228. pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
  229. sourceName, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir)
  230. if err != nil {
  231. return nil, err
  232. }
  233. klog.V(4).Infof("Found volume %s mounted to %s", sourceName, mountPath)
  234. cinderVolume := &v1.Volume{
  235. Name: volumeName,
  236. VolumeSource: v1.VolumeSource{
  237. Cinder: &v1.CinderVolumeSource{
  238. VolumeID: sourceName,
  239. },
  240. },
  241. }
  242. return volume.NewSpecFromVolume(cinderVolume), nil
  243. }
  244. var _ volume.ExpandableVolumePlugin = &cinderPlugin{}
  245. func (plugin *cinderPlugin) ExpandVolumeDevice(spec *volume.Spec, newSize resource.Quantity, oldSize resource.Quantity) (resource.Quantity, error) {
  246. volumeID, _, _, err := getVolumeInfo(spec)
  247. if err != nil {
  248. return oldSize, err
  249. }
  250. cloud, err := plugin.getCloudProvider()
  251. if err != nil {
  252. return oldSize, err
  253. }
  254. expandedSize, err := cloud.ExpandVolume(volumeID, oldSize, newSize)
  255. if err != nil {
  256. return oldSize, err
  257. }
  258. klog.V(2).Infof("volume %s expanded to new size %d successfully", volumeID, int(newSize.Value()))
  259. return expandedSize, nil
  260. }
  261. func (plugin *cinderPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) {
  262. _, err := util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath)
  263. if err != nil {
  264. return false, err
  265. }
  266. return true, nil
  267. }
  268. var _ volume.NodeExpandableVolumePlugin = &cinderPlugin{}
  269. func (plugin *cinderPlugin) RequiresFSResize() bool {
  270. return true
  271. }
  272. // Abstract interface to PD operations.
  273. type cdManager interface {
  274. // Attaches the disk to the kubelet's host machine.
  275. AttachDisk(mounter *cinderVolumeMounter, globalPDPath string) error
  276. // Detaches the disk from the kubelet's host machine.
  277. DetachDisk(unmounter *cinderVolumeUnmounter) error
  278. // Creates a volume
  279. CreateVolume(provisioner *cinderVolumeProvisioner, node *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (volumeID string, volumeSizeGB int, labels map[string]string, fstype string, err error)
  280. // Deletes a volume
  281. DeleteVolume(deleter *cinderVolumeDeleter) error
  282. }
  283. var _ volume.Mounter = &cinderVolumeMounter{}
  284. type cinderVolumeMounter struct {
  285. *cinderVolume
  286. fsType string
  287. readOnly bool
  288. blockDeviceMounter *mount.SafeFormatAndMount
  289. mountOptions []string
  290. }
  291. // cinderPersistentDisk volumes are disk resources provided by C3
  292. // that are attached to the kubelet's host machine and exposed to the pod.
  293. type cinderVolume struct {
  294. volName string
  295. podUID types.UID
  296. // Unique identifier of the volume, used to find the disk resource in the provider.
  297. pdName string
  298. // Filesystem type, optional.
  299. fsType string
  300. // Specifies whether the disk will be attached as read-only.
  301. readOnly bool
  302. // Utility interface that provides API calls to the provider to attach/detach disks.
  303. manager cdManager
  304. // Mounter interface that provides system calls to mount the global path to the pod local path.
  305. mounter mount.Interface
  306. // diskMounter provides the interface that is used to mount the actual block device.
  307. blockDeviceMounter mount.Interface
  308. plugin *cinderPlugin
  309. volume.MetricsProvider
  310. }
  311. func (b *cinderVolumeMounter) GetAttributes() volume.Attributes {
  312. return volume.Attributes{
  313. ReadOnly: b.readOnly,
  314. Managed: !b.readOnly,
  315. SupportsSELinux: true,
  316. }
  317. }
  318. // Checks prior to mount operations to verify that the required components (binaries, etc.)
  319. // to mount the volume are available on the underlying node.
  320. // If not, it returns an error
  321. func (b *cinderVolumeMounter) CanMount() error {
  322. return nil
  323. }
  324. func (b *cinderVolumeMounter) SetUp(mounterArgs volume.MounterArgs) error {
  325. return b.SetUpAt(b.GetPath(), mounterArgs)
  326. }
  327. // SetUp bind mounts to the volume path.
  328. func (b *cinderVolumeMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
  329. klog.V(5).Infof("Cinder SetUp %s to %s", b.pdName, dir)
  330. b.plugin.volumeLocks.LockKey(b.pdName)
  331. defer b.plugin.volumeLocks.UnlockKey(b.pdName)
  332. notmnt, err := b.mounter.IsLikelyNotMountPoint(dir)
  333. if err != nil && !os.IsNotExist(err) {
  334. klog.Errorf("Cannot validate mount point: %s %v", dir, err)
  335. return err
  336. }
  337. if !notmnt {
  338. klog.V(4).Infof("Something is already mounted to target %s", dir)
  339. return nil
  340. }
  341. globalPDPath := makeGlobalPDName(b.plugin.host, b.pdName)
  342. options := []string{"bind"}
  343. if b.readOnly {
  344. options = append(options, "ro")
  345. }
  346. if err := os.MkdirAll(dir, 0750); err != nil {
  347. klog.V(4).Infof("Could not create directory %s: %v", dir, err)
  348. return err
  349. }
  350. mountOptions := util.JoinMountOptions(options, b.mountOptions)
  351. // Perform a bind mount to the full path to allow duplicate mounts of the same PD.
  352. klog.V(4).Infof("Attempting to mount cinder volume %s to %s with options %v", b.pdName, dir, mountOptions)
  353. err = b.mounter.Mount(globalPDPath, dir, "", options)
  354. if err != nil {
  355. klog.V(4).Infof("Mount failed: %v", err)
  356. notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
  357. if mntErr != nil {
  358. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  359. return err
  360. }
  361. if !notmnt {
  362. if mntErr = b.mounter.Unmount(dir); mntErr != nil {
  363. klog.Errorf("Failed to unmount: %v", mntErr)
  364. return err
  365. }
  366. notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
  367. if mntErr != nil {
  368. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  369. return err
  370. }
  371. if !notmnt {
  372. // This is very odd, we don't expect it. We'll try again next sync loop.
  373. klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", b.GetPath())
  374. return err
  375. }
  376. }
  377. os.Remove(dir)
  378. klog.Errorf("Failed to mount %s: %v", dir, err)
  379. return err
  380. }
  381. if !b.readOnly {
  382. volume.SetVolumeOwnership(b, mounterArgs.FsGroup)
  383. }
  384. klog.V(3).Infof("Cinder volume %s mounted to %s", b.pdName, dir)
  385. return nil
  386. }
  387. func makeGlobalPDName(host volume.VolumeHost, devName string) string {
  388. return filepath.Join(host.GetPluginDir(cinderVolumePluginName), util.MountsInGlobalPDPath, devName)
  389. }
  390. func (cd *cinderVolume) GetPath() string {
  391. return getPath(cd.podUID, cd.volName, cd.plugin.host)
  392. }
  393. type cinderVolumeUnmounter struct {
  394. *cinderVolume
  395. }
  396. var _ volume.Unmounter = &cinderVolumeUnmounter{}
  397. func (c *cinderVolumeUnmounter) TearDown() error {
  398. return c.TearDownAt(c.GetPath())
  399. }
  400. // Unmounts the bind mount, and detaches the disk only if the PD
  401. // resource was the last reference to that disk on the kubelet.
  402. func (c *cinderVolumeUnmounter) TearDownAt(dir string) error {
  403. if pathExists, pathErr := mount.PathExists(dir); pathErr != nil {
  404. return fmt.Errorf("Error checking if path exists: %v", pathErr)
  405. } else if !pathExists {
  406. klog.Warningf("Warning: Unmount skipped because path does not exist: %v", dir)
  407. return nil
  408. }
  409. klog.V(5).Infof("Cinder TearDown of %s", dir)
  410. notmnt, err := c.mounter.IsLikelyNotMountPoint(dir)
  411. if err != nil {
  412. klog.V(4).Infof("IsLikelyNotMountPoint check failed: %v", err)
  413. return err
  414. }
  415. if notmnt {
  416. klog.V(4).Infof("Nothing is mounted to %s, ignoring", dir)
  417. return os.Remove(dir)
  418. }
  419. // Find Cinder volumeID to lock the right volume
  420. // TODO: refactor VolumePlugin.NewUnmounter to get full volume.Spec just like
  421. // NewMounter. We could then find volumeID there without probing MountRefs.
  422. refs, err := c.mounter.GetMountRefs(dir)
  423. if err != nil {
  424. klog.V(4).Infof("GetMountRefs failed: %v", err)
  425. return err
  426. }
  427. if len(refs) == 0 {
  428. klog.V(4).Infof("Directory %s is not mounted", dir)
  429. return fmt.Errorf("directory %s is not mounted", dir)
  430. }
  431. c.pdName = path.Base(refs[0])
  432. klog.V(4).Infof("Found volume %s mounted to %s", c.pdName, dir)
  433. // lock the volume (and thus wait for any concurrrent SetUpAt to finish)
  434. c.plugin.volumeLocks.LockKey(c.pdName)
  435. defer c.plugin.volumeLocks.UnlockKey(c.pdName)
  436. // Reload list of references, there might be SetUpAt finished in the meantime
  437. refs, err = c.mounter.GetMountRefs(dir)
  438. if err != nil {
  439. klog.V(4).Infof("GetMountRefs failed: %v", err)
  440. return err
  441. }
  442. if err := c.mounter.Unmount(dir); err != nil {
  443. klog.V(4).Infof("Unmount failed: %v", err)
  444. return err
  445. }
  446. klog.V(3).Infof("Successfully unmounted: %s\n", dir)
  447. notmnt, mntErr := c.mounter.IsLikelyNotMountPoint(dir)
  448. if mntErr != nil {
  449. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  450. return err
  451. }
  452. if notmnt {
  453. if err := os.Remove(dir); err != nil {
  454. klog.V(4).Infof("Failed to remove directory after unmount: %v", err)
  455. return err
  456. }
  457. }
  458. return nil
  459. }
  460. type cinderVolumeDeleter struct {
  461. *cinderVolume
  462. }
  463. var _ volume.Deleter = &cinderVolumeDeleter{}
  464. func (r *cinderVolumeDeleter) GetPath() string {
  465. return getPath(r.podUID, r.volName, r.plugin.host)
  466. }
  467. func (r *cinderVolumeDeleter) Delete() error {
  468. return r.manager.DeleteVolume(r)
  469. }
  470. type cinderVolumeProvisioner struct {
  471. *cinderVolume
  472. options volume.VolumeOptions
  473. }
  474. var _ volume.Provisioner = &cinderVolumeProvisioner{}
  475. func (c *cinderVolumeProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
  476. if !util.AccessModesContainedInAll(c.plugin.GetAccessModes(), c.options.PVC.Spec.AccessModes) {
  477. return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", c.options.PVC.Spec.AccessModes, c.plugin.GetAccessModes())
  478. }
  479. volumeID, sizeGB, labels, fstype, err := c.manager.CreateVolume(c, selectedNode, allowedTopologies)
  480. if err != nil {
  481. return nil, err
  482. }
  483. var volumeMode *v1.PersistentVolumeMode
  484. if utilfeature.DefaultFeatureGate.Enabled(features.BlockVolume) {
  485. volumeMode = c.options.PVC.Spec.VolumeMode
  486. if volumeMode != nil && *volumeMode == v1.PersistentVolumeBlock {
  487. // Block volumes should not have any FSType
  488. fstype = ""
  489. }
  490. }
  491. pv := &v1.PersistentVolume{
  492. ObjectMeta: metav1.ObjectMeta{
  493. Name: c.options.PVName,
  494. Labels: labels,
  495. Annotations: map[string]string{
  496. util.VolumeDynamicallyCreatedByKey: "cinder-dynamic-provisioner",
  497. },
  498. },
  499. Spec: v1.PersistentVolumeSpec{
  500. PersistentVolumeReclaimPolicy: c.options.PersistentVolumeReclaimPolicy,
  501. AccessModes: c.options.PVC.Spec.AccessModes,
  502. Capacity: v1.ResourceList{
  503. v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
  504. },
  505. VolumeMode: volumeMode,
  506. PersistentVolumeSource: v1.PersistentVolumeSource{
  507. Cinder: &v1.CinderPersistentVolumeSource{
  508. VolumeID: volumeID,
  509. FSType: fstype,
  510. ReadOnly: false,
  511. },
  512. },
  513. MountOptions: c.options.MountOptions,
  514. },
  515. }
  516. if len(c.options.PVC.Spec.AccessModes) == 0 {
  517. pv.Spec.AccessModes = c.plugin.GetAccessModes()
  518. }
  519. requirements := make([]v1.NodeSelectorRequirement, 0)
  520. for k, v := range labels {
  521. if v != "" {
  522. requirements = append(requirements, v1.NodeSelectorRequirement{Key: k, Operator: v1.NodeSelectorOpIn, Values: []string{v}})
  523. }
  524. }
  525. if len(requirements) > 0 {
  526. pv.Spec.NodeAffinity = new(v1.VolumeNodeAffinity)
  527. pv.Spec.NodeAffinity.Required = new(v1.NodeSelector)
  528. pv.Spec.NodeAffinity.Required.NodeSelectorTerms = make([]v1.NodeSelectorTerm, 1)
  529. pv.Spec.NodeAffinity.Required.NodeSelectorTerms[0].MatchExpressions = requirements
  530. }
  531. return pv, nil
  532. }
  533. func getVolumeInfo(spec *volume.Spec) (string, string, bool, error) {
  534. if spec.Volume != nil && spec.Volume.Cinder != nil {
  535. return spec.Volume.Cinder.VolumeID, spec.Volume.Cinder.FSType, spec.Volume.Cinder.ReadOnly, nil
  536. } else if spec.PersistentVolume != nil &&
  537. spec.PersistentVolume.Spec.Cinder != nil {
  538. return spec.PersistentVolume.Spec.Cinder.VolumeID, spec.PersistentVolume.Spec.Cinder.FSType, spec.ReadOnly, nil
  539. }
  540. return "", "", false, fmt.Errorf("Spec does not reference a Cinder volume type")
  541. }