tcmu

package module
v0.0.0-...-51bcb4f Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 5, 2018 License: Apache-2.0 Imports: 21 Imported by: 5

README

go-tcmu

GoDoc

Go bindings to attach Go Readers and Writers to the Linux kernel via SCSI.

It connects to the TCM Userspace kernel API, and provides a loopback device that responds to SCSI commands. This project is based on open-iscsi/tcmu-runner, but in pure Go.

Overview

This package creates two types of Handlers (much like net/http) for SCSI block device commands. It wraps the implementation details of the kernel API, and sets up (a) a TCMU SCSI device and connect that to (b) a loopback SCSI target.

From here, the Linux IO Target kernel stack can expose the SCSI target however it likes. This includes iSCSI, vHost, etc. For further details, see the LIO wiki.

Usage

First, to use this package, you'll need the appropriate kernel modules and configfs mounted

Make sure configfs is mounted

This may already be true on your system, depending on kernel configuration. Many distributions do this by default. Check if it's mounted to /sys/kernel/config with

mount | grep configfs

Which should respond

configfs on /sys/kernel/config type configfs (rw,relatime)

To mount it explicitly:

sudo modprobe configfs
sudo mkdir -p /sys/kernel/config
sudo mount -t configfs none /sys/kernel/config
Use the TCMU module

Many distros include the module, but few activate it by default.

sudo modprobe target_core_user

Now that that's settled, there's tcmufile.go for a quick example binary that serves an image file under /dev/tcmufile/myfile.

For creating your custom SCSI targets based on a ReadWriterAt:

handler := &tcmu.SCSIHandler{
        HBA: 30, // Choose a virtual HBA number. 30 is fine.
        LUN: 0,  // The LUN attached to this HBA. Multiple LUNs can work on the same HBA, this differentiates them.
        WWN: tcmu.NaaWWN{
                OUI:      "000000",                      // Or provide your OUI
                VendorID: tcmu.GenerateSerial("foobar"), // Or provide a vendor id/serial number
                // Optional: Provide further information for your WWN
                // VendorIDExt: "0123456789abcdef", 
        },
        VolumeName: "myVolName", // The name of your volume.
        DataSizes: tcmu.DataSizes{
                VolumeSize: 5 * 1024 * 1024, // Size in bytes, eg, 5GiB
                BlockSize:  1024,            // Size of logical blocks, eg, 1K
        },
        DevReady: tcmu.SingleThreadedDevReady(
                tcmu.ReadWriterAtCmdHandler{      // Or replace with your own handler
                        RW: rw,
                }),
}
d, _ := tcmu.OpenTCMUDevice("/dev/myDevDirectory", handler)
defer d.Close()

This will create a device named /dev/myDevDirectory/myVolName with the mentioned details. It is now ready for formatting and treating like a block device.

If you wish to handle more SCSI commands, you can implement a replacement for the ReadWriterAtCmdHandler following the interface:

type SCSICmdHandler interface {
	HandleCommand(cmd *SCSICmd) (SCSIResponse, error)
}

If the default functionality was acceptable, the library contains a number of helpful Emulate functions that you can call to achieve the basic functionality.

Documentation

Overview

tcmu is a package that connects to the TCM in Userspace kernel module, a part of the LIO stack. It provides the ability to emulate a SCSI storage device in pure Go.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CachingModePage

func CachingModePage(w io.Writer, wce bool)

func FixedString

func FixedString(s string, length int) []byte

func GenerateSerial

func GenerateSerial(name string) string

Types

type DataSizes

type DataSizes struct {
	VolumeSize int64
	BlockSize  int64
}

type DevReadyFunc

type DevReadyFunc func(chan *SCSICmd, chan SCSIResponse) error

func MultiThreadedDevReady

func MultiThreadedDevReady(h SCSICmdHandler, threads int) DevReadyFunc

func SingleThreadedDevReady

func SingleThreadedDevReady(h SCSICmdHandler) DevReadyFunc

type Device

type Device struct {
	// contains filtered or unexported fields
}

func OpenTCMUDevice

func OpenTCMUDevice(devPath string, scsi *SCSIHandler) (*Device, error)

