photon_pd.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  1. /*
  2. Copyright 2016 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 photon_pd
  14. import (
  15. "fmt"
  16. "os"
  17. "path/filepath"
  18. "k8s.io/api/core/v1"
  19. "k8s.io/apimachinery/pkg/api/resource"
  20. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  21. "k8s.io/apimachinery/pkg/types"
  22. "k8s.io/klog"
  23. "k8s.io/kubernetes/pkg/util/mount"
  24. "k8s.io/kubernetes/pkg/volume"
  25. "k8s.io/kubernetes/pkg/volume/util"
  26. utilstrings "k8s.io/utils/strings"
  27. )
  28. // This is the primary entrypoint for volume plugins.
  29. func ProbeVolumePlugins() []volume.VolumePlugin {
  30. return []volume.VolumePlugin{&photonPersistentDiskPlugin{}}
  31. }
  32. type photonPersistentDiskPlugin struct {
  33. host volume.VolumeHost
  34. }
  35. var _ volume.VolumePlugin = &photonPersistentDiskPlugin{}
  36. var _ volume.PersistentVolumePlugin = &photonPersistentDiskPlugin{}
  37. var _ volume.DeletableVolumePlugin = &photonPersistentDiskPlugin{}
  38. var _ volume.ProvisionableVolumePlugin = &photonPersistentDiskPlugin{}
  39. const (
  40. photonPersistentDiskPluginName = "kubernetes.io/photon-pd"
  41. )
  42. func (plugin *photonPersistentDiskPlugin) Init(host volume.VolumeHost) error {
  43. plugin.host = host
  44. return nil
  45. }
  46. func (plugin *photonPersistentDiskPlugin) GetPluginName() string {
  47. return photonPersistentDiskPluginName
  48. }
  49. func (plugin *photonPersistentDiskPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  50. volumeSource, _, err := getVolumeSource(spec)
  51. if err != nil {
  52. klog.Errorf("Photon volume plugin: GetVolumeName failed to get volume source")
  53. return "", err
  54. }
  55. return volumeSource.PdID, nil
  56. }
  57. func (plugin *photonPersistentDiskPlugin) CanSupport(spec *volume.Spec) bool {
  58. return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.PhotonPersistentDisk != nil) ||
  59. (spec.Volume != nil && spec.Volume.PhotonPersistentDisk != nil)
  60. }
  61. func (plugin *photonPersistentDiskPlugin) IsMigratedToCSI() bool {
  62. return false
  63. }
  64. func (plugin *photonPersistentDiskPlugin) RequiresRemount() bool {
  65. return false
  66. }
  67. func (plugin *photonPersistentDiskPlugin) SupportsMountOption() bool {
  68. return true
  69. }
  70. func (plugin *photonPersistentDiskPlugin) SupportsBulkVolumeVerification() bool {
  71. return false
  72. }
  73. func (plugin *photonPersistentDiskPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
  74. return plugin.newMounterInternal(spec, pod.UID, &PhotonDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
  75. }
  76. func (plugin *photonPersistentDiskPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
  77. return plugin.newUnmounterInternal(volName, podUID, &PhotonDiskUtil{}, plugin.host.GetMounter(plugin.GetPluginName()))
  78. }
  79. func (plugin *photonPersistentDiskPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager pdManager, mounter mount.Interface) (volume.Mounter, error) {
  80. vvol, _, err := getVolumeSource(spec)
  81. if err != nil {
  82. klog.Errorf("Photon volume plugin: newMounterInternal failed to get volume source")
  83. return nil, err
  84. }
  85. pdID := vvol.PdID
  86. fsType := vvol.FSType
  87. return &photonPersistentDiskMounter{
  88. photonPersistentDisk: &photonPersistentDisk{
  89. podUID: podUID,
  90. volName: spec.Name(),
  91. pdID: pdID,
  92. manager: manager,
  93. mounter: mounter,
  94. plugin: plugin,
  95. },
  96. fsType: fsType,
  97. diskMounter: util.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host),
  98. mountOption: util.MountOptionFromSpec(spec),
  99. }, nil
  100. }
  101. func (plugin *photonPersistentDiskPlugin) newUnmounterInternal(volName string, podUID types.UID, manager pdManager, mounter mount.Interface) (volume.Unmounter, error) {
  102. return &photonPersistentDiskUnmounter{
  103. &photonPersistentDisk{
  104. podUID: podUID,
  105. volName: volName,
  106. manager: manager,
  107. mounter: mounter,
  108. plugin: plugin,
  109. }}, nil
  110. }
  111. func (plugin *photonPersistentDiskPlugin) ConstructVolumeSpec(volumeSpecName, mountPath string) (*volume.Spec, error) {
  112. mounter := plugin.host.GetMounter(plugin.GetPluginName())
  113. pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
  114. pdID, err := mounter.GetDeviceNameFromMount(mountPath, pluginMntDir)
  115. if err != nil {
  116. return nil, err
  117. }
  118. photonPersistentDisk := &v1.Volume{
  119. Name: volumeSpecName,
  120. VolumeSource: v1.VolumeSource{
  121. PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
  122. PdID: pdID,
  123. },
  124. },
  125. }
  126. return volume.NewSpecFromVolume(photonPersistentDisk), nil
  127. }
  128. // Abstract interface to disk operations.
  129. type pdManager interface {
  130. // Creates a volume
  131. CreateVolume(provisioner *photonPersistentDiskProvisioner) (pdID string, volumeSizeGB int, fstype string, err error)
  132. // Deletes a volume
  133. DeleteVolume(deleter *photonPersistentDiskDeleter) error
  134. }
  135. // photonPersistentDisk volumes are disk resources are attached to the kubelet's host machine and exposed to the pod.
  136. type photonPersistentDisk struct {
  137. volName string
  138. podUID types.UID
  139. // Unique identifier of the volume, used to find the disk resource in the provider.
  140. pdID string
  141. // Filesystem type, optional.
  142. fsType string
  143. // Utility interface that provides API calls to the provider to attach/detach disks.
  144. manager pdManager
  145. // Mounter interface that provides system calls to mount the global path to the pod local path.
  146. mounter mount.Interface
  147. plugin *photonPersistentDiskPlugin
  148. volume.MetricsNil
  149. }
  150. var _ volume.Mounter = &photonPersistentDiskMounter{}
  151. type photonPersistentDiskMounter struct {
  152. *photonPersistentDisk
  153. fsType string
  154. diskMounter *mount.SafeFormatAndMount
  155. mountOption []string
  156. }
  157. func (b *photonPersistentDiskMounter) GetAttributes() volume.Attributes {
  158. return volume.Attributes{
  159. SupportsSELinux: true,
  160. }
  161. }
  162. // Checks prior to mount operations to verify that the required components (binaries, etc.)
  163. // to mount the volume are available on the underlying node.
  164. // If not, it returns an error
  165. func (b *photonPersistentDiskMounter) CanMount() error {
  166. return nil
  167. }
  168. // SetUp attaches the disk and bind mounts to the volume path.
  169. func (b *photonPersistentDiskMounter) SetUp(mounterArgs volume.MounterArgs) error {
  170. return b.SetUpAt(b.GetPath(), mounterArgs)
  171. }
  172. // SetUp attaches the disk and bind mounts to the volume path.
  173. func (b *photonPersistentDiskMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
  174. klog.V(4).Infof("Photon Persistent Disk setup %s to %s", b.pdID, dir)
  175. // TODO: handle failed mounts here.
  176. notmnt, err := b.mounter.IsLikelyNotMountPoint(dir)
  177. if err != nil && !os.IsNotExist(err) {
  178. klog.Errorf("cannot validate mount point: %s %v", dir, err)
  179. return err
  180. }
  181. if !notmnt {
  182. return nil
  183. }
  184. if err := os.MkdirAll(dir, 0750); err != nil {
  185. klog.Errorf("mkdir failed on disk %s (%v)", dir, err)
  186. return err
  187. }
  188. options := []string{"bind"}
  189. // Perform a bind mount to the full path to allow duplicate mounts of the same PD.
  190. globalPDPath := makeGlobalPDPath(b.plugin.host, b.pdID)
  191. klog.V(4).Infof("attempting to mount %s", dir)
  192. mountOptions := util.JoinMountOptions(options, b.mountOption)
  193. err = b.mounter.Mount(globalPDPath, dir, "", mountOptions)
  194. if err != nil {
  195. notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
  196. if mntErr != nil {
  197. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  198. return err
  199. }
  200. if !notmnt {
  201. if mntErr = b.mounter.Unmount(dir); mntErr != nil {
  202. klog.Errorf("Failed to unmount: %v", mntErr)
  203. return err
  204. }
  205. notmnt, mntErr := b.mounter.IsLikelyNotMountPoint(dir)
  206. if mntErr != nil {
  207. klog.Errorf("IsLikelyNotMountPoint check failed: %v", mntErr)
  208. return err
  209. }
  210. if !notmnt {
  211. klog.Errorf("%s is still mounted, despite call to unmount(). Will try again next sync loop.", b.GetPath())
  212. return err
  213. }
  214. }
  215. os.Remove(dir)
  216. klog.Errorf("Mount of disk %s failed: %v", dir, err)
  217. return err
  218. }
  219. return nil
  220. }
  221. var _ volume.Unmounter = &photonPersistentDiskUnmounter{}
  222. type photonPersistentDiskUnmounter struct {
  223. *photonPersistentDisk
  224. }
  225. // Unmounts the bind mount, and detaches the disk only if the PD
  226. // resource was the last reference to that disk on the kubelet.
  227. func (c *photonPersistentDiskUnmounter) TearDown() error {
  228. err := c.TearDownAt(c.GetPath())
  229. if err != nil {
  230. return err
  231. }
  232. removeFromScsiSubsystem(c.volName)
  233. return nil
  234. }
  235. // Unmounts the bind mount, and detaches the disk only if the PD
  236. // resource was the last reference to that disk on the kubelet.
  237. func (c *photonPersistentDiskUnmounter) TearDownAt(dir string) error {
  238. return mount.CleanupMountPoint(dir, c.mounter, false)
  239. }
  240. func makeGlobalPDPath(host volume.VolumeHost, devName string) string {
  241. return filepath.Join(host.GetPluginDir(photonPersistentDiskPluginName), util.MountsInGlobalPDPath, devName)
  242. }
  243. func (ppd *photonPersistentDisk) GetPath() string {
  244. name := photonPersistentDiskPluginName
  245. return ppd.plugin.host.GetPodVolumeDir(ppd.podUID, utilstrings.EscapeQualifiedName(name), ppd.volName)
  246. }
  247. // TODO: supporting more access mode for PhotonController persistent disk
  248. func (plugin *photonPersistentDiskPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
  249. return []v1.PersistentVolumeAccessMode{
  250. v1.ReadWriteOnce,
  251. }
  252. }
  253. type photonPersistentDiskDeleter struct {
  254. *photonPersistentDisk
  255. }
  256. var _ volume.Deleter = &photonPersistentDiskDeleter{}
  257. func (plugin *photonPersistentDiskPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
  258. return plugin.newDeleterInternal(spec, &PhotonDiskUtil{})
  259. }
  260. func (plugin *photonPersistentDiskPlugin) newDeleterInternal(spec *volume.Spec, manager pdManager) (volume.Deleter, error) {
  261. if spec.PersistentVolume != nil && spec.PersistentVolume.Spec.PhotonPersistentDisk == nil {
  262. return nil, fmt.Errorf("spec.PersistentVolumeSource.PhotonPersistentDisk is nil")
  263. }
  264. return &photonPersistentDiskDeleter{
  265. &photonPersistentDisk{
  266. volName: spec.Name(),
  267. pdID: spec.PersistentVolume.Spec.PhotonPersistentDisk.PdID,
  268. manager: manager,
  269. plugin: plugin,
  270. }}, nil
  271. }
  272. func (r *photonPersistentDiskDeleter) Delete() error {
  273. return r.manager.DeleteVolume(r)
  274. }
  275. type photonPersistentDiskProvisioner struct {
  276. *photonPersistentDisk
  277. options volume.VolumeOptions
  278. }
  279. var _ volume.Provisioner = &photonPersistentDiskProvisioner{}
  280. func (plugin *photonPersistentDiskPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
  281. return plugin.newProvisionerInternal(options, &PhotonDiskUtil{})
  282. }
  283. func (plugin *photonPersistentDiskPlugin) newProvisionerInternal(options volume.VolumeOptions, manager pdManager) (volume.Provisioner, error) {
  284. return &photonPersistentDiskProvisioner{
  285. photonPersistentDisk: &photonPersistentDisk{
  286. manager: manager,
  287. plugin: plugin,
  288. },
  289. options: options,
  290. }, nil
  291. }
  292. func (p *photonPersistentDiskProvisioner) Provision(selectedNode *v1.Node, allowedTopologies []v1.TopologySelectorTerm) (*v1.PersistentVolume, error) {
  293. if !util.AccessModesContainedInAll(p.plugin.GetAccessModes(), p.options.PVC.Spec.AccessModes) {
  294. return nil, fmt.Errorf("invalid AccessModes %v: only AccessModes %v are supported", p.options.PVC.Spec.AccessModes, p.plugin.GetAccessModes())
  295. }
  296. if util.CheckPersistentVolumeClaimModeBlock(p.options.PVC) {
  297. return nil, fmt.Errorf("%s does not support block volume provisioning", p.plugin.GetPluginName())
  298. }
  299. pdID, sizeGB, fstype, err := p.manager.CreateVolume(p)
  300. if err != nil {
  301. return nil, err
  302. }
  303. if fstype == "" {
  304. fstype = "ext4"
  305. }
  306. pv := &v1.PersistentVolume{
  307. ObjectMeta: metav1.ObjectMeta{
  308. Name: p.options.PVName,
  309. Labels: map[string]string{},
  310. Annotations: map[string]string{
  311. util.VolumeDynamicallyCreatedByKey: "photon-volume-dynamic-provisioner",
  312. },
  313. },
  314. Spec: v1.PersistentVolumeSpec{
  315. PersistentVolumeReclaimPolicy: p.options.PersistentVolumeReclaimPolicy,
  316. AccessModes: p.options.PVC.Spec.AccessModes,
  317. Capacity: v1.ResourceList{
  318. v1.ResourceName(v1.ResourceStorage): resource.MustParse(fmt.Sprintf("%dGi", sizeGB)),
  319. },
  320. PersistentVolumeSource: v1.PersistentVolumeSource{
  321. PhotonPersistentDisk: &v1.PhotonPersistentDiskVolumeSource{
  322. PdID: pdID,
  323. FSType: fstype,
  324. },
  325. },
  326. MountOptions: p.options.MountOptions,
  327. },
  328. }
  329. if len(p.options.PVC.Spec.AccessModes) == 0 {
  330. pv.Spec.AccessModes = p.plugin.GetAccessModes()
  331. }
  332. return pv, nil
  333. }
  334. func getVolumeSource(
  335. spec *volume.Spec) (*v1.PhotonPersistentDiskVolumeSource, bool, error) {
  336. if spec.Volume != nil && spec.Volume.PhotonPersistentDisk != nil {
  337. return spec.Volume.PhotonPersistentDisk, spec.ReadOnly, nil
  338. } else if spec.PersistentVolume != nil &&
  339. spec.PersistentVolume.Spec.PhotonPersistentDisk != nil {
  340. return spec.PersistentVolume.Spec.PhotonPersistentDisk, spec.ReadOnly, nil
  341. }
  342. return nil, false, fmt.Errorf("Spec does not reference a Photon Controller persistent disk type")
  343. }