Merge pull request #26658 from hpcloud/hpe/vsphere-vol-bugfixes

Automatic merge from submit-queue

Fixing vSphere Volume plugin bugs

This PR fixes #26646 and targeted for 1.3
This commit is contained in:
k8s-merge-robot 2016-06-19 21:06:13 -07:00 committed by GitHub
commit 4fcbc0ada7
4 changed files with 63 additions and 36 deletions

View File

@ -44,7 +44,8 @@ const ActivePowerState = "poweredOn"
const DefaultDiskController = "scsi" const DefaultDiskController = "scsi"
const DefaultSCSIControllerType = "lsilogic" const DefaultSCSIControllerType = "lsilogic"
var ErrNoDiskUUIDFound = errors.New("no disk UUID found") var ErrNoDiskUUIDFound = errors.New("No disk UUID found")
var ErrNoDiskIDFound = errors.New("No vSphere disk ID found")
var ErrNoDevicesFound = errors.New("No devices found") var ErrNoDevicesFound = errors.New("No devices found")
// VSphere is an implementation of cloud provider Interface for VSphere. // VSphere is an implementation of cloud provider Interface for VSphere.
@ -273,7 +274,7 @@ func (i *Instances) List(filter string) ([]string, error) {
return nil, err return nil, err
} }
glog.V(3).Infof("Found %s instances matching %s: %s", glog.V(3).Infof("found %s instances matching %s: %s",
len(vmList), filter, vmList) len(vmList), filter, vmList)
return vmList, nil return vmList, nil
@ -435,9 +436,9 @@ func (vs *VSphere) GetZone() (cloudprovider.Zone, error) {
return cloudprovider.Zone{Region: vs.cfg.Global.Datacenter}, nil return cloudprovider.Zone{Region: vs.cfg.Global.Datacenter}, nil
} }
// Routes returns an implementation of Routes for vSphere. // Routes returns a false since the interface is not supported for vSphere.
func (vs *VSphere) Routes() (cloudprovider.Routes, bool) { func (vs *VSphere) Routes() (cloudprovider.Routes, bool) {
return nil, true return nil, false
} }
// ScrubDNS filters DNS settings for pods. // ScrubDNS filters DNS settings for pods.
@ -525,7 +526,7 @@ func (vs *VSphere) AttachDisk(vmDiskPath string, nodeName string) (diskID string
// create a scsi controller if there is not one // create a scsi controller if there is not one
newSCSIController, err := vmDevices.CreateSCSIController(vs.cfg.Disk.SCSIControllerType) newSCSIController, err := vmDevices.CreateSCSIController(vs.cfg.Disk.SCSIControllerType)
if err != nil { if err != nil {
glog.V(3).Infof("Cannot create new SCSI controller - %v", err) glog.V(3).Infof("cannot create new SCSI controller - %v", err)
return "", "", err return "", "", err
} }
configNewSCSIController := newSCSIController.(types.BaseVirtualSCSIController).GetVirtualSCSIController() configNewSCSIController := newSCSIController.(types.BaseVirtualSCSIController).GetVirtualSCSIController()
@ -536,7 +537,7 @@ func (vs *VSphere) AttachDisk(vmDiskPath string, nodeName string) (diskID string
// add the scsi controller to virtual machine // add the scsi controller to virtual machine
err = vm.AddDevice(context.TODO(), newSCSIController) err = vm.AddDevice(context.TODO(), newSCSIController)
if err != nil { if err != nil {
glog.V(3).Infof("Cannot add SCSI controller to vm - %v", err) glog.V(3).Infof("cannot add SCSI controller to vm - %v", err)
// attempt clean up of scsi controller // attempt clean up of scsi controller
if vmDevices, err := vm.Device(ctx); err == nil { if vmDevices, err := vm.Device(ctx); err == nil {
cleanUpController(newSCSIController, vmDevices, vm, ctx) cleanUpController(newSCSIController, vmDevices, vm, ctx)
@ -551,7 +552,7 @@ func (vs *VSphere) AttachDisk(vmDiskPath string, nodeName string) (diskID string
return "", "", err return "", "", err
} }
if diskController, err = vmDevices.FindDiskController(vs.cfg.Disk.DiskController); err != nil { if diskController, err = vmDevices.FindDiskController(vs.cfg.Disk.DiskController); err != nil {
glog.V(3).Infof("Cannot find disk controller - %v", err) glog.V(3).Infof("cannot find disk controller - %v", err)
// attempt clean up of scsi controller // attempt clean up of scsi controller
cleanUpController(newSCSIController, vmDevices, vm, ctx) cleanUpController(newSCSIController, vmDevices, vm, ctx)
return "", "", err return "", "", err
@ -562,12 +563,11 @@ func (vs *VSphere) AttachDisk(vmDiskPath string, nodeName string) (diskID string
disk := vmDevices.CreateDisk(diskController, ds.Reference(), vmDiskPath) disk := vmDevices.CreateDisk(diskController, ds.Reference(), vmDiskPath)
backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo) backing := disk.Backing.(*types.VirtualDiskFlatVer2BackingInfo)
backing.DiskMode = string(types.VirtualDiskModeIndependent_persistent) backing.DiskMode = string(types.VirtualDiskModeIndependent_persistent)
disk = vmDevices.ChildDisk(disk)
// Attach disk to the VM // Attach disk to the VM
err = vm.AddDevice(ctx, disk) err = vm.AddDevice(ctx, disk)
if err != nil { if err != nil {
glog.V(3).Infof("Cannot add disk to the vm - %v", err) glog.V(3).Infof("cannot attach disk to the vm - %v", err)
if newSCSICreated { if newSCSICreated {
cleanUpController(newSCSIController, vmDevices, vm, ctx) cleanUpController(newSCSIController, vmDevices, vm, ctx)
} }
@ -611,13 +611,29 @@ func getVirtualDiskUUID(newDevice types.BaseVirtualDevice) (string, error) {
if b, ok := vd.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok { if b, ok := vd.Backing.(*types.VirtualDiskFlatVer2BackingInfo); ok {
uuidWithNoHypens := strings.Replace(b.Uuid, "-", "", -1) uuidWithNoHypens := strings.Replace(b.Uuid, "-", "", -1)
return uuidWithNoHypens, nil return strings.ToLower(uuidWithNoHypens), nil
} }
return "", ErrNoDiskUUIDFound return "", ErrNoDiskUUIDFound
} }
func getVirtualDiskID(volPath string, vmDevices object.VirtualDeviceList) (string, error) {
// filter vm devices to retrieve disk ID for the given vmdk file
for _, device := range vmDevices {
if vmDevices.TypeName(device) == "VirtualDisk" {
d := device.GetVirtualDevice()
if b, ok := d.Backing.(types.BaseVirtualDeviceFileBackingInfo); ok {
fileName := b.GetVirtualDeviceFileBackingInfo().FileName
if fileName == volPath {
return vmDevices.Name(device), nil
}
}
}
}
return "", ErrNoDiskIDFound
}
// Detaches given virtual disk volume from the compute running kubelet. // Detaches given virtual disk volume from the compute running kubelet.
func (vs *VSphere) DetachDisk(diskID string, nodeName string) error { func (vs *VSphere) DetachDisk(volPath string, nodeName string) error {
// Create context // Create context
ctx, cancel := context.WithCancel(context.Background()) ctx, cancel := context.WithCancel(context.Background())
defer cancel() defer cancel()
@ -642,13 +658,19 @@ func (vs *VSphere) DetachDisk(diskID string, nodeName string) error {
return err return err
} }
diskID, err := getVirtualDiskID(volPath, vmDevices)
if err != nil {
glog.Warningf("disk ID not found for %v ", volPath)
return err
}
// Remove disk from VM // Remove disk from VM
device := vmDevices.Find(diskID) device := vmDevices.Find(diskID)
if device == nil { if device == nil {
return fmt.Errorf("device '%s' not found", diskID) return fmt.Errorf("device '%s' not found", diskID)
} }
err = vm.RemoveDevice(ctx, false, device) err = vm.RemoveDevice(ctx, true, device)
if err != nil { if err != nil {
return err return err
} }

View File

@ -224,19 +224,19 @@ func TestVolumes(t *testing.T) {
t.Fatalf("Cannot create a new VMDK volume: %v", err) t.Fatalf("Cannot create a new VMDK volume: %v", err)
} }
diskID, _, err := vs.AttachDisk(volPath, "") _, _, err = vs.AttachDisk(volPath, "")
if err != nil { if err != nil {
t.Fatalf("Cannot attach volume(%s) to VM(%s): %v", volPath, srvs[0], err) t.Fatalf("Cannot attach volume(%s) to VM(%s): %v", volPath, srvs[0], err)
} }
err = vs.DetachDisk(diskID, "") err = vs.DetachDisk(volPath, "")
if err != nil { if err != nil {
t.Fatalf("Cannot detach disk(%s) from VM(%s): %v", diskID, srvs[0], err) t.Fatalf("Cannot detach disk(%s) from VM(%s): %v", volPath, srvs[0], err)
} }
// todo: Deleting a volume after detach currently not working through API or UI (vSphere) // todo: Deleting a volume after detach currently not working through API or UI (vSphere)
// err = vs.DeleteVolume(volPath) // err = vs.DeleteVolume(volPath)
// if err != nil { // if err != nil {
// t.Fatalf("Cannot delete VMDK volume %s: %v", volPath, err) // t.Fatalf("Cannot delete VMDK volume %s: %v", volPath, err)
// } // }
} }

View File

@ -21,6 +21,7 @@ import (
"fmt" "fmt"
"os" "os"
"path" "path"
"strings"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api"
@ -208,7 +209,7 @@ func (b *vsphereVolumeMounter) SetUpAt(dir string, fsGroup *int64) error {
} }
globalPDPath := makeGlobalPDPath(b.plugin.host, b.volPath) globalPDPath := makeGlobalPDPath(b.plugin.host, b.volPath)
if err := b.manager.AttachDisk(b, globalPDPath); err != nil { if err := b.manager.AttachDisk(b, globalPDPath); err != nil {
glog.V(4).Infof("AttachDisk failed: %v", err) glog.V(3).Infof("AttachDisk failed: %v", err)
return err return err
} }
glog.V(3).Infof("vSphere volume %s attached", b.volPath) glog.V(3).Infof("vSphere volume %s attached", b.volPath)
@ -291,7 +292,11 @@ func (v *vsphereVolumeUnmounter) TearDownAt(dir string) error {
return fmt.Errorf("directory %s is not mounted", dir) return fmt.Errorf("directory %s is not mounted", dir)
} }
v.volPath = path.Base(refs[0]) // space between datastore and vmdk name in volumePath is encoded as '\040' when returned by GetMountRefs().
// volumePath eg: "[local] xxx.vmdk" provided to attach/mount
// replacing \040 with space to match the actual volumePath
mountPath := strings.Replace(path.Base(refs[0]), "\\040", " ", -1)
v.volPath = mountPath
glog.V(4).Infof("Found volume %s mounted to %s", v.volPath, dir) glog.V(4).Infof("Found volume %s mounted to %s", v.volPath, dir)
// Reload list of references, there might be SetUpAt finished in the meantime // Reload list of references, there might be SetUpAt finished in the meantime

View File

@ -25,7 +25,6 @@ import (
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/keymutex" "k8s.io/kubernetes/pkg/util/keymutex"
"k8s.io/kubernetes/pkg/volume" "k8s.io/kubernetes/pkg/volume"
) )
@ -34,6 +33,8 @@ const (
maxRetries = 10 maxRetries = 10
) )
var ErrProbeVolume = errors.New("Error scanning attached volumes")
// Singleton key mutex for keeping attach/detach operations for the same PD atomic // Singleton key mutex for keeping attach/detach operations for the same PD atomic
var attachDetachMutex = keymutex.NewKeyMutex() var attachDetachMutex = keymutex.NewKeyMutex()
@ -55,7 +56,7 @@ func (util *VsphereDiskUtil) AttachDisk(vm *vsphereVolumeMounter, globalPDPath s
diskID, diskUUID, attachError := cloud.AttachDisk(vm.volPath, "") diskID, diskUUID, attachError := cloud.AttachDisk(vm.volPath, "")
if attachError != nil { if attachError != nil {
return err return attachError
} else if diskUUID == "" { } else if diskUUID == "" {
return errors.New("Disk UUID has no value") return errors.New("Disk UUID has no value")
} }
@ -68,7 +69,7 @@ func (util *VsphereDiskUtil) AttachDisk(vm *vsphereVolumeMounter, globalPDPath s
for { for {
devicePath = verifyDevicePath(diskUUID) devicePath = verifyDevicePath(diskUUID)
// probe the attached vol so that symlink in /dev/disk/by-id is created // probe the attached vol so that symlink in /dev/disk/by-id is created
probeAttachedVolume() scsiHostRescan()
_, err := os.Stat(devicePath) _, err := os.Stat(devicePath)
if err == nil { if err == nil {
@ -106,13 +107,25 @@ func (util *VsphereDiskUtil) AttachDisk(vm *vsphereVolumeMounter, globalPDPath s
return nil return nil
} }
// rescan scsi bus
func scsiHostRescan() {
scsi_path := "/sys/class/scsi_host/"
if dirs, err := ioutil.ReadDir(scsi_path); err == nil {
for _, f := range dirs {
name := scsi_path + f.Name() + "/scan"
data := []byte("- - -")
ioutil.WriteFile(name, data, 0666)
}
}
}
func verifyDevicePath(diskUUID string) string { func verifyDevicePath(diskUUID string) string {
files, _ := ioutil.ReadDir("/dev/disk/by-id/") files, _ := ioutil.ReadDir("/dev/disk/by-id/")
for _, f := range files { for _, f := range files {
// TODO: should support other controllers // TODO: should support other controllers
if strings.Contains(f.Name(), "scsi-") { if strings.Contains(f.Name(), "scsi-") {
devID := f.Name()[len("scsi-"):len(f.Name())] devID := f.Name()[len("scsi-"):len(f.Name())]
if strings.Contains(diskUUID, devID) { if strings.Contains(devID, diskUUID) {
glog.V(4).Infof("Found disk attached as %q; full devicepath: %s\n", f.Name(), path.Join("/dev/disk/by-id/", f.Name())) glog.V(4).Infof("Found disk attached as %q; full devicepath: %s\n", f.Name(), path.Join("/dev/disk/by-id/", f.Name()))
return path.Join("/dev/disk/by-id/", f.Name()) return path.Join("/dev/disk/by-id/", f.Name())
} }
@ -122,19 +135,6 @@ func verifyDevicePath(diskUUID string) string {
return "" return ""
} }
func probeAttachedVolume() error {
executor := exec.New()
args := []string{"trigger"}
cmd := executor.Command("/usr/bin/udevadm", args...)
_, err := cmd.CombinedOutput()
if err != nil {
glog.Errorf("error running udevadm trigger %v\n", err)
return err
}
glog.V(4).Infof("Successfully probed all attachments")
return nil
}
// Unmounts the device and detaches the disk from the kubelet's host machine. // Unmounts the device and detaches the disk from the kubelet's host machine.
func (util *VsphereDiskUtil) DetachDisk(vu *vsphereVolumeUnmounter) error { func (util *VsphereDiskUtil) DetachDisk(vu *vsphereVolumeUnmounter) error {
@ -156,7 +156,7 @@ func (util *VsphereDiskUtil) DetachDisk(vu *vsphereVolumeUnmounter) error {
return err return err
} }
if err = cloud.DetachDisk(vu.diskID, ""); err != nil { if err = cloud.DetachDisk(vu.volPath, ""); err != nil {
return err return err
} }
glog.V(2).Infof("Successfully detached vSphere volume %s", vu.volPath) glog.V(2).Infof("Successfully detached vSphere volume %s", vu.volPath)