Fix reconstruction of FC volumes

It should reconstruct all WWNs or WWIDs instead of just the first one.
On-disk directory name format had to be changed to contain all WWNs/WWIDs.
pull/564/head
Jan Safranek 2019-02-13 14:57:16 +01:00
parent 40c91a0951
commit 662b683de4
3 changed files with 123 additions and 61 deletions

View File

@ -19,6 +19,7 @@ package fc
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"strconv" "strconv"
"strings" "strings"
@ -257,40 +258,20 @@ func (plugin *fcPlugin) ConstructVolumeSpec(volumeName, mountPath string) (*volu
if len(globalPDPath) == 0 { if len(globalPDPath) == 0 {
return nil, fmt.Errorf("couldn't fetch globalPDPath. failed to obtain volume spec") return nil, fmt.Errorf("couldn't fetch globalPDPath. failed to obtain volume spec")
} }
arr := strings.Split(globalPDPath, "/")
if len(arr) < 1 { wwns, lun, wwids, err := parsePDName(globalPDPath)
return nil, fmt.Errorf("failed to retrieve volume plugin information from globalPDPath: %v", globalPDPath) if err != nil {
return nil, fmt.Errorf("failed to retrieve volume plugin information from globalPDPath: %s", err)
} }
volumeInfo := arr[len(arr)-1]
// Create volume from wwn+lun or wwid // Create volume from wwn+lun or wwid
var fcVolume *v1.Volume fcVolume := &v1.Volume{
if strings.Contains(volumeInfo, "-lun-") { Name: volumeName,
wwnLun := strings.Split(volumeInfo, "-lun-") VolumeSource: v1.VolumeSource{
if len(wwnLun) < 2 { FC: &v1.FCVolumeSource{WWIDs: wwids, Lun: &lun, TargetWWNs: wwns},
return nil, fmt.Errorf("failed to retrieve TargetWWN and Lun. volumeInfo is invalid: %v", volumeInfo) },
}
lun, err := strconv.Atoi(wwnLun[1])
if err != nil {
return nil, err
}
lun32 := int32(lun)
fcVolume = &v1.Volume{
Name: volumeName,
VolumeSource: v1.VolumeSource{
FC: &v1.FCVolumeSource{TargetWWNs: []string{wwnLun[0]}, Lun: &lun32},
},
}
klog.V(5).Infof("ConstructVolumeSpec: TargetWWNs: %v, Lun: %v",
fcVolume.VolumeSource.FC.TargetWWNs, *fcVolume.VolumeSource.FC.Lun)
} else {
fcVolume = &v1.Volume{
Name: volumeName,
VolumeSource: v1.VolumeSource{
FC: &v1.FCVolumeSource{WWIDs: []string{volumeInfo}},
},
}
klog.V(5).Infof("ConstructVolumeSpec: WWIDs: %v", fcVolume.VolumeSource.FC.WWIDs)
} }
klog.V(5).Infof("ConstructVolumeSpec: TargetWWNs: %v, Lun: %v, WWIDs: %v",
fcVolume.VolumeSource.FC.TargetWWNs, *fcVolume.VolumeSource.FC.Lun, fcVolume.VolumeSource.FC.WWIDs)
return volume.NewSpecFromVolume(fcVolume), nil return volume.NewSpecFromVolume(fcVolume), nil
} }
@ -310,36 +291,23 @@ func (plugin *fcPlugin) ConstructBlockVolumeSpec(podUID types.UID, volumeName, m
} }
klog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err) klog.V(5).Infof("globalMapPathUUID: %v, err: %v", globalMapPathUUID, err)
// Retrieve volumePluginDependentPath from globalMapPathUUID // Retrieve globalPDPath from globalMapPathUUID
// globalMapPathUUID examples: // globalMapPathUUID examples:
// wwn+lun: plugins/kubernetes.io/fc/volumeDevices/50060e801049cfd1-lun-0/{pod uuid} // wwn+lun: plugins/kubernetes.io/fc/volumeDevices/50060e801049cfd1-lun-0/{pod uuid}
// wwid: plugins/kubernetes.io/fc/volumeDevices/3600508b400105e210000900000490000/{pod uuid} // wwid: plugins/kubernetes.io/fc/volumeDevices/3600508b400105e210000900000490000/{pod uuid}
arr := strings.Split(globalMapPathUUID, "/") globalPDPath := filepath.Dir(globalMapPathUUID)
if len(arr) < 2 {
return nil, fmt.Errorf("Fail to retrieve volume plugin information from globalMapPathUUID: %v", globalMapPathUUID)
}
l := len(arr) - 2
volumeInfo := arr[l]
// Create volume from wwn+lun or wwid // Create volume from wwn+lun or wwid
var fcPV *v1.PersistentVolume wwns, lun, wwids, err := parsePDName(globalPDPath)
if strings.Contains(volumeInfo, "-lun-") { if err != nil {
wwnLun := strings.Split(volumeInfo, "-lun-") return nil, fmt.Errorf("failed to retrieve volume plugin information from globalPDPath: %s", err)
lun, err := strconv.Atoi(wwnLun[1])
if err != nil {
return nil, err
}
lun32 := int32(lun)
fcPV = createPersistentVolumeFromFCVolumeSource(volumeName,
v1.FCVolumeSource{TargetWWNs: []string{wwnLun[0]}, Lun: &lun32})
klog.V(5).Infof("ConstructBlockVolumeSpec: TargetWWNs: %v, Lun: %v",
fcPV.Spec.PersistentVolumeSource.FC.TargetWWNs,
*fcPV.Spec.PersistentVolumeSource.FC.Lun)
} else {
fcPV = createPersistentVolumeFromFCVolumeSource(volumeName,
v1.FCVolumeSource{WWIDs: []string{volumeInfo}})
klog.V(5).Infof("ConstructBlockVolumeSpec: WWIDs: %v", fcPV.Spec.PersistentVolumeSource.FC.WWIDs)
} }
fcPV := createPersistentVolumeFromFCVolumeSource(volumeName,
v1.FCVolumeSource{TargetWWNs: wwns, Lun: &lun, WWIDs: wwids})
klog.V(5).Infof("ConstructBlockVolumeSpec: TargetWWNs: %v, Lun: %v, WWIDs: %v",
fcPV.Spec.PersistentVolumeSource.FC.TargetWWNs,
*fcPV.Spec.PersistentVolumeSource.FC.Lun,
fcPV.Spec.PersistentVolumeSource.FC.WWIDs)
return volume.NewSpecFromPersistentVolume(fcPV, false), nil return volume.NewSpecFromPersistentVolume(fcPV, false), nil
} }

