mapping lun and fix portal management
This commit is contained in:
+9
-8
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
@@ -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"})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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)
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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))
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user