Merge pull request #13 from gostor/lun-mapping

mapping lun and fix portal management
This commit is contained in:
Lei Xue
2016-10-04 13:43:48 +08:00
committed by GitHub
16 changed files with 298 additions and 119 deletions
+9 -8
View File
@@ -1,10 +1,11 @@
sudo: required sudo: required
dist: trusty dist: trusty
env:
- TARGET=iqn.2016-09.com.gotgt.gostor:example_tgt_0
language: go language: go
go: go:
- 1.4 - 1.6
- 1.5
install: install:
- true - true
@@ -23,7 +24,7 @@ script:
- hack/verify-gofmt.sh - hack/verify-gofmt.sh
- dd if=/dev/zero of=/var/tmp/disk.img bs=1024 count=10240 - dd if=/dev/zero of=/var/tmp/disk.img bs=1024 count=10240
- mkdir ${HOME}/.gotgt - mkdir ${HOME}/.gotgt
- echo '{"targets":{"test-iscsi-target":{"name":"test-iscsi-target","luns":["/var/tmp/disk.img"]}}}' > ${HOME}/.gotgt/config.json - echo '{"storages":[{"deviceID":1000,"path":"file:/var/tmp/disk.img","online":true}],"targets":{"iqn.2016-09.com.gotgt.gostor:example_tgt_0":{"portals":["127.0.0.1"],"luns":{"0":1000}}}}' > ${HOME}/.gotgt/config.json
- ./citd -v 4 1>/dev/null 2>&1 & - ./citd -v 4 1>/dev/null 2>&1 &
# libiscsi test # libiscsi test
- mkdir ${HOME}/libiscsi - mkdir ${HOME}/libiscsi
@@ -33,11 +34,11 @@ script:
- ./autogen.sh - ./autogen.sh
- ./configure - ./configure
- make - make
- ./test-tool/iscsi-test-cu -d --test=SCSI.Read10.Simple iscsi://127.0.0.1:3260/test-iscsi-target/0 - ./test-tool/iscsi-test-cu -d --test=SCSI.Read10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
- ./test-tool/iscsi-test-cu -d --test=SCSI.Write10.Simple iscsi://127.0.0.1:3260/test-iscsi-target/0 - ./test-tool/iscsi-test-cu -d --test=SCSI.Write10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
- ./utils/iscsi-ls -s iscsi://127.0.0.1:3260/test-iscsi-target - ./utils/iscsi-ls -s iscsi://127.0.0.1:3260/${TARGET}
- ./utils/iscsi-inq iscsi://127.0.0.1:3260/test-iscsi-target/0 - ./utils/iscsi-inq iscsi://127.0.0.1:3260/${TARGET}/0
- ./utils/iscsi-readcapacity16 iscsi://127.0.0.1:3260/test-iscsi-target/0 - ./utils/iscsi-readcapacity16 iscsi://127.0.0.1:3260/${TARGET}/0
# iscsi initiator test # iscsi initiator test
- sudo iscsiadm -m discovery -t sendtargets -p 127.0.0.1 - sudo iscsiadm -m discovery -t sendtargets -p 127.0.0.1
- sudo iscsiadm -m node -L all - sudo iscsiadm -m node -L all
+12 -4
View File
@@ -64,18 +64,26 @@ Help Options:
os.Exit(1) os.Exit(1)
} }
scsi := scsi.NewSCSITargetService() err = scsi.InitSCSILUMap(config)
t, err := port.NewTargetService(*flDriver, scsi) if err != nil {
glog.Error(err)
os.Exit(1)
}
service := scsi.NewSCSITargetService()
t, err := port.NewTargetService(*flDriver, service)
if err != nil { if err != nil {
glog.Error(err) glog.Error(err)
os.Exit(1) os.Exit(1)
} }
iscsit := reflect.ValueOf(t) iscsit := reflect.ValueOf(t)
// create a new target // create a new target
for _, tgt := range config.Targets { for tgtname, tgt := range config.Targets {
create := iscsit.MethodByName("NewTarget") create := iscsit.MethodByName("NewTarget")
create.Call([]reflect.Value{reflect.ValueOf(tgt.Name), reflect.ValueOf(tgt.LUNs)}) create.Call([]reflect.Value{reflect.ValueOf(tgtname),
reflect.ValueOf(tgt.Portals)})
} }
runtime.GOMAXPROCS(runtime.NumCPU()) runtime.GOMAXPROCS(runtime.NumCPU())
// run a service // run a service
run := iscsit.MethodByName("Run") run := iscsit.MethodByName("Run")
+14 -15
View File
@@ -18,7 +18,6 @@ package api
import ( import (
"bytes" "bytes"
"errors" "errors"
"os"
) )
type SCSICommandType byte type SCSICommandType byte
@@ -193,7 +192,7 @@ type SCSITarget struct {
TID int `json:"tid"` TID int `json:"tid"`
LID int `json:"lid"` LID int `json:"lid"`
State SCSITargetState `json:"state"` State SCSITargetState `json:"state"`
Devices []*SCSILu `json:"-"` Devices LUNMap `json:"-"`
ITNexus []*ITNexus `json:"itnexus"` ITNexus []*ITNexus `json:"itnexus"`
SCSITargetDriver interface{} `json:"-"` SCSITargetDriver interface{} `json:"-"`
@@ -314,10 +313,11 @@ var (
type CommandFunc func(host int, cmd *SCSICommand) SAMStat type CommandFunc func(host int, cmd *SCSICommand) SAMStat
type BackingStore interface { type BackingStore interface {
Open(dev *SCSILu, path string) (*os.File, error) Open(dev *SCSILu, path string) error
Close(dev *SCSILu) error Close(dev *SCSILu) error
Init(dev *SCSILu, Opts string) error Init(dev *SCSILu, Opts string) error
Exit(dev *SCSILu) error Exit(dev *SCSILu) error
Size(dev *SCSILu) uint64
CommandSubmit(cmd *SCSICommand) error CommandSubmit(cmd *SCSICommand) error
} }
@@ -336,21 +336,20 @@ type ModePage struct {
} }
type SCSILu struct { type SCSILu struct {
File *os.File Address uint64
Address uint64 Size uint64
Size uint64 Lun uint64
Lun uint64 Path string
Path string BsoFlags int
BsoFlags int BlockShift uint
BlockShift uint ReserveID uint64
ReserveID uint64 Attrs SCSILuPhyAttribute
Attrs SCSILuPhyAttribute ModePages []ModePage
ModePages []ModePage
Target *SCSITarget
Storage BackingStore Storage BackingStore
DeviceProtocol SCSIDeviceProtocol DeviceProtocol SCSIDeviceProtocol
PerformCommand CommandFunc PerformCommand CommandFunc
FinishCommand func(*SCSITarget, *SCSICommand) FinishCommand func(*SCSITarget, *SCSICommand)
} }
type LUNMap map[uint64]*SCSILu
+68 -7
View File
@@ -25,6 +25,58 @@ import (
"github.com/gostor/gotgt/pkg/homedir" "github.com/gostor/gotgt/pkg/homedir"
) )
/*
Format of configuration file
{
"storages": [
{
"deviceID": integer, uniqu device id,
"path": string, <protocal>:<absolute/file/path>",
"online": bool, online/offline
},
],
"targets": {
<target name >: {
"portals": [
<IP Addresswith Port(assumed port as 3260 without port information>
],
"luns": {
<lu number for the target>: <mappingd with the device ID>
}
}
}
}
Example of the configuration file
{
"storages": [
{
"deviceID": 1000,
"path": "file:/tmp/image",
"online": true
},
{
"deviceID": 2000,
"path": "ceph:/rbd/image",
"online": true
}
],
"targets": {
"iqn.2016-09.com.gotgt.gostor:example_tgt_0": {
"portals": [
"192.168.1.1"
],
"luns": {
"1": 1000
"2": 2000
}
}
}
}
*/
const ( const (
// ConfigFileName is the name of config file // ConfigFileName is the name of config file
ConfigFileName = "config.json" ConfigFileName = "config.json"
@@ -32,17 +84,23 @@ const (
var ( var (
configDir = os.Getenv("GOSTOR_CONFIG") configDir = os.Getenv("GOSTOR_CONFIG")
config *Config
) )
type BackendStorage struct {
DeviceID uint64 `json:"deviceID"`
Path string `json:"path"`
Online bool `json:"online"`
}
type Target struct { type Target struct {
Name string `json:"name"` Portals []string `json:"portals"`
Portals []string `json:"portals"` LUNs map[string]uint64 `json:"luns"`
LUNs []string `json:"luns"`
} }
type Config struct { type Config struct {
Storage string `json:"storage"` Storages []BackendStorage `json:"storages"`
Targets map[string]Target `json:"targets"` Targets map[string]Target `json:"targets"`
} }
func init() { func init() {
@@ -54,6 +112,10 @@ func init() {
// ConfigDir returns the directory the configuration file is stored in // ConfigDir returns the directory the configuration file is stored in
func ConfigDir() string { func ConfigDir() string {
return configDir return configDir
}
func GetConfig() *Config {
return config
} }
// Load reads the configuration files in the given directory and return values. // Load reads the configuration files in the given directory and return values.
@@ -63,8 +125,7 @@ func Load(configDir string) (*Config, error) {
} }
filename := filepath.Join(configDir, ConfigFileName) filename := filepath.Join(configDir, ConfigFileName)
config := &Config{ config = &Config{
Storage: "file",
Targets: make(map[string]Target), Targets: make(map[string]Target),
} }
+29 -26
View File
@@ -22,7 +22,6 @@ import (
"net" "net"
"os" "os"
"runtime/debug" "runtime/debug"
"strings"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
@@ -35,7 +34,6 @@ type ISCSITargetService struct {
SCSI *scsi.SCSITargetService SCSI *scsi.SCSITargetService
Name string Name string
Targets map[string]*ISCSITarget Targets map[string]*ISCSITarget
Portals map[string]struct{}
} }
func init() { func init() {
@@ -46,40 +44,44 @@ func NewISCSITargetService(base *scsi.SCSITargetService) (port.SCSITargetService
return &ISCSITargetService{ return &ISCSITargetService{
Name: "iscsi", Name: "iscsi",
Targets: map[string]*ISCSITarget{}, Targets: map[string]*ISCSITarget{},
Portals: map[string]struct{}{},
SCSI: base, SCSI: base,
}, nil }, nil
} }
func (s *ISCSITargetService) NewTarget(target string, luns []string) (port.SCSITargetDriver, error) { func (s *ISCSITargetService) NewTarget(target string, portals []string) (port.SCSITargetDriver, error) {
if _, ok := s.Targets[target]; ok { if _, ok := s.Targets[target]; ok {
return nil, fmt.Errorf("target name has been existed") return nil, fmt.Errorf("target name has been existed")
} }
stgt, err := s.SCSI.NewSCSITarget(len(s.Targets), "iscsi", target, luns) stgt, err := s.SCSI.NewSCSITarget(len(s.Targets), "iscsi", target)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tgt := newISCSITarget(stgt) tgt := newISCSITarget(stgt)
s.Targets[target] = tgt s.Targets[target] = tgt
for _, portal := range portals {
s.AddNewPortal(target, portal)
}
return tgt, nil return tgt, nil
} }
func (s *ISCSITargetService) AddNewPortal(portals []string) error { func (s *ISCSITargetService) AddNewPortal(tgtName string, portal string) error {
for _, p := range portals { target := s.Targets[tgtName]
if !strings.Contains(p, ":") { tgtPortals := target.Portals
p = p + ":3260" _, ok := tgtPortals[portal]
} if !ok {
s.Portals[p] = struct{}{} tgtPortals[portal] = struct{}{}
} }
return nil return nil
} }
func (s *ISCSITargetService) HasPortal(portal string) bool { func (s *ISCSITargetService) HasPortal(tgtName string, portal string) bool {
if len(s.Portals) == 0 { target := s.Targets[tgtName]
tgtPortals := target.Portals
if len(tgtPortals) == 0 {
return true return true
} }
_, ok := s.Portals[portal] _, ok := tgtPortals[portal]
return ok return ok
} }
@@ -98,10 +100,7 @@ func (s *ISCSITargetService) Run() error {
glog.Error(err) glog.Error(err)
continue continue
} }
if !s.HasPortal(conn.LocalAddr().String()) { glog.Info(conn.LocalAddr().String())
glog.Errorf("unexpected portal")
continue
}
glog.Info("Accepting ...") glog.Info("Accepting ...")
iscsiConn := &iscsiConnection{conn: conn} iscsiConn := &iscsiConnection{conn: conn}
iscsiConn.init() iscsiConn.init()
@@ -363,14 +362,18 @@ func (s *ISCSITargetService) iscsiExecText(conn *iscsiConnection) error {
keys := util.ParseKVText(cmd.RawData) keys := util.ParseKVText(cmd.RawData)
if st, ok := keys["SendTargets"]; ok { if st, ok := keys["SendTargets"]; ok {
if st == "All" { if st == "All" {
list, err := s.SCSI.GetTargetList()
if err != nil { for name, tgt := range s.Targets {
return err glog.V(2).Infof("iscsi target:", name)
glog.V(2).Infof("iscsi target portals:", tgt.Portals)
} }
for _, t := range list {
result = append(result, util.KeyValue{"TargetName", t.Name}) for name, tgt := range s.Targets {
//result = append(result, util.KeyValue{"TargetAddress", "172.16.69.1:3260,1"}) result = append(result, util.KeyValue{"TargetName", name})
result = append(result, util.KeyValue{"TargetAddress", "127.0.0.1:3260,1"}) for portal := range tgt.Portals {
result = append(result, util.KeyValue{"TargetAddress", portal + ",1"})
}
} }
} }
} }
+2 -1
View File
@@ -67,7 +67,7 @@ type ISCSIRedirectInfo struct {
type ISCSITarget struct { type ISCSITarget struct {
api.SCSITarget api.SCSITarget
api.SCSITargetDriverCommon api.SCSITargetDriverCommon
Portals map[string]struct{}
Sessions []*ISCSISession Sessions []*ISCSISession
SessionParam []ISCSISessionParam SessionParam []ISCSISessionParam
Alias string Alias string
@@ -81,6 +81,7 @@ type ISCSITarget struct {
func newISCSITarget(target *api.SCSITarget) *ISCSITarget { func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
return &ISCSITarget{ return &ISCSITarget{
SCSITarget: *target, SCSITarget: *target,
Portals: make(map[string]struct{}),
} }
} }
+1 -1
View File
@@ -25,7 +25,7 @@ import (
type SCSITargetService interface { type SCSITargetService interface {
Run() error Run() error
NewTarget(string, []string) (SCSITargetDriver, error) NewTarget(string, []string) (SCSITargetDriver, error)
AddNewPortal([]string) error AddNewPortal(string, string) error
} }
type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error) type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error)
+3 -4
View File
@@ -18,14 +18,13 @@ package scsi
import ( import (
"fmt" "fmt"
"os"
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
) )
type BaseBackingStore struct { type BaseBackingStore struct {
Name string Name string
DataSize int DataSize uint64
OflagsSupported int OflagsSupported int
} }
@@ -52,8 +51,8 @@ type fakeBackingStore struct {
BaseBackingStore BaseBackingStore
} }
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) { func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) error {
return nil, nil return nil
} }
func (fake *fakeBackingStore) Close(dev *api.SCSILu) error { func (fake *fakeBackingStore) Close(dev *api.SCSILu) error {
+26 -16
View File
@@ -34,6 +34,7 @@ func init() {
type FileBackingStore struct { type FileBackingStore struct {
scsi.BaseBackingStore scsi.BaseBackingStore
File *os.File
} }
func new() (api.BackingStore, error) { func new() (api.BackingStore, error) {
@@ -46,16 +47,22 @@ func new() (api.BackingStore, error) {
}, nil }, nil
} }
func (bs *FileBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) { func (bs *FileBackingStore) Open(dev *api.SCSILu, path string) error {
f, err := os.OpenFile(path, os.O_RDWR, os.ModePerm)
if err != nil { if finfo, err := os.Stat(path); err != nil {
return nil, err return err
} else {
bs.DataSize = uint64(finfo.Size())
} }
return f, nil
f, err := os.OpenFile(path, os.O_RDWR, os.ModePerm)
bs.File = f
return err
} }
func (bs *FileBackingStore) Close(dev *api.SCSILu) error { func (bs *FileBackingStore) Close(dev *api.SCSILu) error {
return dev.File.Close() return bs.File.Close()
} }
func (bs *FileBackingStore) Init(dev *api.SCSILu, Opts string) error { func (bs *FileBackingStore) Init(dev *api.SCSILu, Opts string) error {
@@ -66,6 +73,9 @@ func (bs *FileBackingStore) Exit(dev *api.SCSILu) error {
return nil return nil
} }
func (bs *FileBackingStore) Size(dev *api.SCSILu) uint64 {
return bs.DataSize
}
func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) { func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
var ( var (
scb = cmd.SCB.Bytes() scb = cmd.SCB.Bytes()
@@ -83,7 +93,7 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
switch opcode { switch opcode {
case api.ORWRITE_16: case api.ORWRITE_16:
tmpbuf := []byte{} tmpbuf := []byte{}
length, err = lu.File.ReadAt(tmpbuf, int64(offset)) length, err = bs.File.ReadAt(tmpbuf, int64(offset))
if length != len(tmpbuf) { if length != len(tmpbuf) {
key = scsi.MEDIUM_ERROR key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR asc = scsi.ASC_READ_ERROR
@@ -99,7 +109,7 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
doWrite = true doWrite = true
goto write goto write
case api.SYNCHRONIZE_CACHE, api.SYNCHRONIZE_CACHE_16: case api.SYNCHRONIZE_CACHE, api.SYNCHRONIZE_CACHE_16:
if err = util.Fdatasync(lu.File); err != nil { if err = util.Fdatasync(bs.File); err != nil {
panic(err) panic(err)
} }
break break
@@ -113,7 +123,7 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
// TODO // TODO
break break
case api.READ_6, api.READ_10, api.READ_12, api.READ_16: case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
length, err = lu.File.ReadAt(rbuf, int64(offset)) length, err = bs.File.ReadAt(rbuf, int64(offset))
if err != nil && err != io.EOF { if err != nil && err != io.EOF {
key = scsi.MEDIUM_ERROR key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR asc = scsi.ASC_READ_ERROR
@@ -124,11 +134,11 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
} }
if (opcode != api.READ_6) && (scb[1]&0x10 != 0) { if (opcode != api.READ_6) && (scb[1]&0x10 != 0) {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE) util.Fadvise(bs.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
} }
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(rbuf) cmd.InSDBBuffer.Buffer = bytes.NewBuffer(rbuf)
case api.PRE_FETCH_10, api.PRE_FETCH_16: case api.PRE_FETCH_10, api.PRE_FETCH_16:
err = util.Fadvise(lu.File, int64(offset), int64(cmd.TL), util.POSIX_FADV_WILLNEED) err = util.Fadvise(bs.File, int64(offset), int64(cmd.TL), util.POSIX_FADV_WILLNEED)
if err != nil { if err != nil {
key = scsi.MEDIUM_ERROR key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR asc = scsi.ASC_READ_ERROR
@@ -144,7 +154,7 @@ func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
write: write:
if doWrite { if doWrite {
// hack: wbuf = []byte("hello world!") // hack: wbuf = []byte("hello world!")
length, err = lu.File.WriteAt(wbuf, int64(offset)) length, err = bs.File.WriteAt(wbuf, int64(offset))
if err != nil || length != len(wbuf) { if err != nil || length != len(wbuf) {
glog.Error(err) glog.Error(err)
key = scsi.MEDIUM_ERROR key = scsi.MEDIUM_ERROR
@@ -165,7 +175,7 @@ write:
goto sense goto sense
} }
if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) { if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) {
if err = util.Fdatasync(lu.File); err != nil { if err = util.Fdatasync(bs.File); err != nil {
key = scsi.MEDIUM_ERROR key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR asc = scsi.ASC_READ_ERROR
goto sense goto sense
@@ -173,12 +183,12 @@ write:
} }
if (opcode != api.WRITE_6) && (scb[1]&0x10 != 0) { if (opcode != api.WRITE_6) && (scb[1]&0x10 != 0) {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE) util.Fadvise(bs.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
} }
} }
verify: verify:
if doVerify { if doVerify {
length, err = lu.File.ReadAt(rbuf, int64(offset)) length, err = bs.File.ReadAt(rbuf, int64(offset))
if length != len(rbuf) { if length != len(rbuf) {
key = scsi.MEDIUM_ERROR key = scsi.MEDIUM_ERROR
asc = scsi.ASC_READ_ERROR asc = scsi.ASC_READ_ERROR
@@ -191,7 +201,7 @@ verify:
goto sense goto sense
} }
if scb[1]&0x10 != 0 { if scb[1]&0x10 != 0 {
util.Fadvise(lu.File, int64(offset), int64(length), util.POSIX_FADV_WILLNEED) util.Fadvise(bs.File, int64(offset), int64(length), util.POSIX_FADV_WILLNEED)
} }
} }
glog.Infof("io done %s", string(scb)) glog.Infof("io done %s", string(scb))
+6 -4
View File
@@ -17,8 +17,6 @@ limitations under the License.
package backingstore package backingstore
import ( import (
"os"
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/scsi" "github.com/gostor/gotgt/pkg/scsi"
) )
@@ -41,8 +39,8 @@ func newNull() (api.BackingStore, error) {
}, nil }, nil
} }
func (bs *NullBackingStore) Open(dev *api.SCSILu, path string) (*os.File, error) { func (bs *NullBackingStore) Open(dev *api.SCSILu, path string) error {
return nil, nil return nil
} }
func (bs *NullBackingStore) Close(dev *api.SCSILu) error { func (bs *NullBackingStore) Close(dev *api.SCSILu) error {
@@ -57,6 +55,10 @@ func (bs *NullBackingStore) Exit(dev *api.SCSILu) error {
return nil return nil
} }
func (bs *NullBackingStore) Size(dev *api.SCSILu) uint64 {
return 0
}
func (bs *NullBackingStore) CommandSubmit(cmd *api.SCSICommand) error { func (bs *NullBackingStore) CommandSubmit(cmd *api.SCSICommand) error {
cmd.Result = api.SAM_STAT_GOOD cmd.Result = api.SAM_STAT_GOOD
return nil return nil
+21 -13
View File
@@ -17,36 +17,44 @@ limitations under the License.
package scsi package scsi
import ( import (
"os" "errors"
"strings"
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
) )
func NewSCSILu(lun uint64, target *api.SCSITarget, file string) (*api.SCSILu, error) { /*
* path format <protocol>:/absolute/file/path
*/
func NewSCSILu(device_uuid uint64, path string) (*api.SCSILu, error) {
pathinfo := strings.SplitN(path, ":", 2)
if len(pathinfo) < 2 {
return nil, errors.New("invalid device path string")
}
backendType := pathinfo[0]
backendPath := pathinfo[1]
sbc := NewSBCDevice() sbc := NewSBCDevice()
backing, err := NewBackingStore("file") backing, err := NewBackingStore(backendType)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var lu = &api.SCSILu{ var lu = &api.SCSILu{
Lun: lun, Lun: 0,
Target: target,
PerformCommand: luPerformCommand, PerformCommand: luPerformCommand,
DeviceProtocol: sbc, DeviceProtocol: sbc,
Storage: backing, Storage: backing,
BlockShift: api.DefaultBlockShift, BlockShift: api.DefaultBlockShift,
} }
// hack this
if finfo, err := os.Stat(file); err != nil { err = backing.Open(lu, backendPath)
return nil, err
} else {
lu.Size = uint64(finfo.Size())
}
f, err := backing.Open(lu, file)
if err != nil { if err != nil {
return nil, err return nil, err
} }
lu.File = f lu.Size = backing.Size(lu)
lu.DeviceProtocol.InitLu(lu) lu.DeviceProtocol.InitLu(lu)
lu.Attrs.Online = true lu.Attrs.Online = true
lu.Attrs.Lbppbe = 3 lu.Attrs.Lbppbe = 3
+6 -3
View File
@@ -45,7 +45,6 @@ func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} {
} }
func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error { func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
var tgt = lu.Target
// init LU's phy attribute // init LU's phy attribute
lu.Attrs.DeviceType = api.TYPE_DISK lu.Attrs.DeviceType = api.TYPE_DISK
lu.Attrs.Qualifier = false lu.Attrs.Qualifier = false
@@ -55,8 +54,12 @@ func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
lu.Attrs.SWP = false lu.Attrs.SWP = false
lu.Attrs.SenseFormat = false lu.Attrs.SenseFormat = false
lu.Attrs.VendorID = "GOSTOR" lu.Attrs.VendorID = "GOSTOR"
lu.Attrs.SCSIID = fmt.Sprintf("GOSTOR %x%d", tgt.TID, lu.Lun) /*
lu.Attrs.SCSISN = fmt.Sprintf("beaf%d%d", tgt.TID, lu.Lun) lu.Attrs.SCSIID = fmt.Sprintf("GOSTOR %x%d", tgt.TID, lu.Lun)
lu.Attrs.SCSISN = fmt.Sprintf("beaf%d%d", tgt.TID, lu.Lun)
*/
lu.Attrs.SCSIID = fmt.Sprintf("GOSTOR%d", lu.Lun)
lu.Attrs.SCSISN = fmt.Sprintf("beaf%d", lu.Lun)
lu.Attrs.ProductID = "VIRTUAL-DISK" lu.Attrs.ProductID = "VIRTUAL-DISK"
lu.Attrs.VersionDesction = []uint16{ lu.Attrs.VersionDesction = []uint16{
0x04C0, // SBC-3 no version claimed 0x04C0, // SBC-3 no version claimed
+4
View File
@@ -75,7 +75,11 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro
} }
scmd.ITNexus = itn scmd.ITNexus = itn
/*
* TODO: scmd.Device = target.Devices[util.GetUnalignedUint64(scmd.Lun[:])]
*/
scmd.Device = target.Devices[0] scmd.Device = target.Devices[0]
glog.V(2).Infof("scsi opcode: 0x%x, LUN: %d:", int(scmd.SCB.Bytes()[0]), binary.LittleEndian.Uint64(scmd.Lun[:]))
result := scmd.Device.PerformCommand(tid, scmd) result := scmd.Device.PerformCommand(tid, scmd)
scmd.Result = result.Stat scmd.Result = result.Stat
if result.Err != nil { if result.Err != nil {
+89
View File
@@ -0,0 +1,89 @@
/*
Copyright 2015 The GoStor Authors All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package scsi
import (
"errors"
"strconv"
"sync"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/config"
)
type BackendType string
type SCSILUMap struct {
mutex sync.RWMutex
AllDevices api.LUNMap /* use UUID as the key for all LUs*/
TargetsLUNMap map[string]api.LUNMap /* use target name as the key for target's LUN map*/
}
var globalSCSILUMap = SCSILUMap{AllDevices: make(api.LUNMap), TargetsLUNMap: make(map[string]api.LUNMap)}
func mappingLUN(deviceID uint64, lun uint64, target string) {
device := globalSCSILUMap.AllDevices[deviceID]
lunMap := globalSCSILUMap.TargetsLUNMap[target]
if lunMap == nil {
globalSCSILUMap.TargetsLUNMap[target] = make(api.LUNMap)
lunMap = globalSCSILUMap.TargetsLUNMap[target]
}
lunMap[lun] = device
}
func GetLU(tgtName string, LUN uint64) *api.SCSILu {
globalSCSILUMap.mutex.RLock()
defer globalSCSILUMap.mutex.RUnlock()
lunMap := globalSCSILUMap.TargetsLUNMap[tgtName]
lun := lunMap[LUN]
return lun
}
func GetTargetLUNMap(tgtName string) api.LUNMap {
globalSCSILUMap.mutex.RLock()
defer globalSCSILUMap.mutex.RUnlock()
lunMap := globalSCSILUMap.TargetsLUNMap[tgtName]
return lunMap
}
func InitSCSILUMap(config *config.Config) error {
globalSCSILUMap.mutex.Lock()
defer globalSCSILUMap.mutex.Unlock()
for _, bs := range config.Storages {
lu, err := NewSCSILu(bs.DeviceID, bs.Path)
if err != nil {
return errors.New("Init SCSI LU map error.")
}
globalSCSILUMap.AllDevices[bs.DeviceID] = lu
}
for tgtName, tgt := range config.Targets {
for lunstr, deviceID := range tgt.LUNs {
lun, err := strconv.ParseUint(lunstr, 10, 64)
if err != nil {
return errors.New("LU Number must be a number")
}
mappingLUN(deviceID, lun, tgtName)
}
}
return nil
}
+3 -1
View File
@@ -146,6 +146,7 @@ func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat {
b = (uint8(0) & 0x7) << 5 b = (uint8(0) & 0x7) << 5
b |= uint8(0) & 0x1f b |= uint8(0) & 0x1f
} }
fmt.Println(cmd.Device.Lun, *(*uint64)(unsafe.Pointer(&cmd.Lun)))
if cmd.Device.Lun != *(*uint64)(unsafe.Pointer(&cmd.Lun)) { if cmd.Device.Lun != *(*uint64)(unsafe.Pointer(&cmd.Lun)) {
goto sense goto sense
} }
@@ -235,7 +236,8 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
buf.WriteByte(0x00) buf.WriteByte(0x00)
} }
for _, lu := range cmd.Target.Devices { for lunumber, lu := range cmd.Target.Devices {
fmt.Println("LUN:", lunumber)
if remainLength > 0 { if remainLength > 0 {
lun := lu.Lun lun := lu.Lun
if lun > 0xff { if lun > 0xff {
+5 -16
View File
@@ -23,30 +23,19 @@ import (
"github.com/gostor/gotgt/pkg/api" "github.com/gostor/gotgt/pkg/api"
) )
func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string, luns []string) (*api.SCSITarget, error) { func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*api.SCSITarget, error) {
// verify the target ID // verify the target ID
// verify the target's Name // verify the target's Name
// verify the low level driver // verify the low level driver
var target = &api.SCSITarget{ var target = &api.SCSITarget{
Name: name, Name: name,
TID: tid, TID: tid,
Devices: []*api.SCSILu{},
} }
var devices = []*api.SCSILu{}
for i, ln := range luns {
lun, err := NewSCSILu(uint64(i), target, ln)
if err != nil {
glog.Errorf("fail to create LU: %v", err)
return nil, err
}
devices = append(devices, lun)
}
s.mutex.Lock()
target.Devices = devices
s.Targets = append(s.Targets, target) s.Targets = append(s.Targets, target)
s.mutex.Unlock() target.Devices = GetTargetLUNMap(target.Name)
return target, nil return target, nil
} }