1
16
17 package fc
18
19 import (
20 "fmt"
21 "os"
22 "path/filepath"
23 "strconv"
24 "strings"
25
26 utilfeature "k8s.io/apiserver/pkg/util/feature"
27 "k8s.io/klog/v2"
28 "k8s.io/kubernetes/pkg/features"
29 "k8s.io/mount-utils"
30 utilexec "k8s.io/utils/exec"
31 "k8s.io/utils/io"
32 utilstrings "k8s.io/utils/strings"
33
34 v1 "k8s.io/api/core/v1"
35 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
36 "k8s.io/apimachinery/pkg/types"
37 "k8s.io/kubernetes/pkg/volume"
38 "k8s.io/kubernetes/pkg/volume/util"
39 "k8s.io/kubernetes/pkg/volume/util/volumepathhandler"
40 )
41
42
43 func ProbeVolumePlugins() []volume.VolumePlugin {
44 return []volume.VolumePlugin{&fcPlugin{nil}}
45 }
46
47 type fcPlugin struct {
48 host volume.VolumeHost
49 }
50
51 var _ volume.VolumePlugin = &fcPlugin{}
52 var _ volume.PersistentVolumePlugin = &fcPlugin{}
53 var _ volume.BlockVolumePlugin = &fcPlugin{}
54
55 const (
56 fcPluginName = "kubernetes.io/fc"
57 )
58
59 func (plugin *fcPlugin) Init(host volume.VolumeHost) error {
60 plugin.host = host
61 return nil
62 }
63
64 func (plugin *fcPlugin) GetPluginName() string {
65 return fcPluginName
66 }
67
68 func (plugin *fcPlugin) GetVolumeName(spec *volume.Spec) (string, error) {
69 volumeSource, _, err := getVolumeSource(spec)
70 if err != nil {
71 return "", err
72 }
73
74
75
76
77 if len(volumeSource.TargetWWNs) != 0 && volumeSource.Lun != nil {
78
79 return fmt.Sprintf("%v:%v", volumeSource.TargetWWNs, *volumeSource.Lun), nil
80 } else if len(volumeSource.WWIDs) != 0 {
81
82 return fmt.Sprintf("%v", volumeSource.WWIDs), nil
83 }
84
85 return "", err
86 }
87
88 func (plugin *fcPlugin) CanSupport(spec *volume.Spec) bool {
89 return (spec.Volume != nil && spec.Volume.FC != nil) || (spec.PersistentVolume != nil && spec.PersistentVolume.Spec.FC != nil)
90 }
91
92 func (plugin *fcPlugin) RequiresRemount(spec *volume.Spec) bool {
93 return false
94 }
95
96 func (plugin *fcPlugin) SupportsMountOption() bool {
97 return true
98 }
99
100 func (plugin *fcPlugin) SupportsBulkVolumeVerification() bool {
101 return false
102 }
103
104 func (plugin *fcPlugin) SupportsSELinuxContextMount(spec *volume.Spec) (bool, error) {
105 return true, nil
106 }
107
108 func (plugin *fcPlugin) GetAccessModes() []v1.PersistentVolumeAccessMode {
109 return []v1.PersistentVolumeAccessMode{
110 v1.ReadWriteOnce,
111 v1.ReadOnlyMany,
112 }
113 }
114
115 func (plugin *fcPlugin) NewMounter(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.Mounter, error) {
116
117 return plugin.newMounterInternal(spec, pod.UID, &fcUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
118 }
119
120 func (plugin *fcPlugin) newMounterInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface) (volume.Mounter, error) {
121
122
123 fc, readOnly, err := getVolumeSource(spec)
124 if err != nil {
125 return nil, err
126 }
127
128 wwns, lun, wwids, err := getWwnsLunWwids(fc)
129 if err != nil {
130 return nil, fmt.Errorf("fc: no fc disk information found. failed to make a new mounter")
131 }
132 fcDisk := &fcDisk{
133 podUID: podUID,
134 volName: spec.Name(),
135 wwns: wwns,
136 lun: lun,
137 wwids: wwids,
138 manager: manager,
139 io: &osIOHandler{},
140 plugin: plugin,
141 }
142
143 volumeMode, err := util.GetVolumeMode(spec)
144 if err != nil {
145 return nil, err
146 }
147
148 klog.V(5).Infof("fc: newMounterInternal volumeMode %s", volumeMode)
149 return &fcDiskMounter{
150 fcDisk: fcDisk,
151 fsType: fc.FSType,
152 volumeMode: volumeMode,
153 readOnly: readOnly,
154 mounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec},
155 deviceUtil: util.NewDeviceHandler(util.NewIOHandler()),
156 mountOptions: util.MountOptionFromSpec(spec),
157 }, nil
158 }
159
160 func (plugin *fcPlugin) NewBlockVolumeMapper(spec *volume.Spec, pod *v1.Pod, _ volume.VolumeOptions) (volume.BlockVolumeMapper, error) {
161
162
163 var uid types.UID
164 if pod != nil {
165 uid = pod.UID
166 }
167 return plugin.newBlockVolumeMapperInternal(spec, uid, &fcUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
168 }
169
170 func (plugin *fcPlugin) newBlockVolumeMapperInternal(spec *volume.Spec, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface) (volume.BlockVolumeMapper, error) {
171 fc, readOnly, err := getVolumeSource(spec)
172 if err != nil {
173 return nil, err
174 }
175
176 wwns, lun, wwids, err := getWwnsLunWwids(fc)
177 if err != nil {
178 return nil, fmt.Errorf("fc: no fc disk information found. failed to make a new mapper")
179 }
180
181 mapper := &fcDiskMapper{
182 fcDisk: &fcDisk{
183 podUID: podUID,
184 volName: spec.Name(),
185 wwns: wwns,
186 lun: lun,
187 wwids: wwids,
188 manager: manager,
189 io: &osIOHandler{},
190 plugin: plugin},
191 readOnly: readOnly,
192 mounter: &mount.SafeFormatAndMount{Interface: mounter, Exec: exec},
193 deviceUtil: util.NewDeviceHandler(util.NewIOHandler()),
194 }
195
196 blockPath, err := mapper.GetGlobalMapPath(spec)
197 if err != nil {
198 return nil, fmt.Errorf("failed to get device path: %v", err)
199 }
200 mapper.MetricsProvider = volume.NewMetricsBlock(filepath.Join(blockPath, string(podUID)))
201
202 return mapper, nil
203 }
204
205 func (plugin *fcPlugin) NewUnmounter(volName string, podUID types.UID) (volume.Unmounter, error) {
206
207 return plugin.newUnmounterInternal(volName, podUID, &fcUtil{}, plugin.host.GetMounter(plugin.GetPluginName()), plugin.host.GetExec(plugin.GetPluginName()))
208 }
209
210 func (plugin *fcPlugin) newUnmounterInternal(volName string, podUID types.UID, manager diskManager, mounter mount.Interface, exec utilexec.Interface) (volume.Unmounter, error) {
211 return &fcDiskUnmounter{
212 fcDisk: &fcDisk{
213 podUID: podUID,
214 volName: volName,
215 manager: manager,
216 plugin: plugin,
217 io: &osIOHandler{},
218 },
219 mounter: mounter,
220 deviceUtil: util.NewDeviceHandler(util.NewIOHandler()),
221 exec: exec,
222 }, nil
223 }
224
225 func (plugin *fcPlugin) NewBlockVolumeUnmapper(volName string, podUID types.UID) (volume.BlockVolumeUnmapper, error) {
226 return plugin.newUnmapperInternal(volName, podUID, &fcUtil{}, plugin.host.GetExec(plugin.GetPluginName()))
227 }
228
229 func (plugin *fcPlugin) newUnmapperInternal(volName string, podUID types.UID, manager diskManager, exec utilexec.Interface) (volume.BlockVolumeUnmapper, error) {
230 return &fcDiskUnmapper{
231 fcDisk: &fcDisk{
232 podUID: podUID,
233 volName: volName,
234 manager: manager,
235 plugin: plugin,
236 io: &osIOHandler{},
237 },
238 exec: exec,
239 deviceUtil: util.NewDeviceHandler(util.NewIOHandler()),
240 }, nil
241 }
242
243 func (plugin *fcPlugin) ConstructVolumeSpec(volumeName, mountPath string) (volume.ReconstructedVolume, error) {
244
245
246
247
248 var globalPDPath string
249
250 mounter := plugin.host.GetMounter(plugin.GetPluginName())
251
252
253
254
255 paths, err := util.GetReliableMountRefs(mounter, mountPath)
256 if io.IsInconsistentReadError(err) {
257 klog.Errorf("Failed to read mount refs from /proc/mounts for %s: %s", mountPath, err)
258 klog.Errorf("Kubelet cannot unmount volume at %s, please unmount it manually", mountPath)
259 return volume.ReconstructedVolume{}, err
260 }
261 if err != nil {
262 return volume.ReconstructedVolume{}, err
263 }
264 for _, path := range paths {
265 if strings.Contains(path, plugin.host.GetPluginDir(fcPluginName)) {
266 globalPDPath = path
267 break
268 }
269 }
270
271 if len(globalPDPath) == 0 {
272 return volume.ReconstructedVolume{}, fmt.Errorf("couldn't fetch globalPDPath. failed to obtain volume spec")
273 }
274
275 wwns, lun, wwids, err := parsePDName(globalPDPath)
276 if err != nil {
277 return volume.ReconstructedVolume{}, fmt.Errorf("failed to retrieve volume plugin information from globalPDPath: %s", err)
278 }
279
280 fcVolume := &v1.Volume{
281 Name: volumeName,
282 VolumeSource: v1.VolumeSource{
283 FC: &v1.FCVolumeSource{WWIDs: wwids, Lun: &lun, TargetWWNs: wwns},
284 },
285 }
286
287 var mountContext string
288 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) {
289 kvh, ok := plugin.host.(volume.KubeletVolumeHost)
290 if !ok {
291 return volume.ReconstructedVolume{}, fmt.Errorf("plugin volume host does not implement KubeletVolumeHost interface")
292 }
293 hu := kvh.GetHostUtil()
294 mountContext, err = hu.GetSELinuxMountContext(mountPath)
295 if err != nil {
296 return volume.ReconstructedVolume{}, err
297 }
298 }
299
300 klog.V(5).Infof("ConstructVolumeSpec: TargetWWNs: %v, Lun: %v, WWIDs: %v",
301 fcVolume.VolumeSource.FC.TargetWWNs, *fcVolume.VolumeSource.FC.Lun, fcVolume.VolumeSource.FC.WWIDs)
302 return volume.ReconstructedVolume{
303 Spec: volume.NewSpecFromVolume(fcVolume),
304 SELinuxMountContext: mountContext,
305 }, nil
306 }
307
308
309
310
311
312
313
314
315
316
317 func (plugin *fcPlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, mapPath string) (*volume.Spec, error) {
318 pluginDir := plugin.host.GetVolumeDevicePluginDir(fcPluginName)
319 blkutil := volumepathhandler.NewBlockVolumePathHandler()
320 globalMapPathUUID, err := blkutil.FindGlobalMapPathUUIDFromPod(pluginDir, mapPath, podUID)
321 if err != nil {
322 return nil, err
323 }
324 klog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err)
325
326
327
328
329
330 globalPDPath := filepath.Dir(globalMapPathUUID)
331
332 wwns, lun, wwids, err := parsePDName(globalPDPath)
333 if err != nil {
334 return nil, fmt.Errorf("failed to retrieve volume plugin information from globalPDPath: %s", err)
335 }
336 fcPV := createPersistentVolumeFromFCVolumeSource(volumeName,
337 v1.FCVolumeSource{TargetWWNs: wwns, Lun: &lun, WWIDs: wwids})
338 klog.V(5).Infof("ConstructBlockVolumeSpec: TargetWWNs: %v, Lun: %v, WWIDs: %v",
339 fcPV.Spec.PersistentVolumeSource.FC.TargetWWNs,
340 *fcPV.Spec.PersistentVolumeSource.FC.Lun,
341 fcPV.Spec.PersistentVolumeSource.FC.WWIDs)
342
343 return volume.NewSpecFromPersistentVolume(fcPV, false), nil
344 }
345
346 type fcDisk struct {
347 volName string
348 podUID types.UID
349 wwns []string
350 lun string
351 wwids []string
352 plugin *fcPlugin
353
354 manager diskManager
355
356 io ioHandler
357 volume.MetricsNil
358 }
359
360 func (fc *fcDisk) GetPath() string {
361
362 return fc.plugin.host.GetPodVolumeDir(fc.podUID, utilstrings.EscapeQualifiedName(fcPluginName), fc.volName)
363 }
364
365 func (fc *fcDisk) fcGlobalMapPath(spec *volume.Spec) (string, error) {
366 mounter, err := volumeSpecToMounter(spec, fc.plugin.host)
367 if err != nil {
368 klog.Warningf("failed to get fc mounter: %v", err)
369 return "", err
370 }
371 return fc.manager.MakeGlobalVDPDName(*mounter.fcDisk), nil
372 }
373
374 func (fc *fcDisk) fcPodDeviceMapPath() (string, string) {
375 return fc.plugin.host.GetPodVolumeDeviceDir(fc.podUID, utilstrings.EscapeQualifiedName(fcPluginName)), fc.volName
376 }
377
378 type fcDiskMounter struct {
379 *fcDisk
380 readOnly bool
381 fsType string
382 volumeMode v1.PersistentVolumeMode
383 mounter *mount.SafeFormatAndMount
384 deviceUtil util.DeviceUtil
385 mountOptions []string
386 mountedWithSELinuxContext bool
387 }
388
389 var _ volume.Mounter = &fcDiskMounter{}
390
391 func (b *fcDiskMounter) GetAttributes() volume.Attributes {
392 return volume.Attributes{
393 ReadOnly: b.readOnly,
394 Managed: !b.readOnly,
395 SELinuxRelabel: !b.mountedWithSELinuxContext,
396 }
397 }
398
399 func (b *fcDiskMounter) SetUp(mounterArgs volume.MounterArgs) error {
400 return b.SetUpAt(b.GetPath(), mounterArgs)
401 }
402
403 func (b *fcDiskMounter) SetUpAt(dir string, mounterArgs volume.MounterArgs) error {
404
405 err := diskSetUp(b.manager, *b, dir, b.mounter, mounterArgs.FsGroup, mounterArgs.FSGroupChangePolicy)
406 if err != nil {
407 klog.Errorf("fc: failed to setup")
408 }
409
410 if utilfeature.DefaultFeatureGate.Enabled(features.SELinuxMountReadWriteOncePod) {
411
412 b.mountedWithSELinuxContext = mounterArgs.SELinuxLabel != ""
413 }
414 return err
415 }
416
417 type fcDiskUnmounter struct {
418 *fcDisk
419 mounter mount.Interface
420 deviceUtil util.DeviceUtil
421 exec utilexec.Interface
422 }
423
424 var _ volume.Unmounter = &fcDiskUnmounter{}
425
426
427
428 func (c *fcDiskUnmounter) TearDown() error {
429 return c.TearDownAt(c.GetPath())
430 }
431
432 func (c *fcDiskUnmounter) TearDownAt(dir string) error {
433 return mount.CleanupMountPoint(dir, c.mounter, false)
434 }
435
436
437 type fcDiskMapper struct {
438 *fcDisk
439 volume.MetricsProvider
440 readOnly bool
441 mounter mount.Interface
442 deviceUtil util.DeviceUtil
443 }
444
445 var _ volume.BlockVolumeMapper = &fcDiskMapper{}
446
447 type fcDiskUnmapper struct {
448 *fcDisk
449 deviceUtil util.DeviceUtil
450 exec utilexec.Interface
451 }
452
453 var _ volume.BlockVolumeUnmapper = &fcDiskUnmapper{}
454 var _ volume.CustomBlockVolumeUnmapper = &fcDiskUnmapper{}
455
456 func (c *fcDiskUnmapper) TearDownDevice(mapPath, devicePath string) error {
457 err := c.manager.DetachBlockFCDisk(*c, mapPath, devicePath)
458 if err != nil {
459 return fmt.Errorf("fc: failed to detach disk: %s\nError: %v", mapPath, err)
460 }
461 klog.V(4).Infof("fc: %s is unmounted, deleting the directory", mapPath)
462 if err = os.RemoveAll(mapPath); err != nil {
463 return fmt.Errorf("fc: failed to delete the directory: %s\nError: %v", mapPath, err)
464 }
465 klog.V(4).Infof("fc: successfully detached disk: %s", mapPath)
466 return nil
467 }
468
469 func (c *fcDiskUnmapper) UnmapPodDevice() error {
470 return nil
471 }
472
473
474
475 func (fc *fcDisk) GetGlobalMapPath(spec *volume.Spec) (string, error) {
476 return fc.fcGlobalMapPath(spec)
477 }
478
479
480
481
482 func (fc *fcDisk) GetPodDeviceMapPath() (string, string) {
483 return fc.fcPodDeviceMapPath()
484 }
485
486 func getVolumeSource(spec *volume.Spec) (*v1.FCVolumeSource, bool, error) {
487
488
489 if spec.Volume != nil && spec.Volume.FC != nil {
490 return spec.Volume.FC, spec.Volume.FC.ReadOnly, nil
491 } else if spec.PersistentVolume != nil &&
492 spec.PersistentVolume.Spec.FC != nil {
493 return spec.PersistentVolume.Spec.FC, spec.ReadOnly, nil
494 }
495
496 return nil, false, fmt.Errorf("Spec does not reference a FibreChannel volume type")
497 }
498
499 func createPersistentVolumeFromFCVolumeSource(volumeName string, fc v1.FCVolumeSource) *v1.PersistentVolume {
500 block := v1.PersistentVolumeBlock
501 return &v1.PersistentVolume{
502 ObjectMeta: metav1.ObjectMeta{
503 Name: volumeName,
504 },
505 Spec: v1.PersistentVolumeSpec{
506 PersistentVolumeSource: v1.PersistentVolumeSource{
507 FC: &fc,
508 },
509 VolumeMode: &block,
510 },
511 }
512 }
513
514 func getWwnsLunWwids(fc *v1.FCVolumeSource) ([]string, string, []string, error) {
515 var lun string
516 var wwids []string
517 if fc.Lun != nil && len(fc.TargetWWNs) != 0 {
518 lun = strconv.Itoa(int(*fc.Lun))
519 return fc.TargetWWNs, lun, wwids, nil
520 }
521 if len(fc.WWIDs) != 0 {
522 for _, wwid := range fc.WWIDs {
523 wwids = append(wwids, strings.Replace(wwid, " ", "_", -1))
524 }
525 return fc.TargetWWNs, lun, wwids, nil
526 }
527 return nil, "", nil, fmt.Errorf("fc: no fc disk information found")
528 }
529
View as plain text