1/* 2Copyright 2017 The Kubernetes Authors. 3 4Licensed under the Apache License, Version 2.0 (the "License"); 5you may not use this file except in compliance with the License. 6You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10Unless required by applicable law or agreed to in writing, software 11distributed under the License is distributed on an "AS IS" BASIS, 12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13See the License for the specific language governing permissions and 14limitations under the License. 15*/ 16 17package fc 18 19import ( 20 "fmt" 21 "os" 22 "strconv" 23 "strings" 24 "time" 25 26 v1 "k8s.io/api/core/v1" 27 "k8s.io/apimachinery/pkg/types" 28 "k8s.io/apimachinery/pkg/util/wait" 29 "k8s.io/klog/v2" 30 "k8s.io/kubernetes/pkg/volume" 31 volumeutil "k8s.io/kubernetes/pkg/volume/util" 32 "k8s.io/mount-utils" 33) 34 35type fcAttacher struct { 36 host volume.VolumeHost 37 manager diskManager 38} 39 40var _ volume.Attacher = &fcAttacher{} 41 42var _ volume.DeviceMounter = &fcAttacher{} 43 44var _ volume.AttachableVolumePlugin = &fcPlugin{} 45 46var _ volume.DeviceMountableVolumePlugin = &fcPlugin{} 47 48func (plugin *fcPlugin) NewAttacher() (volume.Attacher, error) { 49 return &fcAttacher{ 50 host: plugin.host, 51 manager: &fcUtil{}, 52 }, nil 53} 54 55func (plugin *fcPlugin) NewDeviceMounter() (volume.DeviceMounter, error) { 56 return plugin.NewAttacher() 57} 58 59func (plugin *fcPlugin) GetDeviceMountRefs(deviceMountPath string) ([]string, error) { 60 mounter := plugin.host.GetMounter(plugin.GetPluginName()) 61 return mounter.GetMountRefs(deviceMountPath) 62} 63 64func (attacher *fcAttacher) Attach(spec *volume.Spec, nodeName types.NodeName) (string, error) { 65 return "", nil 66} 67 68func (attacher *fcAttacher) VolumesAreAttached(specs []*volume.Spec, nodeName types.NodeName) (map[*volume.Spec]bool, error) { 69 volumesAttachedCheck := make(map[*volume.Spec]bool) 70 for _, spec := range specs { 71 volumesAttachedCheck[spec] = true 72 } 73 74 return volumesAttachedCheck, nil 75} 76 77func (attacher *fcAttacher) WaitForAttach(spec *volume.Spec, devicePath string, _ *v1.Pod, timeout time.Duration) (string, error) { 78 mounter, err := volumeSpecToMounter(spec, attacher.host) 79 if err != nil { 80 klog.Warningf("failed to get fc mounter: %v", err) 81 return "", err 82 } 83 return attacher.manager.AttachDisk(*mounter) 84} 85 86func (attacher *fcAttacher) GetDeviceMountPath( 87 spec *volume.Spec) (string, error) { 88 mounter, err := volumeSpecToMounter(spec, attacher.host) 89 if err != nil { 90 klog.Warningf("failed to get fc mounter: %v", err) 91 return "", err 92 } 93 94 return attacher.manager.MakeGlobalPDName(*mounter.fcDisk), nil 95} 96 97func (attacher *fcAttacher) MountDevice(spec *volume.Spec, devicePath string, deviceMountPath string, _ volume.DeviceMounterArgs) error { 98 mounter := attacher.host.GetMounter(fcPluginName) 99 notMnt, err := mounter.IsLikelyNotMountPoint(deviceMountPath) 100 if err != nil { 101 if os.IsNotExist(err) { 102 if err := os.MkdirAll(deviceMountPath, 0750); err != nil { 103 return err 104 } 105 notMnt = true 106 } else { 107 return err 108 } 109 } 110 111 volumeSource, readOnly, err := getVolumeSource(spec) 112 if err != nil { 113 return err 114 } 115 116 options := []string{} 117 if readOnly { 118 options = append(options, "ro") 119 } 120 if notMnt { 121 diskMounter := &mount.SafeFormatAndMount{Interface: mounter, Exec: attacher.host.GetExec(fcPluginName)} 122 mountOptions := volumeutil.MountOptionFromSpec(spec, options...) 123 err = diskMounter.FormatAndMount(devicePath, deviceMountPath, volumeSource.FSType, mountOptions) 124 if err != nil { 125 os.Remove(deviceMountPath) 126 return err 127 } 128 } 129 return nil 130} 131 132type fcDetacher struct { 133 mounter mount.Interface 134 manager diskManager 135 host volume.VolumeHost 136} 137 138var _ volume.Detacher = &fcDetacher{} 139 140var _ volume.DeviceUnmounter = &fcDetacher{} 141 142func (plugin *fcPlugin) NewDetacher() (volume.Detacher, error) { 143 return &fcDetacher{ 144 mounter: plugin.host.GetMounter(plugin.GetPluginName()), 145 manager: &fcUtil{}, 146 host: plugin.host, 147 }, nil 148} 149 150func (plugin *fcPlugin) NewDeviceUnmounter() (volume.DeviceUnmounter, error) { 151 return plugin.NewDetacher() 152} 153 154func (detacher *fcDetacher) Detach(volumeName string, nodeName types.NodeName) error { 155 return nil 156} 157 158func (detacher *fcDetacher) UnmountDevice(deviceMountPath string) error { 159 // Specify device name for DetachDisk later 160 devName, _, err := mount.GetDeviceNameFromMount(detacher.mounter, deviceMountPath) 161 if err != nil { 162 klog.Errorf("fc: failed to get device from mnt: %s\nError: %v", deviceMountPath, err) 163 return err 164 } 165 // Unmount for deviceMountPath(=globalPDPath) 166 err = mount.CleanupMountPoint(deviceMountPath, detacher.mounter, false) 167 if err != nil { 168 return fmt.Errorf("fc: failed to unmount: %s\nError: %v", deviceMountPath, err) 169 } 170 // GetDeviceNameFromMount from above returns an empty string if deviceMountPath is not a mount point 171 // There is no need to DetachDisk if this is the case (and DetachDisk will throw an error if we attempt) 172 if devName == "" { 173 return nil 174 } 175 176 unMounter := volumeSpecToUnmounter(detacher.mounter, detacher.host) 177 // The device is unmounted now. If UnmountDevice was retried, GetDeviceNameFromMount 178 // won't find any mount and won't return DetachDisk below. 179 // Therefore implement our own retry mechanism here. 180 // E.g. DetachDisk sometimes fails to flush a multipath device with "device is busy" when it was 181 // just unmounted. 182 // 2 minutes should be enough within 6 minute force detach timeout. 183 var detachError error 184 err = wait.PollImmediate(10*time.Second, 2*time.Minute, func() (bool, error) { 185 detachError = detacher.manager.DetachDisk(*unMounter, devName) 186 if detachError != nil { 187 klog.V(4).Infof("fc: failed to detach disk %s (%s): %v", devName, deviceMountPath, detachError) 188 return false, nil 189 } 190 return true, nil 191 }) 192 if err != nil { 193 return fmt.Errorf("fc: failed to detach disk: %s: %v", devName, detachError) 194 } 195 196 klog.V(2).Infof("fc: successfully detached disk: %s", devName) 197 return nil 198} 199 200func (plugin *fcPlugin) CanAttach(spec *volume.Spec) (bool, error) { 201 return true, nil 202} 203 204func (plugin *fcPlugin) CanDeviceMount(spec *volume.Spec) (bool, error) { 205 return true, nil 206} 207 208func volumeSpecToMounter(spec *volume.Spec, host volume.VolumeHost) (*fcDiskMounter, error) { 209 fc, readOnly, err := getVolumeSource(spec) 210 if err != nil { 211 return nil, err 212 } 213 var lun string 214 var wwids []string 215 if fc.Lun != nil && len(fc.TargetWWNs) != 0 { 216 lun = strconv.Itoa(int(*fc.Lun)) 217 } else if len(fc.WWIDs) != 0 { 218 for _, wwid := range fc.WWIDs { 219 wwids = append(wwids, strings.Replace(wwid, " ", "_", -1)) 220 } 221 } else { 222 return nil, fmt.Errorf("fc: no fc disk information found. failed to make a new mounter") 223 } 224 fcDisk := &fcDisk{ 225 plugin: &fcPlugin{ 226 host: host, 227 }, 228 wwns: fc.TargetWWNs, 229 lun: lun, 230 wwids: wwids, 231 io: &osIOHandler{}, 232 } 233 234 volumeMode, err := volumeutil.GetVolumeMode(spec) 235 if err != nil { 236 return nil, err 237 } 238 239 klog.V(5).Infof("fc: volumeSpecToMounter volumeMode %s", volumeMode) 240 return &fcDiskMounter{ 241 fcDisk: fcDisk, 242 fsType: fc.FSType, 243 volumeMode: volumeMode, 244 readOnly: readOnly, 245 mounter: volumeutil.NewSafeFormatAndMountFromHost(fcPluginName, host), 246 deviceUtil: volumeutil.NewDeviceHandler(volumeutil.NewIOHandler()), 247 mountOptions: volumeutil.MountOptionFromSpec(spec), 248 }, nil 249} 250 251func volumeSpecToUnmounter(mounter mount.Interface, host volume.VolumeHost) *fcDiskUnmounter { 252 return &fcDiskUnmounter{ 253 fcDisk: &fcDisk{ 254 io: &osIOHandler{}, 255 }, 256 mounter: mounter, 257 deviceUtil: volumeutil.NewDeviceHandler(volumeutil.NewIOHandler()), 258 exec: host.GetExec(fcPluginName), 259 } 260} 261