OpenTCMUDevice creates the virtual device based on the details in the SCSIHandler, eventually creating a device under devPath (eg, "/dev") with the file name scsi.VolumeName. The returned Device represents the open device connection to the kernel, and must be closed.

func (*Device) Close

func (d *Device) Close() error

func (*Device) GetDevConfig

func (d *Device) GetDevConfig() string

func (*Device) Sizes

func (d *Device) Sizes() DataSizes

type InquiryInfo

type InquiryInfo struct {
	VendorID   string
	ProductID  string
	ProductRev string
}

InquiryInfo holds the general vendor information for the emulated SCSI Device. Fields used from this will be padded or trunacted to meet the spec.

type NaaWWN

type NaaWWN struct {
	// OUI is the first three bytes (six hex digits), in ASCII, of your
	// IEEE Organizationally Unique Identifier, eg, "05abcd".
	OUI string
	// The VendorID is the first four bytes (eight hex digits), in ASCII, of
	// the device's vendor-specific ID (perhaps a serial number), eg, "2416c05f".
	VendorID string
	// The VendorIDExt is an optional eight more bytes (16 hex digits) in the same format
	// as the above, if necessary.
	VendorIDExt string
}

NaaWWN represents the World Wide Name of the SCSI device we are emulating, using the Network Address Authority standard.

func (NaaWWN) DeviceID

func (n NaaWWN) DeviceID() string

func (NaaWWN) NexusID

func (n NaaWWN) NexusID() string

type ReadWriterAt

type ReadWriterAt interface {
	io.ReaderAt
	io.WriterAt
}

type ReadWriterAtCmdHandler

type ReadWriterAtCmdHandler struct {
	RW  ReadWriterAt
	Inq *InquiryInfo
}

func (ReadWriterAtCmdHandler) HandleCommand

func (h ReadWriterAtCmdHandler) HandleCommand(cmd *SCSICmd) (SCSIResponse, error)

type SCSICmd

type SCSICmd struct {

	// Buf, if provided, may be used as a scratch buffer for copying data to and from the kernel.
	Buf []byte
	// contains filtered or unexported fields
}

SCSICmd represents a single SCSI command recieved from the kernel to the virtual target.

func (*SCSICmd) CdbLen

func (c *SCSICmd) CdbLen() int

CdbLen returns the length of the command, in bytes.

func (*SCSICmd) CheckCondition

func (c *SCSICmd) CheckCondition(key byte, asc uint16) SCSIResponse

CheckCondition returns a response providing extra sense data. Takes a Sense Key and an Additional Sense Code.

func (*SCSICmd) Command

func (c *SCSICmd) Command() byte

Command returns the SCSI command byte for the command. Useful when used as a comparison to the constants in the scsi package: c.Command() == scsi.Read6

func (*SCSICmd) Device

func (c *SCSICmd) Device() *Device

Device accesses the details of the SCSI device this command is handling.

func (*SCSICmd) GetCDB

func (c *SCSICmd) GetCDB(index int) byte

GetCDB returns the byte at `index` inside the command.

func (*SCSICmd) IllegalRequest

func (c *SCSICmd) IllegalRequest() SCSIResponse

IllegalRequest is a preset response for a request that is malformed or unexpected.

func (*SCSICmd) LBA

func (c *SCSICmd) LBA() uint64

LBA returns the block address that this command wishes to access.

func (*SCSICmd) MediumError

func (c *SCSICmd) MediumError() SCSIResponse

MediumError is a preset response for a read error condition from the device

func (*SCSICmd) NotHandled

func (c *SCSICmd) NotHandled() SCSIResponse

NotHandled creates a response and sense data that tells the kernel this device does not emulate this command.

func (*SCSICmd) Ok

func (c *SCSICmd) Ok() SCSIResponse

Ok creates a SCSIResponse to this command with SAM_STAT_GOOD, the common case for commands that succeed.

func (*SCSICmd) Read

func (c *SCSICmd) Read(b []byte) (n int, err error)

Read, for a SCSICmd, is a io.Reader from the data buffer attached to this SCSI command. If there's data to be written to the virtual device, this is the way to access it.

func (*SCSICmd) RespondSenseData

