diff --git a/qemu/qmp.go b/qemu/qmp.go index 3727b8478e..8888ad4d4a 100644 --- a/qemu/qmp.go +++ b/qemu/qmp.go @@ -672,6 +672,45 @@ func (q *QMP) ExecuteDeviceAdd(ctx context.Context, blockdevID, devID, driver, b return q.executeCommand(ctx, "device_add", args, nil) } +// ExecuteSCSIDeviceAdd adds the guest portion of a block device to a QEMU instance +// using a SCSI driver with the device_add command. blockdevID should match the +// blockdevID passed to a previous call to ExecuteBlockdevAdd. devID is the id of +// the device to add. Both strings must be valid QMP identifiers. driver is the name of the +// scsi driver,e.g., scsi-hd, and bus is the name of a SCSI controller bus. +// scsiID is the SCSI id, lun is logical unit number. scsiID and lun are optional, a negative value +// for scsiID and lun is ignored. +func (q *QMP) ExecuteSCSIDeviceAdd(ctx context.Context, blockdevID, devID, driver, bus string, scsiID, lun int) error { + // TBD: Add drivers for scsi passthrough like scsi-generic and scsi-block + drivers := []string{"scsi-hd", "scsi-cd", "scsi-disk"} + + isSCSIDriver := false + for _, d := range drivers { + if driver == d { + isSCSIDriver = true + break + } + } + + if !isSCSIDriver { + return fmt.Errorf("Invalid SCSI driver provided %s", driver) + } + + args := map[string]interface{}{ + "id": devID, + "driver": driver, + "drive": blockdevID, + "bus": bus, + } + if scsiID >= 0 { + args["scsi-id"] = scsiID + } + if lun >= 0 { + args["lun"] = lun + } + + return q.executeCommand(ctx, "device_add", args, nil) +} + // ExecuteBlockdevDel deletes a block device by sending a x-blockdev-del command // for qemu versions < 2.9. It sends the updated blockdev-del command for qemu>=2.9. // blockdevID is the id of the block device to be deleted. Typically, this will diff --git a/qemu/qmp_test.go b/qemu/qmp_test.go index 9b87fbcc5d..85850fcb5b 100644 --- a/qemu/qmp_test.go +++ b/qemu/qmp_test.go @@ -389,6 +389,31 @@ func TestQMPDeviceAdd(t *testing.T) { <-disconnectedCh } +// Checks that the device_add command for scsi is correctly sent. +// +// We start a QMPLoop, send the device_add command and stop the loop. +// +// The device_add command should be correctly sent and the QMP loop should +// exit gracefully. +func TestQMPSCSIDeviceAdd(t *testing.T) { + connectedCh := make(chan *QMPVersion) + disconnectedCh := make(chan struct{}) + buf := newQMPTestCommandBuffer(t) + buf.AddCommand("device_add", nil, "return", nil) + cfg := QMPConfig{Logger: qmpTestLogger{}} + q := startQMPLoop(buf, cfg, connectedCh, disconnectedCh) + checkVersion(t, connectedCh) + blockdevID := fmt.Sprintf("drive_%s", volumeUUID) + devID := fmt.Sprintf("device_%s", volumeUUID) + err := q.ExecuteSCSIDeviceAdd(context.Background(), blockdevID, devID, + "scsi-hd", "scsi0.0", 1, 2) + if err != nil { + t.Fatalf("Unexpected error %v", err) + } + q.Shutdown() + <-disconnectedCh +} + // Checks that the x-blockdev-del command is correctly sent. // // We start a QMPLoop, send the x-blockdev-del command and stop the loop.