azure_dd.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
  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. "context"
  17. "fmt"
  18. "strings"
  19. "github.com/Azure/azure-sdk-for-go/services/compute/mgmt/2019-07-01/compute"
  20. "github.com/Azure/azure-sdk-for-go/services/storage/mgmt/2019-06-01/storage"
  21. "k8s.io/klog"
  22. v1 "k8s.io/api/core/v1"
  23. "k8s.io/apimachinery/pkg/api/resource"
  24. "k8s.io/apimachinery/pkg/types"
  25. "k8s.io/apimachinery/pkg/util/sets"
  26. "k8s.io/kubernetes/pkg/volume"
  27. "k8s.io/kubernetes/pkg/volume/util"
  28. "k8s.io/legacy-cloud-providers/azure"
  29. )
  30. // interface exposed by the cloud provider implementing Disk functionality
  31. type DiskController interface {
  32. CreateBlobDisk(dataDiskName string, storageAccountType storage.SkuName, sizeGB int) (string, error)
  33. DeleteBlobDisk(diskUri string) error
  34. CreateManagedDisk(options *azure.ManagedDiskOptions) (string, error)
  35. DeleteManagedDisk(diskURI string) error
  36. // Attaches the disk to the host machine.
  37. AttachDisk(isManagedDisk bool, diskName, diskUri string, nodeName types.NodeName, cachingMode compute.CachingTypes) (int32, error)
  38. // Detaches the disk, identified by disk name or uri, from the host machine.
  39. DetachDisk(diskName, diskUri string, nodeName types.NodeName) error
  40. // Check if a list of volumes are attached to the node with the specified NodeName
  41. DisksAreAttached(diskNames []string, nodeName types.NodeName) (map[string]bool, error)
  42. // Get the LUN number of the disk that is attached to the host
  43. GetDiskLun(diskName, diskUri string, nodeName types.NodeName) (int32, error)
  44. // Get the next available LUN number to attach a new VHD
  45. GetNextDiskLun(nodeName types.NodeName) (int32, error)
  46. // Create a VHD blob
  47. CreateVolume(name, storageAccount, storageAccountType, location string, requestGB int) (string, string, int, error)
  48. // Delete a VHD blob
  49. DeleteVolume(diskURI string) error
  50. // Expand the disk to new size
  51. ResizeDisk(diskURI string, oldSize resource.Quantity, newSize resource.Quantity) (resource.Quantity, error)
  52. // GetAzureDiskLabels gets availability zone labels for Azuredisk.
  53. GetAzureDiskLabels(diskURI string) (map[string]string, error)
  54. // GetActiveZones returns all the zones in which k8s nodes are currently running.
  55. GetActiveZones() (sets.String, error)
  56. // GetLocation returns the location in which k8s cluster is currently running.
  57. GetLocation() string
  58. }
  59. type azureDataDiskPlugin struct {
  60. host volume.VolumeHost
  61. }
  62. var _ volume.VolumePlugin = &azureDataDiskPlugin{}
  63. var _ volume.PersistentVolumePlugin = &azureDataDiskPlugin{}
  64. var _ volume.DeletableVolumePlugin = &azureDataDiskPlugin{}
  65. var _ volume.ProvisionableVolumePlugin = &azureDataDiskPlugin{}
  66. var _ volume.AttachableVolumePlugin = &azureDataDiskPlugin{}
  67. var _ volume.VolumePluginWithAttachLimits = &azureDataDiskPlugin{}
  68. var _ volume.ExpandableVolumePlugin = &azureDataDiskPlugin{}
  69. var _ volume.DeviceMountableVolumePlugin = &azureDataDiskPlugin{}
  70. const (
  71. azureDataDiskPluginName = "kubernetes.io/azure-disk"
  72. defaultAzureVolumeLimit = 16
  73. )
  74. func ProbeVolumePlugins() []volume.VolumePlugin {
  75. return []volume.VolumePlugin{&azureDataDiskPlugin{}}
  76. }
  77. func (plugin *azureDataDiskPlugin) Init(host volume.VolumeHost) error {
  78. plugin.host = host
  79. return nil
  80. }
  81. func (plugin *azureDataDiskPlugin) GetPluginName() string {
  82. return azureDataDiskPluginName
  83. }
  84. func (plugin *azureDataDiskPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
  85. volumeSource, _, err := getVolumeSource(spec)
  86. if err != nil {
  87. return "", err
  88. }
  89. return volumeSource.DataDiskURI, nil
  90. }
  91. func (plugin *azureDataDiskPlugin) CanSupport(spec *volume.Spec) bool {
  92. return (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.AzureDisk != nil) ||
  93. (spec.Volume != nil && spec.Volume.AzureDisk != nil)
  94. }
  95. func (plugin *azureDataDiskPlugin) RequiresRemount() bool {
  96. return false
  97. }
  98. func (plugin *azureDataDiskPlugin) SupportsMountOption() bool {
  99. return true
  100. }
  101. func (plugin *azureDataDiskPlugin) SupportsBulkVolumeVerification() bool {
  102. return false
  103. }
  104. func (plugin *azureDataDiskPlugin) GetVolumeLimits() (map[string]int64, error) {
  105. volumeLimits := map[string]int64{
  106. util.AzureVolumeLimitKey: defaultAzureVolumeLimit,
  107. }
  108. az, err := getCloud(plugin.host)
  109. if err != nil {
  110. // if we can't fetch cloudprovider we return an error
  111. // hoping external CCM or admin can set it. Returning
  112. // default values from here will mean, no one can
  113. // override them.
  114. return nil, fmt.Errorf("failed to get azure cloud in GetVolumeLimits, plugin.host: %s", plugin.host.GetHostName())
  115. }
  116. instances, ok := az.Instances()
  117. if !ok {
  118. klog.Warningf("Failed to get instances from cloud provider")
  119. return volumeLimits, nil
  120. }
  121. instanceType, err := instances.InstanceType(context.TODO(), plugin.host.GetNodeName())
  122. if err != nil {
  123. klog.Errorf("Failed to get instance type from Azure cloud provider, nodeName: %s", plugin.host.GetNodeName())
  124. return volumeLimits, nil
  125. }
  126. volumeLimits = map[string]int64{
  127. util.AzureVolumeLimitKey: getMaxDataDiskCount(instanceType),
  128. }
  129. return volumeLimits, nil
  130. }
  131. func getMaxDataDiskCount(instanceType string) int64 {
  132. vmsize := strings.ToUpper(instanceType)
  133. maxDataDiskCount, exists := maxDataDiskCountMap[vmsize]
  134. if exists {
  135. klog.V(12).Infof("got a matching size in getMaxDataDiskCount, VM Size: %s, MaxDataDiskCount: %d", vmsize, maxDataDiskCount)
  136. return maxDataDiskCount
  137. }
  138. klog.V(12).Infof("not found a matching size in getMaxDataDiskCount, VM Size: %s, use default volume limit: %d", vmsize, defaultAzureVolumeLimit)
  139. return defaultAzureVolumeLimit
  140. }
  141. func (plugin *azureDataDiskPlugin) VolumeLimitKey(spec *volume.Spec) string {
  142. return util.AzureVolumeLimitKey
  143. }
  144. func (plugin *azureDataDiskPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
  145. return []v1.PersistentVolumeAccessMode{
  146. v1.ReadWriteOnce,
  147. }
  148. }
  149. // NewAttacher initializes an Attacher
  150. func (plugin *azureDataDiskPlugin) NewAttacher() (volume.Attacher, error) {
  151. azure, err := getCloud(plugin.host)
  152. if err != nil {
  153. klog.Errorf("failed to get azure cloud in NewAttacher, plugin.host : %s, err:%v", plugin.host.GetHostName(), err)
  154. return nil, err
  155. }
  156. return &azureDiskAttacher{
  157. plugin: plugin,
  158. cloud: azure,
  159. }, nil
  160. }
  161. func (plugin *azureDataDiskPlugin) NewDetacher() (volume.Detacher, error) {
  162. azure, err := getCloud(plugin.host)
  163. if err != nil {
  164. klog.V(4).Infof("failed to get azure cloud in NewDetacher, plugin.host : %s", plugin.host.GetHostName())
  165. return nil, err
  166. }
  167. return &azureDiskDetacher{
  168. plugin: plugin,
  169. cloud: azure,
  170. }, nil
  171. }
  172. func (plugin *azureDataDiskPlugin) CanAttach(spec *volume.Spec) (bool, error) {
  173. return true, nil
  174. }
  175. func (plugin *azureDataDiskPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) {
  176. return true, nil
  177. }
  178. func (plugin *azureDataDiskPlugin) NewDeleter(spec *volume.Spec) (volume.Deleter, error) {
  179. volumeSource, _, err := getVolumeSource(spec)
  180. if err != nil {
  181. return nil, err
  182. }
  183. disk := makeDataDisk(spec.Name(), "", volumeSource.DiskName, plugin.host, plugin)
  184. return &azureDiskDeleter{
  185. spec: spec,
  186. plugin: plugin,
  187. dataDisk: disk,
  188. }, nil
  189. }
  190. func (plugin *azureDataDiskPlugin) NewProvisioner(options volume.VolumeOptions) (volume.Provisioner, error) {
  191. if len(options.PVC.Spec.AccessModes) == 0 {
  192. options.PVC.Spec.AccessModes = plugin.GetAccessModes()
  193. }
  194. return &azureDiskProvisioner{
  195. plugin: plugin,
  196. options: options,
  197. }, nil
  198. }
  199. func (plugin *azureDataDiskPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, options volume.VolumeOptions) (volume.Mounter, error) {
  200. volumeSource, _, err := getVolumeSource(spec)
  201. if err != nil {
  202. return nil, err
  203. }
  204. disk := makeDataDisk(spec.Name(), pod.UID, volumeSource.DiskName, plugin.host, plugin)
  205. return &azureDiskMounter{
  206. plugin: plugin,
  207. spec: spec,
  208. options: options,
  209. dataDisk: disk,
  210. }, nil
  211. }
  212. func (plugin *azureDataDiskPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
  213. disk := makeDataDisk(volName, podUID, "", plugin.host, plugin)
  214. return &azureDiskUnmounter{
  215. plugin: plugin,
  216. dataDisk: disk,
  217. }, nil
  218. }
  219. func (plugin *azureDataDiskPlugin) RequiresFSResize() bool {
  220. return true
  221. }
  222. func (plugin *azureDataDiskPlugin) ExpandVolumeDevice(
  223. spec *volume.Spec,
  224. newSize resource.Quantity,
  225. oldSize resource.Quantity) (resource.Quantity, error) {
  226. if spec.PersistentVolume == nil || spec.PersistentVolume.Spec.AzureDisk == nil {
  227. return oldSize, fmt.Errorf("invalid PV spec")
  228. }
  229. diskController, err := getDiskController(plugin.host)
  230. if err != nil {
  231. return oldSize, err
  232. }
  233. return diskController.ResizeDisk(spec.PersistentVolume.Spec.AzureDisk.DataDiskURI, oldSize, newSize)
  234. }
  235. func (plugin *azureDataDiskPlugin) NodeExpand(resizeOptions volume.NodeResizeOptions) (bool, error) {
  236. fsVolume, err := util.CheckVolumeModeFilesystem(resizeOptions.VolumeSpec)
  237. if err != nil {
  238. return false, fmt.Errorf("error checking VolumeMode: %v", err)
  239. }
  240. // if volume is not a fs file system, there is nothing for us to do here.
  241. if !fsVolume {
  242. return true, nil
  243. }
  244. _, err = util.GenericResizeFS(plugin.host, plugin.GetPluginName(), resizeOptions.DevicePath, resizeOptions.DeviceMountPath)
  245. if err != nil {
  246. return false, err
  247. }
  248. return true, nil
  249. }
  250. var _ volume.NodeExpandableVolumePlugin = &azureDataDiskPlugin{}
  251. func (plugin *azureDataDiskPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volume.Spec, error) {
  252. mounter := plugin.host.GetMounter(plugin.GetPluginName())
  253. kvh, ok := plugin.host.(volume.KubeletVolumeHost)
  254. if !ok {
  255. return nil, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
  256. }
  257. hu := kvh.GetHostUtil()
  258. pluginMntDir := util.GetPluginMountDir(plugin.host, plugin.GetPluginName())
  259. sourceName, err := hu.GetDeviceNameFromMount(mounter, mountPath, pluginMntDir)
  260. if err != nil {
  261. return nil, err
  262. }
  263. azureVolume := &v1.Volume{
  264. Name: volumeName,
  265. VolumeSource: v1.VolumeSource{
  266. AzureDisk: &v1.AzureDiskVolumeSource{
  267. DataDiskURI: sourceName,
  268. },
  269. },
  270. }
  271. return volume.NewSpecFromVolume(azureVolume), nil
  272. }
  273. func (plugin *azureDataDiskPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) {
  274. m := plugin.host.GetMounter(plugin.GetPluginName())
  275. return m.GetMountRefs(deviceMountPath)
  276. }
  277. func (plugin *azureDataDiskPlugin) NewDeviceMounter() (volume.DeviceMounter, error) {
  278. return plugin.NewAttacher()
  279. }
  280. func (plugin *azureDataDiskPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) {
  281. return plugin.NewDetacher()
  282. }