func (c *SCSICmd) RespondSenseData(status byte, sense []byte) SCSIResponse

RespondSenseData returns a SCSIResponse with the given status byte set and takes a byte array representing the SCSI sense data to be written.

func (*SCSICmd) RespondStatus

func (c *SCSICmd) RespondStatus(status byte) SCSIResponse

RespondStatus returns a SCSIResponse with the given status byte set. Ok() is equivalent to RespondStatus(scsi.SamStatGood).

func (*SCSICmd) TargetFailure

func (c *SCSICmd) TargetFailure() SCSIResponse

TargetFailure is a preset response for returning a hardware error.

func (*SCSICmd) Write

func (c *SCSICmd) Write(b []byte) (n int, err error)

Write, for a SCSICmd, is a io.Writer to the data buffer attached to this SCSI command. It's writing *to* the buffer, which happens most commonly when responding to Read commands (take data and write it back to the kernel buffer)

func (*SCSICmd) XferLen

func (c *SCSICmd) XferLen() uint32

XferLen returns the length of the data buffer this command provides for transfering data to/from the kernel.

type SCSICmdHandler

type SCSICmdHandler interface {
	HandleCommand(cmd *SCSICmd) (SCSIResponse, error)
}

SCSICmdHandler is a simple request/response handler for SCSI commands coming to TCMU. A SCSI error is reported as an SCSIResponse with an error bit set, while returning a Go error is for flagrant, process-ending errors (OOM, perhaps).

type SCSIHandler

type SCSIHandler struct {
	// The volume name and resultant device name.
	VolumeName string
	// The size of the device and the blocksize for the device.
	DataSizes DataSizes
	// The loopback HBA for the emulated SCSI device
	HBA int
	// The LUN for the emulated HBA
	LUN int
	// The SCSI World Wide Identifer for the device
	WWN WWN
	// Called once the device is ready. Should spawn a goroutine (or several)
	// to handle commands coming in the first channel, and send their associated
	// responses down the second channel, ordering optional.
	DevReady DevReadyFunc
}

SCSIHandler is the high-level data for the emulated SCSI device.

func BasicSCSIHandler

func BasicSCSIHandler(rw ReadWriterAt) *SCSIHandler

type SCSIResponse

type SCSIResponse struct {
	// contains filtered or unexported fields
}

A SCSIResponse is generated from methods on SCSICmd.

func EmulateEvpdInquiry

func EmulateEvpdInquiry(cmd *SCSICmd, inq *InquiryInfo) (SCSIResponse, error)

func EmulateInquiry

func EmulateInquiry(cmd *SCSICmd, inq *InquiryInfo) (SCSIResponse, error)

func EmulateModeSelect

func EmulateModeSelect(cmd *SCSICmd, wce bool) (SCSIResponse, error)

EmulateModeSelect checks that the only mode selected is the static one returned from EmulateModeSense. `wce` should match the Write Cache Enabled of the EmulateModeSense call.

func EmulateModeSense

func EmulateModeSense(cmd *SCSICmd, wce bool) (SCSIResponse, error)

EmulateModeSense responds to a static Mode Sense command. `wce` enables or diables the SCSI "Write Cache Enabled" flag.

func EmulateRead

func EmulateRead(cmd *SCSICmd, r io.ReaderAt) (SCSIResponse, error)

func EmulateReadCapacity16

func EmulateReadCapacity16(cmd *SCSICmd) (SCSIResponse, error)

func EmulateServiceActionIn

func EmulateServiceActionIn(cmd *SCSICmd) (SCSIResponse, error)

func EmulateStdInquiry

func EmulateStdInquiry(cmd *SCSICmd, inq *InquiryInfo) (SCSIResponse, error)

func EmulateTestUnitReady

func EmulateTestUnitReady(cmd *SCSICmd) (SCSIResponse, error)

func EmulateWrite

func EmulateWrite(cmd *SCSICmd, r io.WriterAt) (SCSIResponse, error)

type WWN

type WWN interface {
	DeviceID() string
	NexusID() string
}

WWN provides two WWNs, one for the device itself and one for the loopback device created for the kernel.

func GenerateTestWWN

func GenerateTestWWN() WWN

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL