cinder.go 20 KB

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