k8s.io/kubernetes@v1.29.3/pkg/volume/rbd/attacher.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 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 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package rbd 18 19 import ( 20 "fmt" 21 "os" 22 "time" 23 24 "k8s.io/klog/v2" 25 "k8s.io/mount-utils" 26 27 v1 "k8s.io/api/core/v1" 28 "k8s.io/apimachinery/pkg/types" 29 "k8s.io/kubernetes/pkg/volume" 30 volutil "k8s.io/kubernetes/pkg/volume/util" 31 ) 32 33 // NewAttacher implements AttachableVolumePlugin.NewAttacher. 34 func (plugin *rbdPlugin) NewAttacher() (volume.Attacher, error) { 35 return plugin.newAttacherInternal(&rbdUtil{}) 36 } 37 38 // NewDeviceMounter implements DeviceMountableVolumePlugin.NewDeviceMounter 39 func (plugin *rbdPlugin) NewDeviceMounter() (volume.DeviceMounter, error) { 40 return plugin.NewAttacher() 41 } 42 43 func (plugin *rbdPlugin) newAttacherInternal(manager diskManager) (volume.Attacher, error) { 44 return &rbdAttacher{ 45 plugin: plugin, 46 manager: manager, 47 mounter: volutil.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host), 48 }, nil 49 } 50 51 // NewDetacher implements AttachableVolumePlugin.NewDetacher. 52 func (plugin *rbdPlugin) NewDetacher() (volume.Detacher, error) { 53 return plugin.newDetacherInternal(&rbdUtil{}) 54 } 55 56 // NewDeviceUnmounter implements DeviceMountableVolumePlugin.NewDeviceUnmounter 57 func (plugin *rbdPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) { 58 return plugin.NewDetacher() 59 } 60 61 func (plugin *rbdPlugin) newDetacherInternal(manager diskManager) (volume.Detacher, error) { 62 return &rbdDetacher{ 63 plugin: plugin, 64 manager: manager, 65 mounter: volutil.NewSafeFormatAndMountFromHost(plugin.GetPluginName(), plugin.host), 66 }, nil 67 } 68 69 // GetDeviceMountRefs implements AttachableVolumePlugin.GetDeviceMountRefs. 70 func (plugin *rbdPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) { 71 mounter := plugin.host.GetMounter(plugin.GetPluginName()) 72 return mounter.GetMountRefs(deviceMountPath) 73 } 74 75 func (plugin *rbdPlugin) CanAttach(spec *volume.Spec) (bool, error) { 76 return true, nil 77 } 78 79 func (plugin *rbdPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) { 80 return true, nil 81 } 82 83 // rbdAttacher implements volume.Attacher interface. 84 type rbdAttacher struct { 85 plugin *rbdPlugin 86 mounter *mount.SafeFormatAndMount 87 manager diskManager 88 } 89 90 var _ volume.Attacher = &rbdAttacher{} 91 92 var _ volume.DeviceMounter = &rbdAttacher{} 93 94 // Attach implements Attacher.Attach. 95 // We do not lock image here, because it requires kube-controller-manager to 96 // access external `rbd` utility. And there is no need since AttachDetach 97 // controller will not try to attach RWO volumes which are already attached to 98 // other nodes. 99 func (attacher *rbdAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) { 100 return "", nil 101 } 102 103 // VolumesAreAttached implements Attacher.VolumesAreAttached. 104 // There is no way to confirm whether the volume is attached or not from 105 // outside of the kubelet node. This method needs to return true always, like 106 // iSCSI, FC plugin. 107 func (attacher *rbdAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) { 108 volumesAttachedCheck := make(map[*volume.Spec]bool) 109 for _, spec := range specs { 110 volumesAttachedCheck[spec] = true 111 } 112 return volumesAttachedCheck, nil 113 } 114 115 // WaitForAttach implements Attacher.WaitForAttach. It's called by kubelet to 116 // attach volume onto the node. 117 // This method is idempotent, callers are responsible for retrying on failure. 118 func (attacher *rbdAttacher) WaitForAttach(spec *volume.Spec, devicePath string, pod *v1.Pod, timeout time.Duration) (string, error) { 119 klog.V(4).Infof("rbd: waiting for attach volume (name: %s) for pod (name: %s, uid: %s)", spec.Name(), pod.Name, pod.UID) 120 mounter, err := attacher.plugin.createMounterFromVolumeSpecAndPod(spec, pod) 121 if err != nil { 122 klog.Warningf("failed to create mounter: %v", spec) 123 return "", err 124 } 125 realDevicePath, err := attacher.manager.AttachDisk(*mounter) 126 if err != nil { 127 return "", err 128 } 129 klog.V(3).Infof("rbd: successfully wait for attach volume (spec: %s, pool: %s, image: %s) at %s", spec.Name(), mounter.Pool, mounter.Image, realDevicePath) 130 return realDevicePath, nil 131 } 132 133 // GetDeviceMountPath implements Attacher.GetDeviceMountPath. 134 func (attacher *rbdAttacher) GetDeviceMountPath(spec *volume.Spec) (string, error) { 135 img, err := getVolumeSourceImage(spec) 136 if err != nil { 137 return "", err 138 } 139 pool, err := getVolumeSourcePool(spec) 140 if err != nil { 141 return "", err 142 } 143 return makePDNameInternal(attacher.plugin.host, pool, img), nil 144 } 145 146 // MountDevice implements Attacher.MountDevice. It is called by the kubelet to 147 // mount device at the given mount path. 148 // This method is idempotent, callers are responsible for retrying on failure. 149 func (attacher *rbdAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string, mountArgs volume.DeviceMounterArgs) error { 150 klog.V(4).Infof("rbd: mouting device %s to %s", devicePath, deviceMountPath) 151 notMnt, err := attacher.mounter.IsLikelyNotMountPoint(deviceMountPath) 152 if err != nil { 153 if os.IsNotExist(err) { 154 if err := os.MkdirAll(deviceMountPath, 0750); err != nil { 155 return err 156 } 157 notMnt = true 158 } else { 159 return err 160 } 161 } 162 if !notMnt { 163 return nil 164 } 165 fstype, err := getVolumeSourceFSType(spec) 166 if err != nil { 167 return err 168 } 169 ro, err := getVolumeSourceReadOnly(spec) 170 if err != nil { 171 return err 172 } 173 options := []string{} 174 if ro { 175 options = append(options, "ro") 176 } 177 if mountArgs.SELinuxLabel != "" { 178 options = volutil.AddSELinuxMountOption(options, mountArgs.SELinuxLabel) 179 } 180 mountOptions := volutil.MountOptionFromSpec(spec, options...) 181 182 err = attacher.mounter.FormatAndMount(devicePath, deviceMountPath, fstype, mountOptions) 183 if err != nil { 184 os.Remove(deviceMountPath) 185 return fmt.Errorf("rbd: failed to mount device %s at %s (fstype: %s), error %v", devicePath, deviceMountPath, fstype, err) 186 } 187 klog.V(3).Infof("rbd: successfully mount device %s at %s (fstype: %s)", devicePath, deviceMountPath, fstype) 188 return nil 189 } 190 191 // rbdDetacher implements volume.Detacher interface. 192 type rbdDetacher struct { 193 plugin *rbdPlugin 194 manager diskManager 195 mounter *mount.SafeFormatAndMount 196 } 197 198 var _ volume.Detacher = &rbdDetacher{} 199 200 var _ volume.DeviceUnmounter = &rbdDetacher{} 201 202 // UnmountDevice implements Detacher.UnmountDevice. It unmounts the global 203 // mount of the RBD image. This is called once all bind mounts have been 204 // unmounted. 205 // Internally, it does four things: 206 // - Unmount device from deviceMountPath. 207 // - Detach device from the node. 208 // - Remove lock if found. (No need to check volume readonly or not, because 209 // device is not on the node anymore, it's safe to remove lock.) 210 // - Remove the deviceMountPath at last. 211 // 212 // This method is idempotent, callers are responsible for retrying on failure. 213 func (detacher *rbdDetacher) UnmountDevice(deviceMountPath string) error { 214 if pathExists, pathErr := mount.PathExists(deviceMountPath); pathErr != nil { 215 return fmt.Errorf("error checking if path exists: %v", pathErr) 216 } else if !pathExists { 217 klog.Warningf("Warning: Unmount skipped because path does not exist: %v", deviceMountPath) 218 return nil 219 } 220 devicePath, _, err := mount.GetDeviceNameFromMount(detacher.mounter, deviceMountPath) 221 if err != nil { 222 return err 223 } 224 // Unmount the device from the device mount point. 225 notMnt, err := detacher.mounter.IsLikelyNotMountPoint(deviceMountPath) 226 if err != nil { 227 return err 228 } 229 if !notMnt { 230 klog.V(4).Infof("rbd: unmouting device mountpoint %s", deviceMountPath) 231 if err = detacher.mounter.Unmount(deviceMountPath); err != nil { 232 return err 233 } 234 klog.V(3).Infof("rbd: successfully unmount device mountpath %s", deviceMountPath) 235 } 236 237 // Get devicePath from deviceMountPath if devicePath is empty 238 if devicePath == "" { 239 rbdImageInfo, err := getRbdImageInfo(deviceMountPath) 240 if err != nil { 241 return err 242 } 243 found := false 244 devicePath, found = getRbdDevFromImageAndPool(rbdImageInfo.pool, rbdImageInfo.name) 245 if !found { 246 klog.Warningf("rbd: can't found devicePath for %v. Device is already unmounted, Image %v, Pool %v", deviceMountPath, rbdImageInfo.pool, rbdImageInfo.name) 247 } 248 } 249 250 if devicePath != "" { 251 klog.V(4).Infof("rbd: detaching device %s", devicePath) 252 err = detacher.manager.DetachDisk(detacher.plugin, deviceMountPath, devicePath) 253 if err != nil { 254 return err 255 } 256 klog.V(3).Infof("rbd: successfully detach device %s", devicePath) 257 } 258 err = os.Remove(deviceMountPath) 259 if err != nil { 260 return err 261 } 262 klog.V(3).Infof("rbd: successfully remove device mount point %s", deviceMountPath) 263 return nil 264 } 265 266 // Detach implements Detacher.Detach. 267 func (detacher *rbdDetacher) Detach(volumeName string, nodeName types.NodeName) error { 268 return nil 269 }