iSCSI/SCSI multi port/ALUA support
fix ALUA flag issue fix NNA flag issue fix fixed format sense data builder issue
This commit is contained in:
@@ -272,7 +272,8 @@ func (m *ISCSICommand) dataInBytes() []byte {
|
||||
}
|
||||
buf.WriteByte(b)
|
||||
|
||||
buf.WriteByte(0x00) // 4
|
||||
buf.WriteByte(0x00) // 4
|
||||
|
||||
buf.Write(util.MarshalUint64(uint64(len(m.RawData)))[5:]) // 5-8
|
||||
// Skip through to byte 16
|
||||
for i := 0; i < 8; i++ {
|
||||
|
||||
@@ -61,6 +61,7 @@ type iscsiConnection struct {
|
||||
conn net.Conn
|
||||
initiator string
|
||||
initiatorAlias string
|
||||
tpgt uint16
|
||||
|
||||
rxBuffer []byte
|
||||
txBuffer []byte
|
||||
|
||||
@@ -18,21 +18,24 @@ package iscsit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/config"
|
||||
"github.com/gostor/gotgt/pkg/port"
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
type ISCSITargetService struct {
|
||||
SCSI *scsi.SCSITargetService
|
||||
Name string
|
||||
Targets map[string]*ISCSITarget
|
||||
SCSI *scsi.SCSITargetService
|
||||
Name string
|
||||
iSCSITargets map[string]*ISCSITarget
|
||||
}
|
||||
|
||||
func init() {
|
||||
@@ -41,47 +44,86 @@ func init() {
|
||||
|
||||
func NewISCSITargetService(base *scsi.SCSITargetService) (port.SCSITargetService, error) {
|
||||
return &ISCSITargetService{
|
||||
Name: "iscsi",
|
||||
Targets: map[string]*ISCSITarget{},
|
||||
SCSI: base,
|
||||
Name: "iscsi",
|
||||
iSCSITargets: map[string]*ISCSITarget{},
|
||||
SCSI: base,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) NewTarget(target string, portals []string) (port.SCSITargetDriver, error) {
|
||||
if _, ok := s.Targets[target]; ok {
|
||||
func (s *ISCSITargetService) NewTarget(tgtName string, configInfo *config.Config) (port.SCSITargetDriver, error) {
|
||||
if _, ok := s.iSCSITargets[tgtName]; ok {
|
||||
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 {
|
||||
return nil, err
|
||||
}
|
||||
tgt := newISCSITarget(stgt)
|
||||
s.Targets[target] = tgt
|
||||
for _, portal := range portals {
|
||||
s.AddNewPortal(target, portal)
|
||||
s.iSCSITargets[tgtName] = tgt
|
||||
scsiTPG := tgt.SCSITarget.TargetPortGroups[0]
|
||||
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
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) AddNewPortal(tgtName string, portal string) error {
|
||||
target := s.Targets[tgtName]
|
||||
tgtPortals := target.Portals
|
||||
_, ok := tgtPortals[portal]
|
||||
if !ok {
|
||||
tgtPortals[portal] = struct{}{}
|
||||
func (s *ISCSITargetService) AddiSCSIPortal(tgtName string, tpgt uint16, portal string) error {
|
||||
var (
|
||||
ok bool
|
||||
errMsg string
|
||||
target *ISCSITarget
|
||||
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
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) HasPortal(tgtName string, portal string) bool {
|
||||
target := s.Targets[tgtName]
|
||||
tgtPortals := target.Portals
|
||||
func (s *ISCSITargetService) HasPortal(tgtName string, tpgt uint16, portal string) bool {
|
||||
var (
|
||||
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
|
||||
}
|
||||
_, ok := tgtPortals[portal]
|
||||
return ok
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) Run() error {
|
||||
@@ -254,6 +296,8 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
var (
|
||||
target *ISCSITarget
|
||||
cmd = conn.req
|
||||
TPGT uint16
|
||||
err error
|
||||
)
|
||||
conn.resp = &ISCSICommand{
|
||||
OpCode: OpLoginResp,
|
||||
@@ -298,7 +342,7 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
if conn.sessionType == SESSION_DISCOVERY {
|
||||
conn.tid = 0xffff
|
||||
} else {
|
||||
for _, t := range s.Targets {
|
||||
for _, t := range s.iSCSITargets {
|
||||
if t.SCSITarget.Name == targetName {
|
||||
target = t
|
||||
break
|
||||
@@ -308,7 +352,15 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
conn.state = CONN_STATE_EXIT
|
||||
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
|
||||
|
||||
}
|
||||
switch conn.state {
|
||||
case CONN_STATE_FREE:
|
||||
@@ -361,13 +413,16 @@ func (s *ISCSITargetService) iscsiExecText(conn *iscsiConnection) error {
|
||||
keys := util.ParseKVText(cmd.RawData)
|
||||
if st, ok := keys["SendTargets"]; ok {
|
||||
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 portals: %v", tgt.Portals)
|
||||
//glog.V(2).Infof("iscsi target portals: %v", tgt.Portals)
|
||||
|
||||
result = append(result, util.KeyValue{"TargetName", name})
|
||||
for portal := range tgt.Portals {
|
||||
result = append(result, util.KeyValue{"TargetAddress", portal + ",1"})
|
||||
for _, tpgt := range tgt.TPGTs {
|
||||
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.Lun = cmd.LUN
|
||||
task.scmd.Tag = uint64(cmd.TaskTag)
|
||||
task.scmd.RelTargetPortID = task.conn.tpgt
|
||||
task.state = taskSCSI
|
||||
if task.scmd.OutSDBBuffer.Buffer == nil {
|
||||
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer(cmd.RawData)
|
||||
|
||||
@@ -17,7 +17,13 @@ limitations under the License.
|
||||
// iSCSI Target Driver
|
||||
package iscsit
|
||||
|
||||
import "github.com/gostor/gotgt/pkg/api"
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
)
|
||||
|
||||
const (
|
||||
IOSTATE_FREE = iota
|
||||
@@ -64,10 +70,15 @@ type ISCSIRedirectInfo struct {
|
||||
Callback string
|
||||
}
|
||||
|
||||
type iSCSITPGT struct {
|
||||
TPGT uint16 /* Mapping to SCSI Reltive Target Port ID */
|
||||
Portals map[string]struct{}
|
||||
}
|
||||
|
||||
type ISCSITarget struct {
|
||||
api.SCSITarget
|
||||
api.SCSITargetDriverCommon
|
||||
Portals map[string]struct{}
|
||||
TPGTs map[uint16]*iSCSITPGT
|
||||
Sessions []*ISCSISession
|
||||
SessionParam []ISCSISessionParam
|
||||
Alias string
|
||||
@@ -78,10 +89,22 @@ type ISCSITarget struct {
|
||||
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 {
|
||||
return &ISCSITarget{
|
||||
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
|
||||
)
|
||||
|
||||
for _, t := range s.Targets {
|
||||
for _, t := range s.iSCSITargets {
|
||||
if t.TID == conn.tid {
|
||||
target = t
|
||||
break
|
||||
|
||||
@@ -19,13 +19,13 @@ package port
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/config"
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
)
|
||||
|
||||
type SCSITargetService interface {
|
||||
Run() error
|
||||
NewTarget(string, []string) (SCSITargetDriver, error)
|
||||
AddNewPortal(string, string) error
|
||||
NewTarget(string, *config.Config) (SCSITargetDriver, error)
|
||||
}
|
||||
|
||||
type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error)
|
||||
|
||||
Reference in New Issue
Block a user