attacher.go 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  1. // +build !providerless
  2. /*
  3. Copyright 2016 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 azure_dd
  15. import (
  16. "fmt"
  17. "os"
  18. "path/filepath"
  19. "runtime"
  20. "strconv"
  21. "strings"
  22. "time"
  23. "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
  24. "k8s.io/klog"
  25. "k8s.io/utils/mount"
  26. v1 "k8s.io/api/core/v1"
  27. "k8s.io/apimachinery/pkg/types"
  28. "k8s.io/apimachinery/pkg/util/wait"
  29. cloudprovider "k8s.io/cloud-provider"
  30. "k8s.io/kubernetes/pkg/volume"
  31. "k8s.io/kubernetes/pkg/volume/util"
  32. "k8s.io/legacy-cloud-providers/azure"
  33. )
  34. type azureDiskDetacher struct {
  35. plugin *azureDataDiskPlugin
  36. cloud *azure.Cloud
  37. }
  38. type azureDiskAttacher struct {
  39. plugin *azureDataDiskPlugin
  40. cloud *azure.Cloud
  41. }
  42. var _ volume.Attacher = &azureDiskAttacher{}
  43. var _ volume.Detacher = &azureDiskDetacher{}
  44. var _ volume.DeviceMounter = &azureDiskAttacher{}
  45. var _ volume.DeviceUnmounter = &azureDiskDetacher{}
  46. // Attach attaches a volume.Spec to an Azure VM referenced by NodeName, returning the disk's LUN
  47. func (a *azureDiskAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) {
  48. volumeSource, _, err := getVolumeSource(spec)
  49. if err != nil {
  50. klog.Warningf("failed to get azure disk spec (%v)", err)
  51. return "", err
  52. }
  53. diskController, err := getDiskController(a.plugin.host)
  54. if err != nil {
  55. return "", err
  56. }
  57. lun, err := diskController.GetDiskLun(volumeSource.DiskName, volumeSource.DataDiskURI, nodeName)
  58. if err == cloudprovider.InstanceNotFound {
  59. // Log error and continue with attach
  60. klog.Warningf(
  61. "Error checking if volume is already attached to current node (%q). Will continue and try attach anyway. err=%v",
  62. nodeName, err)
  63. }
  64. if err == nil {
  65. // Volume is already attached to node.
  66. klog.V(2).Infof("Attach operation is successful. volume %q is already attached to node %q at lun %d.", volumeSource.DiskName, nodeName, lun)
  67. } else {
  68. klog.V(2).Infof("GetDiskLun returned: %v. Initiating attaching volume %q to node %q.", err, volumeSource.DataDiskURI, nodeName)
  69. isManagedDisk := (*volumeSource.Kind == v1.AzureManagedDisk)
  70. lun, err = diskController.AttachDisk(isManagedDisk, volumeSource.DiskName, volumeSource.DataDiskURI, nodeName, compute.CachingTypes(*volumeSource.CachingMode))
  71. if err == nil {
  72. klog.V(2).Infof("Attach operation successful: volume %q attached to node %q.", volumeSource.DataDiskURI, nodeName)
  73. } else {
  74. klog.V(2).Infof("Attach volume %q to instance %q failed with %v", volumeSource.DataDiskURI, nodeName, err)
  75. return "", err
  76. }
  77. }
  78. return strconv.Itoa(int(lun)), err
  79. }
  80. func (a *azureDiskAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) {
  81. volumesAttachedCheck := make(map[*volume.Spec]bool)
  82. volumeSpecMap := make(map[string]*volume.Spec)
  83. volumeIDList := []string{}
  84. for _, spec := range specs {
  85. volumeSource, _, err := getVolumeSource(spec)
  86. if err != nil {
  87. klog.Errorf("azureDisk - Error getting volume (%q) source : %v", spec.Name(), err)
  88. continue
  89. }
  90. volumeIDList = append(volumeIDList, volumeSource.DiskName)
  91. volumesAttachedCheck[spec] = true
  92. volumeSpecMap[volumeSource.DiskName] = spec
  93. }
  94. diskController, err := getDiskController(a.plugin.host)
  95. if err != nil {
  96. return nil, err
  97. }
  98. attachedResult, err := diskController.DisksAreAttached(volumeIDList, nodeName)
  99. if err != nil {
  100. // Log error and continue with attach
  101. klog.Errorf(
  102. "azureDisk - Error checking if volumes (%v) are attached to current node (%q). err=%v",
  103. volumeIDList, nodeName, err)
  104. return volumesAttachedCheck, err
  105. }
  106. for volumeID, attached := range attachedResult {
  107. if !attached {
  108. spec := volumeSpecMap[volumeID]
  109. volumesAttachedCheck[spec] = false
  110. klog.V(2).Infof("azureDisk - VolumesAreAttached: check volume %q (specName: %q) is no longer attached", volumeID, spec.Name())
  111. }
  112. }
  113. return volumesAttachedCheck, nil
  114. }
  115. func (a *azureDiskAttacher) WaitForAttach(spec *volume.Spec, devicePath string, _ *v1.Pod, timeout time.Duration) (string, error) {
  116. // devicePath could be a LUN number or
  117. // "/dev/disk/azure/scsi1/lunx", "/dev/sdx" on Linux node
  118. // "/dev/diskx" on Windows node
  119. if strings.HasPrefix(devicePath, "/dev/") {
  120. return devicePath, nil
  121. }
  122. volumeSource, _, err := getVolumeSource(spec)
  123. if err != nil {
  124. return "", err
  125. }
  126. nodeName := types.NodeName(a.plugin.host.GetHostName())
  127. diskName := volumeSource.DiskName
  128. lun, err := strconv.Atoi(devicePath)
  129. if err != nil {
  130. return "", fmt.Errorf("parse %s failed with error: %v, diskName: %s, nodeName: %s", devicePath, err, diskName, nodeName)
  131. }
  132. exec := a.plugin.host.GetExec(a.plugin.GetPluginName())
  133. io := &osIOHandler{}
  134. scsiHostRescan(io, exec)
  135. newDevicePath := ""
  136. err = wait.Poll(1*time.Second, timeout, func() (bool, error) {
  137. if newDevicePath, err = findDiskByLun(int(lun), io, exec); err != nil {
  138. return false, fmt.Errorf("azureDisk - WaitForAttach ticker failed node (%s) disk (%s) lun(%v) err(%s)", nodeName, diskName, lun, err)
  139. }
  140. // did we find it?
  141. if newDevicePath != "" {
  142. return true, nil
  143. }
  144. // wait until timeout
  145. return false, nil
  146. })
  147. if err == nil && newDevicePath == "" {
  148. err = fmt.Errorf("azureDisk - WaitForAttach failed within timeout node (%s) diskId:(%s) lun:(%v)", nodeName, diskName, lun)
  149. }
  150. return newDevicePath, err
  151. }
  152. // to avoid name conflicts (similar *.vhd name)
  153. // we use hash diskUri and we use it as device mount target.
  154. // this is generalized for both managed and blob disks
  155. // we also prefix the hash with m/b based on disk kind
  156. func (a *azureDiskAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) {
  157. volumeSource, _, err := getVolumeSource(spec)
  158. if err != nil {
  159. return "", err
  160. }
  161. if volumeSource.Kind == nil { // this spec was constructed from info on the node
  162. pdPath := filepath.Join(a.plugin.host.GetPluginDir(azureDataDiskPluginName), util.MountsInGlobalPDPath, volumeSource.DataDiskURI)
  163. return pdPath, nil
  164. }
  165. isManagedDisk := (*volumeSource.Kind == v1.AzureManagedDisk)
  166. return makeGlobalPDPath(a.plugin.host, volumeSource.DataDiskURI, isManagedDisk)
  167. }
  168. func (attacher *azureDiskAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string) error {
  169. mounter := attacher.plugin.host.GetMounter(azureDataDiskPluginName)
  170. notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath)
  171. if err != nil {
  172. if os.IsNotExist(err) {
  173. dir := deviceMountPath
  174. if runtime.GOOS == "windows" {
  175. // in windows, as we use mklink, only need to MkdirAll for parent directory
  176. dir = filepath.Dir(deviceMountPath)
  177. }
  178. if err := os.MkdirAll(dir, 0750); err != nil {
  179. return fmt.Errorf("azureDisk - mountDevice:CreateDirectory failed with %s", err)
  180. }
  181. notMnt = true
  182. } else {
  183. return fmt.Errorf("azureDisk - mountDevice:IsLikelyNotMountPoint failed with %s", err)
  184. }
  185. }
  186. if !notMnt {
  187. // testing original mount point, make sure the mount link is valid
  188. if _, err := (&osIOHandler{}).ReadDir(deviceMountPath); err != nil {
  189. // mount link is invalid, now unmount and remount later
  190. klog.Warningf("azureDisk - ReadDir %s failed with %v, unmount this directory", deviceMountPath, err)
  191. if err := mounter.Unmount(deviceMountPath); err != nil {
  192. klog.Errorf("azureDisk - Unmount deviceMountPath %s failed with %v", deviceMountPath, err)
  193. return err
  194. }
  195. notMnt = true
  196. }
  197. }
  198. volumeSource, _, err := getVolumeSource(spec)
  199. if err != nil {
  200. return err
  201. }
  202. options := []string{}
  203. if notMnt {
  204. diskMounter := util.NewSafeFormatAndMountFromHost(azureDataDiskPluginName, attacher.plugin.host)
  205. mountOptions := util.MountOptionFromSpec(spec, options...)
  206. if runtime.GOOS == "windows" {
  207. // only parse devicePath on Windows node
  208. diskNum, err := getDiskNum(devicePath)
  209. if err != nil {
  210. return err
  211. }
  212. devicePath = diskNum
  213. }
  214. err = diskMounter.FormatAndMount(devicePath, deviceMountPath, *volumeSource.FSType, mountOptions)
  215. if err != nil {
  216. if cleanErr := os.Remove(deviceMountPath); cleanErr != nil {
  217. return fmt.Errorf("azureDisk - mountDevice:FormatAndMount failed with %s and clean up failed with :%v", err, cleanErr)
  218. }
  219. return fmt.Errorf("azureDisk - mountDevice:FormatAndMount failed with %s", err)
  220. }
  221. }
  222. return nil
  223. }
  224. // Detach detaches disk from Azure VM.
  225. func (d *azureDiskDetacher) Detach(diskURI string, nodeName types.NodeName) error {
  226. if diskURI == "" {
  227. return fmt.Errorf("invalid disk to detach: %q", diskURI)
  228. }
  229. diskController, err := getDiskController(d.plugin.host)
  230. if err != nil {
  231. return err
  232. }
  233. err = diskController.DetachDisk("", diskURI, nodeName)
  234. if err != nil {
  235. klog.Errorf("failed to detach azure disk %q, err %v", diskURI, err)
  236. }
  237. klog.V(2).Infof("azureDisk - disk:%s was detached from node:%v", diskURI, nodeName)
  238. return err
  239. }
  240. // UnmountDevice unmounts the volume on the node
  241. func (detacher *azureDiskDetacher) UnmountDevice(deviceMountPath string) error {
  242. err := mount.CleanupMountPoint(deviceMountPath, detacher.plugin.host.GetMounter(detacher.plugin.GetPluginName()), false)
  243. if err == nil {
  244. klog.V(2).Infof("azureDisk - Device %s was unmounted", deviceMountPath)
  245. } else {
  246. klog.Warningf("azureDisk - Device %s failed to unmount with error: %s", deviceMountPath, err.Error())
  247. }
  248. return err
  249. }