202 lines
6.3 KiB
Go
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
|
|
}
|