Merge pull request #21 from orzhang/multi-port
iSCSI/SCSI multi port/ALUA support
This commit is contained in:
@@ -26,7 +26,7 @@ script:
|
|||||||
- go test -v ./pkg/...
|
- go test -v ./pkg/...
|
||||||
- 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 '{"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
|
- echo '{"storages":[{"deviceID":1000,"path":"file:/var/tmp/disk.img","online":true}],"iscsiportals":[{"id":0,"portal":"127.0.0.1:3260"}],"iscsitargets":{"iqn.2016-09.com.gotgt.gostor:example_tgt_0":{"tpgts":{"1":[0]},"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
|
||||||
|
|||||||
4
citd.go
4
citd.go
@@ -76,8 +76,8 @@ Help Options:
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
for tgtname, tgt := range config.Targets {
|
for tgtname := range config.ISCSITargets {
|
||||||
targetDriver.NewTarget(tgtname, tgt.Portals)
|
targetDriver.NewTarget(tgtname, config)
|
||||||
}
|
}
|
||||||
|
|
||||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||||
|
|||||||
@@ -149,13 +149,14 @@ type SCSIDataBuffer struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SCSICommand struct {
|
type SCSICommand struct {
|
||||||
Target *SCSITarget
|
Target *SCSITarget
|
||||||
DeviceID uint64
|
DeviceID uint64
|
||||||
Device *SCSILu
|
Device *SCSILu
|
||||||
State uint64
|
State uint64
|
||||||
Direction SCSIDataDirection
|
Direction SCSIDataDirection
|
||||||
InSDBBuffer SCSIDataBuffer
|
InSDBBuffer SCSIDataBuffer
|
||||||
OutSDBBuffer SCSIDataBuffer
|
OutSDBBuffer SCSIDataBuffer
|
||||||
|
RelTargetPortID uint16
|
||||||
// Command ITN ID
|
// Command ITN ID
|
||||||
CommandITNID uint64
|
CommandITNID uint64
|
||||||
Offset uint64
|
Offset uint64
|
||||||
@@ -187,16 +188,26 @@ type ITNexusLuInfo struct {
|
|||||||
Prevent int
|
Prevent int
|
||||||
}
|
}
|
||||||
|
|
||||||
type SCSITarget struct {
|
type SCSITargetPort struct {
|
||||||
Name string `json:"name"`
|
RelativeTargetPortID uint16
|
||||||
TID int `json:"tid"`
|
TargetPortName string
|
||||||
LID int `json:"lid"`
|
}
|
||||||
State SCSITargetState `json:"state"`
|
|
||||||
Devices LUNMap `json:"-"`
|
|
||||||
LUN0 *SCSILu `json:"-"`
|
|
||||||
ITNexus []*ITNexus `json:"itnexus"`
|
|
||||||
|
|
||||||
SCSITargetDriver interface{} `json:"-"`
|
type TargetPortGroup struct {
|
||||||
|
GroupID uint16
|
||||||
|
TargetPortGroup []*SCSITargetPort `json:"targetportgroup"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCSITarget struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
TID int `json:"tid"`
|
||||||
|
LID int `json:"lid"`
|
||||||
|
State SCSITargetState `json:"state"`
|
||||||
|
Devices LUNMap `json:"-"`
|
||||||
|
LUN0 *SCSILu `json:"-"`
|
||||||
|
ITNexus []*ITNexus `json:"itnexus"`
|
||||||
|
TargetPortGroups []*TargetPortGroup `json:"tpg"`
|
||||||
|
SCSITargetDriver interface{} `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SCSITargetDriverState int
|
type SCSITargetDriverState int
|
||||||
|
|||||||
@@ -34,13 +34,23 @@ Format of configuration file
|
|||||||
"deviceID": integer, uniqu device id,
|
"deviceID": integer, uniqu device id,
|
||||||
"path": string, <protocal>:<absolute/file/path>",
|
"path": string, <protocal>:<absolute/file/path>",
|
||||||
"online": bool, online/offline
|
"online": bool, online/offline
|
||||||
},
|
}
|
||||||
],
|
],
|
||||||
|
|
||||||
|
"portals": [
|
||||||
|
{
|
||||||
|
"id": integer, uniqu portal id
|
||||||
|
"portal":string, <IP>:<PORT>
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
"targets": {
|
"targets": {
|
||||||
<target name >: {
|
<target name >: {
|
||||||
"portals": [
|
"tpgts":{
|
||||||
<IP Addresswith Port(assumed port as 3260 without port information>
|
<tpgt number>: [<portal id[,portal id....]]
|
||||||
],
|
}
|
||||||
|
//n shoud be an value from 0 ~ 65535
|
||||||
|
|
||||||
"luns": {
|
"luns": {
|
||||||
<lu number for the target>: <mappingd with the device ID>
|
<lu number for the target>: <mappingd with the device ID>
|
||||||
}
|
}
|
||||||
@@ -54,27 +64,29 @@ Example of the configuration file
|
|||||||
"storages": [
|
"storages": [
|
||||||
{
|
{
|
||||||
"deviceID": 1000,
|
"deviceID": 1000,
|
||||||
"path": "file:/tmp/image",
|
"path": "file:/tmp/disk.img",
|
||||||
"online": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"deviceID": 2000,
|
|
||||||
"path": "ceph:/rbd/image",
|
|
||||||
"online": true
|
"online": true
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"targets": {
|
"iscsiportals":[
|
||||||
|
{
|
||||||
|
"id":0,
|
||||||
|
"portal":"192.168.159.1:3260"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"iscsitargets": {
|
||||||
"iqn.2016-09.com.gotgt.gostor:example_tgt_0": {
|
"iqn.2016-09.com.gotgt.gostor:example_tgt_0": {
|
||||||
"portals": [
|
"tpgts": {
|
||||||
"192.168.1.1"
|
"1":[0]
|
||||||
],
|
}
|
||||||
|
,
|
||||||
"luns": {
|
"luns": {
|
||||||
"1": 1000
|
"1": 1000
|
||||||
"2": 2000
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@@ -93,14 +105,20 @@ type BackendStorage struct {
|
|||||||
Online bool `json:"online"`
|
Online bool `json:"online"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Target struct {
|
type ISCSIPortalInfo struct {
|
||||||
Portals []string `json:"portals"`
|
ID uint16 `json:"id"`
|
||||||
LUNs map[string]uint64 `json:"luns"`
|
Portal string `json:"portal"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ISCSITarget struct {
|
||||||
|
TPGTs map[string][]uint64 `json:"tpgts"`
|
||||||
|
LUNs map[string]uint64 `json:"luns"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Storages []BackendStorage `json:"storages"`
|
Storages []BackendStorage `json:"storages"`
|
||||||
Targets map[string]Target `json:"targets"`
|
ISCSIPortals []ISCSIPortalInfo `json:"iscsiportals"`
|
||||||
|
ISCSITargets map[string]ISCSITarget `json:"iscsitargets"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -126,7 +144,7 @@ func Load(configDir string) (*Config, error) {
|
|||||||
|
|
||||||
filename := filepath.Join(configDir, ConfigFileName)
|
filename := filepath.Join(configDir, ConfigFileName)
|
||||||
config = &Config{
|
config = &Config{
|
||||||
Targets: make(map[string]Target),
|
ISCSITargets: make(map[string]ISCSITarget),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try happy path first - latest config file
|
// Try happy path first - latest config file
|
||||||
|
|||||||
@@ -272,7 +272,8 @@ func (m *ISCSICommand) dataInBytes() []byte {
|
|||||||
}
|
}
|
||||||
buf.WriteByte(b)
|
buf.WriteByte(b)
|
||||||
|
|
||||||
buf.WriteByte(0x00) // 4
|
buf.WriteByte(0x00) // 4
|
||||||
|
|
||||||
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||||
// Skip through to byte 16
|
// Skip through to byte 16
|
||||||
for i := 0; i < 8; i++ {
|
for i := 0; i < 8; i++ {
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ type iscsiConnection struct {
|
|||||||
conn net.Conn
|
conn net.Conn
|
||||||
initiator string
|
initiator string
|
||||||
initiatorAlias string
|
initiatorAlias string
|
||||||
|
tpgt uint16
|
||||||
|
|
||||||
rxBuffer []byte
|
rxBuffer []byte
|
||||||
txBuffer []byte
|
txBuffer []byte
|
||||||
|
|||||||
@@ -18,21 +18,24 @@ package iscsit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
"github.com/gostor/gotgt/pkg/config"
|
||||||
"github.com/gostor/gotgt/pkg/port"
|
"github.com/gostor/gotgt/pkg/port"
|
||||||
"github.com/gostor/gotgt/pkg/scsi"
|
"github.com/gostor/gotgt/pkg/scsi"
|
||||||
"github.com/gostor/gotgt/pkg/util"
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ISCSITargetService struct {
|
type ISCSITargetService struct {
|
||||||
SCSI *scsi.SCSITargetService
|
SCSI *scsi.SCSITargetService
|
||||||
Name string
|
Name string
|
||||||
Targets map[string]*ISCSITarget
|
iSCSITargets map[string]*ISCSITarget
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -41,47 +44,86 @@ func init() {
|
|||||||
|
|
||||||
func NewISCSITargetService(base *scsi.SCSITargetService) (port.SCSITargetService, error) {
|
func NewISCSITargetService(base *scsi.SCSITargetService) (port.SCSITargetService, error) {
|
||||||
return &ISCSITargetService{
|
return &ISCSITargetService{
|
||||||
Name: "iscsi",
|
Name: "iscsi",
|
||||||
Targets: map[string]*ISCSITarget{},
|
iSCSITargets: map[string]*ISCSITarget{},
|
||||||
SCSI: base,
|
SCSI: base,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ISCSITargetService) NewTarget(target string, portals []string) (port.SCSITargetDriver, error) {
|
func (s *ISCSITargetService) NewTarget(tgtName string, configInfo *config.Config) (port.SCSITargetDriver, error) {
|
||||||
if _, ok := s.Targets[target]; ok {
|
if _, ok := s.iSCSITargets[tgtName]; 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)
|
stgt, err := s.SCSI.NewSCSITarget(len(s.iSCSITargets), "iscsi", tgtName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tgt := newISCSITarget(stgt)
|
tgt := newISCSITarget(stgt)
|
||||||
s.Targets[target] = tgt
|
s.iSCSITargets[tgtName] = tgt
|
||||||
for _, portal := range portals {
|
scsiTPG := tgt.SCSITarget.TargetPortGroups[0]
|
||||||
s.AddNewPortal(target, portal)
|
targetConfig := configInfo.ISCSITargets[tgtName]
|
||||||
|
for tpgt, portalIDArrary := range targetConfig.TPGTs {
|
||||||
|
tpgtNumber, _ := strconv.ParseUint(tpgt, 10, 16)
|
||||||
|
tgt.TPGTs[uint16(tpgtNumber)] = &iSCSITPGT{uint16(tpgtNumber), make(map[string]struct{})}
|
||||||
|
targetPortName := fmt.Sprintf("%s,t,0x%02x", tgtName, tpgtNumber)
|
||||||
|
scsiTPG.TargetPortGroup = append(scsiTPG.TargetPortGroup, &api.SCSITargetPort{uint16(tpgtNumber), targetPortName})
|
||||||
|
for _, portalID := range portalIDArrary {
|
||||||
|
portal := configInfo.ISCSIPortals[portalID]
|
||||||
|
s.AddiSCSIPortal(tgtName, uint16(tpgtNumber), portal.Portal)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return tgt, nil
|
return tgt, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ISCSITargetService) AddNewPortal(tgtName string, portal string) error {
|
func (s *ISCSITargetService) AddiSCSIPortal(tgtName string, tpgt uint16, portal string) error {
|
||||||
target := s.Targets[tgtName]
|
var (
|
||||||
tgtPortals := target.Portals
|
ok bool
|
||||||
_, ok := tgtPortals[portal]
|
errMsg string
|
||||||
if !ok {
|
target *ISCSITarget
|
||||||
tgtPortals[portal] = struct{}{}
|
tpgtInfo *iSCSITPGT
|
||||||
|
)
|
||||||
|
|
||||||
|
if target, ok = s.iSCSITargets[tgtName]; !ok {
|
||||||
|
errMsg = fmt.Sprintf("no target %s", tgtName)
|
||||||
|
return errors.New(errMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tpgtInfo, ok = target.TPGTs[tpgt]; !ok {
|
||||||
|
errMsg = fmt.Sprintf("no tpgt %d", tpgt)
|
||||||
|
return errors.New(errMsg)
|
||||||
|
}
|
||||||
|
tgtPortals := tpgtInfo.Portals
|
||||||
|
|
||||||
|
if _, ok = tgtPortals[portal]; !ok {
|
||||||
|
tgtPortals[portal] = struct{}{}
|
||||||
|
} else {
|
||||||
|
errMsg := fmt.Sprintf("duplicate portal %s,in %s,%d", portal, tgtName, tpgt)
|
||||||
|
return errors.New(errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ISCSITargetService) HasPortal(tgtName string, portal string) bool {
|
func (s *ISCSITargetService) HasPortal(tgtName string, tpgt uint16, portal string) bool {
|
||||||
target := s.Targets[tgtName]
|
var (
|
||||||
tgtPortals := target.Portals
|
ok bool
|
||||||
|
target *ISCSITarget
|
||||||
|
tpgtInfo *iSCSITPGT
|
||||||
|
)
|
||||||
|
|
||||||
if len(tgtPortals) == 0 {
|
if target, ok = s.iSCSITargets[tgtName]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if tpgtInfo, ok = target.TPGTs[tpgt]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
tgtPortals := tpgtInfo.Portals
|
||||||
|
|
||||||
|
if _, ok = tgtPortals[portal]; !ok {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
_, ok := tgtPortals[portal]
|
|
||||||
return ok
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *ISCSITargetService) Run() error {
|
func (s *ISCSITargetService) Run() error {
|
||||||
@@ -254,6 +296,8 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
|||||||
var (
|
var (
|
||||||
target *ISCSITarget
|
target *ISCSITarget
|
||||||
cmd = conn.req
|
cmd = conn.req
|
||||||
|
TPGT uint16
|
||||||
|
err error
|
||||||
)
|
)
|
||||||
conn.resp = &ISCSICommand{
|
conn.resp = &ISCSICommand{
|
||||||
OpCode: OpLoginResp,
|
OpCode: OpLoginResp,
|
||||||
@@ -298,7 +342,7 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
|||||||
if conn.sessionType == SESSION_DISCOVERY {
|
if conn.sessionType == SESSION_DISCOVERY {
|
||||||
conn.tid = 0xffff
|
conn.tid = 0xffff
|
||||||
} else {
|
} else {
|
||||||
for _, t := range s.Targets {
|
for _, t := range s.iSCSITargets {
|
||||||
if t.SCSITarget.Name == targetName {
|
if t.SCSITarget.Name == targetName {
|
||||||
target = t
|
target = t
|
||||||
break
|
break
|
||||||
@@ -308,7 +352,15 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
|||||||
conn.state = CONN_STATE_EXIT
|
conn.state = CONN_STATE_EXIT
|
||||||
return fmt.Errorf("No target found with name(%s)", targetName)
|
return fmt.Errorf("No target found with name(%s)", targetName)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TPGT, err = target.FindTPG(conn.conn.LocalAddr().String())
|
||||||
|
if err != nil {
|
||||||
|
conn.state = CONN_STATE_EXIT
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
conn.tpgt = TPGT
|
||||||
conn.tid = target.TID
|
conn.tid = target.TID
|
||||||
|
|
||||||
}
|
}
|
||||||
switch conn.state {
|
switch conn.state {
|
||||||
case CONN_STATE_FREE:
|
case CONN_STATE_FREE:
|
||||||
@@ -361,13 +413,16 @@ 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" {
|
||||||
for name, tgt := range s.Targets {
|
for name, tgt := range s.iSCSITargets {
|
||||||
glog.V(2).Infof("iscsi target: %v", name)
|
glog.V(2).Infof("iscsi target: %v", name)
|
||||||
glog.V(2).Infof("iscsi target portals: %v", tgt.Portals)
|
//glog.V(2).Infof("iscsi target portals: %v", tgt.Portals)
|
||||||
|
|
||||||
result = append(result, util.KeyValue{"TargetName", name})
|
result = append(result, util.KeyValue{"TargetName", name})
|
||||||
for portal := range tgt.Portals {
|
for _, tpgt := range tgt.TPGTs {
|
||||||
result = append(result, util.KeyValue{"TargetAddress", portal + ",1"})
|
for portal := range tpgt.Portals {
|
||||||
|
targetPort := fmt.Sprintf("%s,%d", portal, tpgt.TPGT)
|
||||||
|
result = append(result, util.KeyValue{"TargetAddress", targetPort})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -754,6 +809,7 @@ func (s *ISCSITargetService) iscsiExecTask(task *iscsiTask) error {
|
|||||||
task.scmd.SCBLength = len(cmd.CDB)
|
task.scmd.SCBLength = len(cmd.CDB)
|
||||||
task.scmd.Lun = cmd.LUN
|
task.scmd.Lun = cmd.LUN
|
||||||
task.scmd.Tag = uint64(cmd.TaskTag)
|
task.scmd.Tag = uint64(cmd.TaskTag)
|
||||||
|
task.scmd.RelTargetPortID = task.conn.tpgt
|
||||||
task.state = taskSCSI
|
task.state = taskSCSI
|
||||||
if task.scmd.OutSDBBuffer.Buffer == nil {
|
if task.scmd.OutSDBBuffer.Buffer == nil {
|
||||||
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer(cmd.RawData)
|
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer(cmd.RawData)
|
||||||
|
|||||||
@@ -17,7 +17,13 @@ limitations under the License.
|
|||||||
// iSCSI Target Driver
|
// iSCSI Target Driver
|
||||||
package iscsit
|
package iscsit
|
||||||
|
|
||||||
import "github.com/gostor/gotgt/pkg/api"
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
IOSTATE_FREE = iota
|
IOSTATE_FREE = iota
|
||||||
@@ -64,10 +70,15 @@ type ISCSIRedirectInfo struct {
|
|||||||
Callback string
|
Callback string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type iSCSITPGT struct {
|
||||||
|
TPGT uint16 /* Mapping to SCSI Reltive Target Port ID */
|
||||||
|
Portals map[string]struct{}
|
||||||
|
}
|
||||||
|
|
||||||
type ISCSITarget struct {
|
type ISCSITarget struct {
|
||||||
api.SCSITarget
|
api.SCSITarget
|
||||||
api.SCSITargetDriverCommon
|
api.SCSITargetDriverCommon
|
||||||
Portals map[string]struct{}
|
TPGTs map[uint16]*iSCSITPGT
|
||||||
Sessions []*ISCSISession
|
Sessions []*ISCSISession
|
||||||
SessionParam []ISCSISessionParam
|
SessionParam []ISCSISessionParam
|
||||||
Alias string
|
Alias string
|
||||||
@@ -78,10 +89,22 @@ type ISCSITarget struct {
|
|||||||
NopCount int
|
NopCount int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
||||||
|
for tpgt, TPG := range tgt.TPGTs {
|
||||||
|
for tgtPortal := range TPG.Portals {
|
||||||
|
if strings.EqualFold(portal, tgtPortal) {
|
||||||
|
return tpgt, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
errMsg := fmt.Sprintf("No TPGT found with IP(%s)", portal)
|
||||||
|
return 0, errors.New(errMsg)
|
||||||
|
}
|
||||||
|
|
||||||
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{}),
|
TPGTs: make(map[uint16]*iSCSITPGT),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -221,7 +221,7 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
|||||||
tsih uint64
|
tsih uint64
|
||||||
)
|
)
|
||||||
|
|
||||||
for _, t := range s.Targets {
|
for _, t := range s.iSCSITargets {
|
||||||
if t.TID == conn.tid {
|
if t.TID == conn.tid {
|
||||||
target = t
|
target = t
|
||||||
break
|
break
|
||||||
|
|||||||
@@ -19,13 +19,13 @@ package port
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/gostor/gotgt/pkg/config"
|
||||||
"github.com/gostor/gotgt/pkg/scsi"
|
"github.com/gostor/gotgt/pkg/scsi"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SCSITargetService interface {
|
type SCSITargetService interface {
|
||||||
Run() error
|
Run() error
|
||||||
NewTarget(string, []string) (SCSITargetDriver, error)
|
NewTarget(string, *config.Config) (SCSITargetDriver, error)
|
||||||
AddNewPortal(string, string) error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error)
|
type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error)
|
||||||
|
|||||||
@@ -16,6 +16,10 @@ limitations under the License.
|
|||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
|
)
|
||||||
|
|
||||||
type SCSIPRServiceAction byte
|
type SCSIPRServiceAction byte
|
||||||
type SCSIPRType byte
|
type SCSIPRType byte
|
||||||
|
|
||||||
@@ -73,3 +77,39 @@ const (
|
|||||||
func SCSICDBGroupID(opcode byte) byte {
|
func SCSICDBGroupID(opcode byte) byte {
|
||||||
return ((opcode >> 5) & 0x7)
|
return ((opcode >> 5) & 0x7)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Transfer Length (if any)
|
||||||
|
* Parameter List Length (if any)
|
||||||
|
* Allocation Length (if any)
|
||||||
|
*/
|
||||||
|
func SCSICDBBufXLength(scb []byte) (int64, bool) {
|
||||||
|
var (
|
||||||
|
opcode byte
|
||||||
|
length int64
|
||||||
|
group byte
|
||||||
|
ok bool = true
|
||||||
|
)
|
||||||
|
opcode = scb[0]
|
||||||
|
group = SCSICDBGroupID(opcode)
|
||||||
|
|
||||||
|
switch group {
|
||||||
|
case CBD_GROUPID_0:
|
||||||
|
length = int64(scb[4])
|
||||||
|
case CBD_GROUPID_1, CBD_GROUPID_2:
|
||||||
|
length = int64(util.GetUnalignedUint16(scb[7:9]))
|
||||||
|
case CBD_GROUPID_3:
|
||||||
|
if opcode == 0x7F {
|
||||||
|
length = int64(scb[7])
|
||||||
|
} else {
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
case CBD_GROUPID_4:
|
||||||
|
length = int64(util.GetUnalignedUint32(scb[6:10]))
|
||||||
|
case CBD_GROUPID_5:
|
||||||
|
length = int64(util.GetUnalignedUint32(scb[10:14]))
|
||||||
|
default:
|
||||||
|
ok = false
|
||||||
|
}
|
||||||
|
return length, ok
|
||||||
|
}
|
||||||
|
|||||||
@@ -158,8 +158,8 @@ func NewSBCDevice(deviceType api.SCSIDeviceType) api.SCSIDeviceProtocol {
|
|||||||
|
|
||||||
sbc.SCSIDeviceOps[api.MODE_SELECT_10] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
|
sbc.SCSIDeviceOps[api.MODE_SELECT_10] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
|
||||||
sbc.SCSIDeviceOps[api.MODE_SENSE_10] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_WE_FN|PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[api.MODE_SENSE_10] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_WE_FN|PR_EA_FA|PR_EA_FN)
|
||||||
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)
|
||||||
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)
|
||||||
|
|
||||||
sbc.SCSIDeviceOps[api.READ_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[api.READ_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||||
sbc.SCSIDeviceOps[api.WRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
|
sbc.SCSIDeviceOps[api.WRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ func NewSCSIDeviceOperation(fn api.CommandFunc, sa *SCSIServiceAction, pr uint8)
|
|||||||
|
|
||||||
func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
||||||
senseBuffer := &bytes.Buffer{}
|
senseBuffer := &bytes.Buffer{}
|
||||||
|
inBufLen, ok := SCSICDBBufXLength(cmd.SCB.Bytes())
|
||||||
|
|
||||||
if cmd.Device.Attrs.SenseFormat {
|
if cmd.Device.Attrs.SenseFormat {
|
||||||
// descriptor format
|
// descriptor format
|
||||||
@@ -149,8 +150,19 @@ func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
|||||||
}
|
}
|
||||||
senseBuffer.WriteByte((byte(asc) >> 8) & 0xff)
|
senseBuffer.WriteByte((byte(asc) >> 8) & 0xff)
|
||||||
senseBuffer.WriteByte(byte(asc) & 0xff)
|
senseBuffer.WriteByte(byte(asc) & 0xff)
|
||||||
|
senseBuffer.WriteByte(0x00)
|
||||||
|
senseBuffer.WriteByte(0x00)
|
||||||
|
senseBuffer.WriteByte(0x00)
|
||||||
|
senseBuffer.WriteByte(0x00)
|
||||||
cmd.SenseLength = length + 8
|
cmd.SenseLength = length + 8
|
||||||
}
|
}
|
||||||
|
if ok {
|
||||||
|
if int64(len(senseBuffer.Bytes())) > inBufLen {
|
||||||
|
senseBuffer.Truncate(int(inBufLen))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
glog.V(2).Infof("cannot calc cbd alloc length. truncate failed")
|
||||||
|
}
|
||||||
cmd.Result = key
|
cmd.Result = key
|
||||||
cmd.SenseBuffer = senseBuffer
|
cmd.SenseBuffer = senseBuffer
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ func InitSCSILUMap(config *config.Config) error {
|
|||||||
globalSCSILUMap.AllDevices[bs.DeviceID] = lu
|
globalSCSILUMap.AllDevices[bs.DeviceID] = lu
|
||||||
}
|
}
|
||||||
|
|
||||||
for tgtName, tgt := range config.Targets {
|
for tgtName, tgt := range config.ISCSITargets {
|
||||||
for lunstr, deviceID := range tgt.LUNs {
|
for lunstr, deviceID := range tgt.LUNs {
|
||||||
lun, err := strconv.ParseUint(lunstr, 10, 64)
|
lun, err := strconv.ParseUint(lunstr, 10, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -122,8 +122,8 @@ const (
|
|||||||
INQUIRY_SCCS = byte(0x80)
|
INQUIRY_SCCS = byte(0x80)
|
||||||
INQUIRY_AAC = byte(0x40)
|
INQUIRY_AAC = byte(0x40)
|
||||||
INQUIRY_TPGS_NO = byte(0x00)
|
INQUIRY_TPGS_NO = byte(0x00)
|
||||||
INQUIRY_TPGS_IMPLICIT = byte(0x20)
|
INQUIRY_TPGS_IMPLICIT = byte(0x10)
|
||||||
INQUIRY_TPGS_EXPLICIT = byte(0x10)
|
INQUIRY_TPGS_EXPLICIT = byte(0x20)
|
||||||
INQUIRY_TPGS_BOTH = byte(0x30)
|
INQUIRY_TPGS_BOTH = byte(0x30)
|
||||||
INQUIRY_3PC = byte(0x08)
|
INQUIRY_3PC = byte(0x08)
|
||||||
INQUIRY_Reserved = byte(0x06)
|
INQUIRY_Reserved = byte(0x06)
|
||||||
@@ -189,11 +189,12 @@ const (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
SCSI_VendorID = "GOSTOR"
|
SCSI_VendorID = "GOSTOR"
|
||||||
SCSI_ProductID = "GOTGT-VDISK"
|
SCSI_ProductID = "GOTGT"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SPCIllegalOp(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCIllegalOp(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
return api.SAMStatGood
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCLuOffline(lu *api.SCSILu) error {
|
func SPCLuOffline(lu *api.SCSILu) error {
|
||||||
@@ -282,12 +283,16 @@ func InquiryPage0x83(host int, cmd *api.SCSICommand) (*bytes.Buffer, uint16) {
|
|||||||
buf = &bytes.Buffer{}
|
buf = &bytes.Buffer{}
|
||||||
descBuf = &bytes.Buffer{}
|
descBuf = &bytes.Buffer{}
|
||||||
data []byte = []byte{}
|
data []byte = []byte{}
|
||||||
pageLength uint16 = 0
|
portName []byte
|
||||||
|
pageLength uint16 = 0
|
||||||
|
portID uint16 = cmd.RelTargetPortID
|
||||||
|
portGroup uint16 = FindTargetGroup(cmd.Target, portID)
|
||||||
|
targetPort *api.SCSITargetPort = FindTargetPort(cmd.Target, portID)
|
||||||
)
|
)
|
||||||
|
|
||||||
//DESCRIPTOR 1 TARGET NAME
|
//DESCRIPTOR 1 TARGET NAME
|
||||||
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_ASCII)
|
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_ASCII)
|
||||||
descBuf.WriteByte(0x80 | ASS_TGT_PORT | DESG_VENDOR)
|
descBuf.WriteByte(0x80 | (ASS_TGT_PORT << 4) | DESG_VENDOR)
|
||||||
descBuf.WriteByte(0x00)
|
descBuf.WriteByte(0x00)
|
||||||
//length
|
//length
|
||||||
descBuf.WriteByte(byte(len([]byte(cmd.Target.Name))))
|
descBuf.WriteByte(byte(len([]byte(cmd.Target.Name))))
|
||||||
@@ -296,7 +301,7 @@ func InquiryPage0x83(host int, cmd *api.SCSICommand) (*bytes.Buffer, uint16) {
|
|||||||
|
|
||||||
//DESCRIPTOR 2 NNA Locally
|
//DESCRIPTOR 2 NNA Locally
|
||||||
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_BIN)
|
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_BIN)
|
||||||
descBuf.WriteByte(0x80 | ASS_TGT_PORT | DESG_NAA)
|
descBuf.WriteByte(0x80 | (ASS_LU << 4) | DESG_NAA)
|
||||||
descBuf.WriteByte(0x00)
|
descBuf.WriteByte(0x00)
|
||||||
//length
|
//length
|
||||||
descBuf.WriteByte(0x08)
|
descBuf.WriteByte(0x08)
|
||||||
@@ -305,25 +310,37 @@ func InquiryPage0x83(host int, cmd *api.SCSICommand) (*bytes.Buffer, uint16) {
|
|||||||
|
|
||||||
//TODO: Target Port Group(0x05), Relative Target port identifier(0x04)
|
//TODO: Target Port Group(0x05), Relative Target port identifier(0x04)
|
||||||
|
|
||||||
/*
|
//DESCRIPTOR 3 TPG
|
||||||
//DESCRIPTOR 3 TPG
|
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_BIN)
|
||||||
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_BIN)
|
descBuf.WriteByte(0x80 | (ASS_TGT_PORT << 4) | DESG_TGT_PORT_GRP)
|
||||||
descBuf.WriteByte(0x80 | ASS_TGT_PORT | DESG_REL_TGT_PORT)
|
descBuf.WriteByte(0x00)
|
||||||
descBuf.WriteByte(0x00)
|
//length
|
||||||
//length
|
descBuf.WriteByte(0x04)
|
||||||
descBuf.WriteByte(0x08)
|
//TPG
|
||||||
//TPG
|
descBuf.WriteByte(0x00)
|
||||||
binary.Write(descBuf, binary.BigEndian,)
|
descBuf.WriteByte(0x00)
|
||||||
|
binary.Write(descBuf, binary.BigEndian, portGroup)
|
||||||
|
|
||||||
//DESCRIPTOR 4 RTPI
|
//DESCRIPTOR 4 Relative Target Port ID
|
||||||
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_BIN)
|
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_BIN)
|
||||||
descBuf.WriteByte(0x80 | ASS_TGT_PORT | DESG_NAA)
|
descBuf.WriteByte(0x80 | (ASS_TGT_PORT << 4) | DESG_REL_TGT_PORT)
|
||||||
descBuf.WriteByte(0x00)
|
descBuf.WriteByte(0x00)
|
||||||
//length
|
//length
|
||||||
descBuf.WriteByte(0x08)
|
descBuf.WriteByte(0x04)
|
||||||
//RTPGI
|
//RTPGI
|
||||||
binary.Write(descBuf, binary.BigEndian,)
|
descBuf.WriteByte(0x00)
|
||||||
*/
|
descBuf.WriteByte(0x00)
|
||||||
|
binary.Write(descBuf, binary.BigEndian, portID)
|
||||||
|
|
||||||
|
//DESCRIPTOR 5 SCSI Name,Port
|
||||||
|
portName = util.StringToByte(targetPort.TargetPortName, 4, 256)
|
||||||
|
descBuf.WriteByte((PIV_ISCSI << 4) | INQ_CODE_UTF8)
|
||||||
|
descBuf.WriteByte(0x80 | (ASS_TGT_PORT << 4) | DESG_SCSI)
|
||||||
|
descBuf.WriteByte(0x00)
|
||||||
|
//length
|
||||||
|
descBuf.WriteByte(byte(len(portName)))
|
||||||
|
//RTPGI
|
||||||
|
descBuf.Write(portName)
|
||||||
|
|
||||||
data = descBuf.Bytes()
|
data = descBuf.Bytes()
|
||||||
pageLength = uint16(len(data))
|
pageLength = uint16(len(data))
|
||||||
@@ -395,7 +412,7 @@ func SPCInquiry(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
} else {
|
} else {
|
||||||
//byte 5
|
//byte 5
|
||||||
//SCCS(0) AAC(0) TPGS(0) 3PC(0) PROTECT(0)
|
//SCCS(0) AAC(0) TPGS(0) 3PC(0) PROTECT(0)
|
||||||
addBuf.WriteByte(0x00)
|
addBuf.WriteByte(INQUIRY_TPGS_IMPLICIT)
|
||||||
//byte 6
|
//byte 6
|
||||||
//ENCSERV(0) VS(0) MULTIP(0) ADDR16(0)
|
//ENCSERV(0) VS(0) MULTIP(0) ADDR16(0)
|
||||||
addBuf.WriteByte(0x00)
|
addBuf.WriteByte(0x00)
|
||||||
|
|||||||
@@ -31,15 +31,40 @@ func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*ap
|
|||||||
|
|
||||||
// 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,
|
||||||
|
TargetPortGroups: []*api.TargetPortGroup{},
|
||||||
}
|
}
|
||||||
|
tpg := &api.TargetPortGroup{0, []*api.SCSITargetPort{}}
|
||||||
s.Targets = append(s.Targets, target)
|
s.Targets = append(s.Targets, target)
|
||||||
target.Devices = GetTargetLUNMap(target.Name)
|
target.Devices = GetTargetLUNMap(target.Name)
|
||||||
target.LUN0 = NewLUN0()
|
target.LUN0 = NewLUN0()
|
||||||
|
target.TargetPortGroups = append(target.TargetPortGroups, tpg)
|
||||||
return target, nil
|
return target, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func FindTargetGroup(target *api.SCSITarget, relPortID uint16) uint16 {
|
||||||
|
for _, tpg := range target.TargetPortGroups {
|
||||||
|
for _, port := range tpg.TargetPortGroup {
|
||||||
|
if port.RelativeTargetPortID == relPortID {
|
||||||
|
return tpg.GroupID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func FindTargetPort(target *api.SCSITarget, relPortID uint16) *api.SCSITargetPort {
|
||||||
|
for _, tpg := range target.TargetPortGroups {
|
||||||
|
for _, port := range tpg.TargetPortGroup {
|
||||||
|
if port.RelativeTargetPortID == relPortID {
|
||||||
|
return port
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func deviceReserve(cmd *api.SCSICommand) error {
|
func deviceReserve(cmd *api.SCSICommand) error {
|
||||||
var lu *api.SCSILu
|
var lu *api.SCSILu
|
||||||
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
||||||
|
|||||||
@@ -90,6 +90,28 @@ func MarshalUint64(i uint64) []byte {
|
|||||||
return data
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func StringToByte(str string, align int, maxlength int) []byte {
|
||||||
|
var (
|
||||||
|
data []byte
|
||||||
|
data2 []byte
|
||||||
|
length int
|
||||||
|
d int
|
||||||
|
)
|
||||||
|
|
||||||
|
data = []byte(str)
|
||||||
|
length = len(data)
|
||||||
|
d = align - (length % align)
|
||||||
|
|
||||||
|
if (length + d) > maxlength {
|
||||||
|
data = ([]byte(str))[0:maxlength]
|
||||||
|
return data
|
||||||
|
} else {
|
||||||
|
data2 = make([]byte, length+d)
|
||||||
|
copy(data2, data)
|
||||||
|
return data2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
POSIX_FADV_NORMAL = iota
|
POSIX_FADV_NORMAL = iota
|
||||||
POSIX_FADV_RANDOM
|
POSIX_FADV_RANDOM
|
||||||
|
|||||||
Reference in New Issue
Block a user