azure_dd.go 11 KB

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