binctr/vendor/github.com/Microsoft/hcsshim/internal/uvm/scsi.go
Jess Frazelle 94d1cfbfbf
update vendor
Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
2018-09-25 12:27:46 -04:00

202 lines
6.3 KiB
Go

package uvm
import (
"fmt"
"github.com/Microsoft/hcsshim/internal/guestrequest"
"github.com/Microsoft/hcsshim/internal/requesttype"
"github.com/Microsoft/hcsshim/internal/schema2"
"github.com/sirupsen/logrus"
)
var (
ErrNoAvailableLocation = fmt.Errorf("no available location")
ErrNotAttached = fmt.Errorf("not attached")
ErrAlreadyAttached = fmt.Errorf("already attached")
ErrNoSCSIControllers = fmt.Errorf("no SCSI controllers configured for this utility VM")
ErrNoUvmParameter = fmt.Errorf("invalid parameters - uvm parameter missing")
ErrTooManyAttachments = fmt.Errorf("too many SCSI attachments")
)
// allocateSCSI finds the next available slot on the
// SCSI controllers associated with a utility VM to use.
func (uvm *UtilityVM) allocateSCSI(hostPath string, uvmPath string) (int, int32, error) {
uvm.m.Lock()
defer uvm.m.Unlock()
for controller, luns := range uvm.scsiLocations {
for lun, si := range luns {
if si.hostPath == "" {
uvm.scsiLocations[controller][lun].hostPath = hostPath
uvm.scsiLocations[controller][lun].uvmPath = uvmPath
logrus.Debugf("uvm::allocateSCSI %d:%d %q %q", controller, lun, hostPath, uvmPath)
return controller, int32(lun), nil
}
}
}
return -1, -1, ErrNoAvailableLocation
}
func (uvm *UtilityVM) deallocateSCSI(controller int, lun int32) error {
uvm.m.Lock()
defer uvm.m.Unlock()
logrus.Debugf("uvm::deallocateSCSI %d:%d %+v", controller, lun, uvm.scsiLocations[controller][lun])
uvm.scsiLocations[controller][lun] = scsiInfo{}
return nil
}
// Lock must be held when calling this function
func (uvm *UtilityVM) findSCSIAttachment(findThisHostPath string) (int, int32, string, error) {
for controller, luns := range uvm.scsiLocations {
for lun, si := range luns {
if si.hostPath == findThisHostPath {
logrus.Debugf("uvm::findSCSIAttachment %d:%d %+v", controller, lun, si)
return controller, int32(lun), si.uvmPath, nil
}
}
}
return -1, -1, "", ErrNotAttached
}
// AddSCSI adds a SCSI disk to a utility VM at the next available location.
//
// We are in control of everything ourselves. Hence we have ref-
// counting and so-on tracking what SCSI locations are available or used.
//
// hostPath is required
// uvmPath is optional.
//
// Returns the controller ID (0..3) and LUN (0..63) where the disk is attached.
func (uvm *UtilityVM) AddSCSI(hostPath string, uvmPath string) (int, int32, error) {
if uvm == nil {
return -1, -1, ErrNoUvmParameter
}
logrus.Debugf("uvm::AddSCSI id:%s hostPath:%s uvmPath:%s", uvm.id, hostPath, uvmPath)
if uvm.scsiControllerCount == 0 {
return -1, -1, ErrNoSCSIControllers
}
uvm.m.Lock()
if _, _, _, err := uvm.findSCSIAttachment(hostPath); err == nil {
uvm.m.Unlock()
return -1, -1, ErrAlreadyAttached
}
uvm.m.Unlock()
controller, lun, err := uvm.allocateSCSI(hostPath, uvmPath)
if err != nil {
return -1, -1, err
}
// Note: Can remove this check post-RS5 if multiple controllers are supported
if controller > 0 {
return -1, -1, ErrTooManyAttachments
}
SCSIModification := &hcsschema.ModifySettingRequest{
RequestType: requesttype.Add,
Settings: hcsschema.Attachment{
Path: hostPath,
Type_: "VirtualDisk",
},
ResourcePath: fmt.Sprintf("VirtualMachine/Devices/Scsi/%d/Attachments/%d", controller, lun),
}
if uvmPath != "" {
if uvm.operatingSystem == "windows" {
SCSIModification.GuestRequest = guestrequest.GuestRequest{
ResourceType: guestrequest.ResourceTypeMappedVirtualDisk,
RequestType: requesttype.Add,
Settings: guestrequest.WCOWMappedVirtualDisk{
ContainerPath: uvmPath,
Lun: lun,
},
}
} else {
SCSIModification.GuestRequest = guestrequest.GuestRequest{
ResourceType: guestrequest.ResourceTypeMappedVirtualDisk,
RequestType: requesttype.Add,
Settings: guestrequest.LCOWMappedVirtualDisk{
MountPath: uvmPath,
Lun: uint8(lun),
Controller: uint8(controller),
ReadOnly: false,
},
}
}
}
if err := uvm.Modify(SCSIModification); err != nil {
uvm.deallocateSCSI(controller, lun)
return -1, -1, fmt.Errorf("uvm::AddSCSI: failed to modify utility VM configuration: %s", err)
}
logrus.Debugf("uvm::AddSCSI id:%s hostPath:%s added at %d:%d", uvm.id, hostPath, controller, lun)
return controller, int32(lun), nil
}
// RemoveSCSI removes a SCSI disk from a utility VM. As an external API, it
// is "safe". Internal use can call removeSCSI.
func (uvm *UtilityVM) RemoveSCSI(hostPath string) error {
uvm.m.Lock()
defer uvm.m.Unlock()
if uvm.scsiControllerCount == 0 {
return ErrNoSCSIControllers
}
// Make sure is actually attached
controller, lun, uvmPath, err := uvm.findSCSIAttachment(hostPath)
if err != nil {
return err
}
if err := uvm.removeSCSI(hostPath, uvmPath, controller, lun); err != nil {
return fmt.Errorf("failed to remove SCSI disk %s from container %s: %s", hostPath, uvm.id, err)
}
return nil
}
// removeSCSI is the internally callable "unsafe" version of RemoveSCSI. The mutex
// MUST be held when calling this function.
func (uvm *UtilityVM) removeSCSI(hostPath string, uvmPath string, controller int, lun int32) error {
logrus.Debugf("uvm::RemoveSCSI id:%s hostPath:%s", uvm.id, hostPath)
scsiModification := &hcsschema.ModifySettingRequest{
RequestType: requesttype.Remove,
ResourcePath: fmt.Sprintf("VirtualMachine/Devices/Scsi/%d/Attachments/%d", controller, lun),
}
// Include the GuestRequest so that the GCS ejects the disk cleanly if the disk was attached/mounted
if uvmPath != "" {
if uvm.operatingSystem == "windows" {
scsiModification.GuestRequest = guestrequest.GuestRequest{
ResourceType: guestrequest.ResourceTypeMappedVirtualDisk,
RequestType: requesttype.Remove,
Settings: guestrequest.WCOWMappedVirtualDisk{
ContainerPath: uvmPath,
Lun: lun,
},
}
} else {
scsiModification.GuestRequest = guestrequest.GuestRequest{
ResourceType: guestrequest.ResourceTypeMappedVirtualDisk,
RequestType: requesttype.Remove,
Settings: guestrequest.LCOWMappedVirtualDisk{
MountPath: uvmPath, // May be blank in attach-only
Lun: uint8(lun),
Controller: uint8(controller),
},
}
}
}
if err := uvm.Modify(scsiModification); err != nil {
return err
}
uvm.scsiLocations[controller][lun] = scsiInfo{}
logrus.Debugf("uvm::RemoveSCSI: Success %s removed from %s %d:%d", hostPath, uvm.id, controller, lun)
return nil
}