View File

@ -22,6 +22,7 @@ import (
"os" "os"
"path" "path"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"k8s.io/api/core/v1" "k8s.io/api/core/v1"
@ -131,20 +132,47 @@ func scsiHostRescan(io ioHandler) {
} }
} }
// make a directory like /var/lib/kubelet/plugins/kubernetes.io/fc/target-lun-0 // make a directory like /var/lib/kubelet/plugins/kubernetes.io/fc/target1-target2-lun-0
func makePDNameInternal(host volume.VolumeHost, wwns []string, lun string, wwids []string) string { func makePDNameInternal(host volume.VolumeHost, wwns []string, lun string, wwids []string) string {
if len(wwns) != 0 { if len(wwns) != 0 {
return path.Join(host.GetPluginDir(fcPluginName), wwns[0]+"-lun-"+lun) w := strings.Join(wwns, "-")
return path.Join(host.GetPluginDir(fcPluginName), w+"-lun-"+lun)
} }
return path.Join(host.GetPluginDir(fcPluginName), wwids[0]) return path.Join(host.GetPluginDir(fcPluginName), strings.Join(wwids, "-"))
} }
// make a directory like /var/lib/kubelet/plugins/kubernetes.io/fc/volumeDevices/target-lun-0 // make a directory like /var/lib/kubelet/plugins/kubernetes.io/fc/volumeDevices/target-lun-0
func makeVDPDNameInternal(host volume.VolumeHost, wwns []string, lun string, wwids []string) string { func makeVDPDNameInternal(host volume.VolumeHost, wwns []string, lun string, wwids []string) string {
if len(wwns) != 0 { if len(wwns) != 0 {
return path.Join(host.GetVolumeDevicePluginDir(fcPluginName), wwns[0]+"-lun-"+lun) w := strings.Join(wwns, "-")
return path.Join(host.GetVolumeDevicePluginDir(fcPluginName), w+"-lun-"+lun)
} }
return path.Join(host.GetVolumeDevicePluginDir(fcPluginName), wwids[0]) return path.Join(host.GetVolumeDevicePluginDir(fcPluginName), strings.Join(wwids, "-"))
}
func parsePDName(path string) (wwns []string, lun int32, wwids []string, err error) {
// parse directory name created by makePDNameInternal or makeVDPDNameInternal
dirname := filepath.Base(path)
components := strings.Split(dirname, "-")
l := len(components)
if l == 1 {
// No '-', it must be single WWID
return nil, 0, components, nil
}
if components[l-2] == "lun" {
// it has -lun-, it's list of WWNs + lun number as the last component
if l == 2 {
return nil, 0, nil, fmt.Errorf("no wwn in: %s", dirname)
}
lun, err := strconv.Atoi(components[l-1])
if err != nil {
return nil, 0, nil, err
}
return components[:l-2], int32(lun), nil, nil
}
// no -lun-, it's just list of WWIDs
return nil, 0, components, nil
} }
type fcUtil struct{} type fcUtil struct{}

View File

@ -18,6 +18,7 @@ package fc
import ( import (
"os" "os"
"reflect"
"testing" "testing"
"time" "time"
@ -116,3 +117,68 @@ func TestSearchDiskWWID(t *testing.T) {
t.Errorf("no fc disk found") t.Errorf("no fc disk found")
} }
} }
func TestParsePDName(t *testing.T) {
tests := []struct {
name string
path string
wwns []string
lun int32
wwids []string
expectError bool
}{
{
name: "single WWID",
path: "/var/lib/kubelet/plugins/kubernetes.io/fc/60050763008084e6e0000000000001ae",
wwids: []string{"60050763008084e6e0000000000001ae"},
},
{
name: "multiple WWID",
path: "/var/lib/kubelet/plugins/kubernetes.io/fc/60050763008084e6e0000000000001ae-60050763008084e6e0000000000001af",
wwids: []string{"60050763008084e6e0000000000001ae", "60050763008084e6e0000000000001af"},
},
{
name: "single WWN",
path: "/var/lib/kubelet/plugins/kubernetes.io/fc/50050768030539b6-lun-0",
wwns: []string{"50050768030539b6"},
lun: 0,
},
{
name: "multiple WWNs",
path: "/var/lib/kubelet/plugins/kubernetes.io/fc/50050768030539b6-50050768030539b7-lun-0",
wwns: []string{"50050768030539b6", "50050768030539b7"},
lun: 0,
},
{
name: "no WWNs",
path: "/var/lib/kubelet/plugins/kubernetes.io/fc/lun-0",
expectError: true,
},
{
name: "invalid lun",
path: "/var/lib/kubelet/plugins/kubernetes.io/fc/50050768030539b6-lun-x",
expectError: true,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
wwns, lun, wwids, err := parsePDName(test.path)
if test.expectError && err == nil {
t.Errorf("expected error but got none")
}
if !test.expectError && err != nil {
t.Errorf("got unexpected error: %s", err)
}
if !reflect.DeepEqual(wwns, test.wwns) {
t.Errorf("expected WWNs %+v, got %+v", test.wwns, wwns)
}
if lun != test.lun {
t.Errorf("expected lun %d, got %d", test.lun, lun)
}
if !reflect.DeepEqual(wwids, test.wwids) {
t.Errorf("expected WWIDs %+v, got %+v", test.wwids, wwids)
}
})
}
}