Merge pull request #32 from carmark/update_sn

small refactor: combine all package response generation
This commit is contained in:
Lei Xue
2016-12-11 21:27:00 +08:00
committed by GitHub
7 changed files with 137 additions and 185 deletions

1
.gitignore vendored
View File

@@ -44,5 +44,4 @@ _testmain.go
.idea
/gotgt
*.out
examples/*
*.bak

View File

@@ -1,8 +1,22 @@
## gotgt [![Build Status](https://travis-ci.org/gostor/gotgt.svg)](https://travis-ci.org/gostor/gotgt)
Simple Golang SCSI Target framework, this includes only one binary, you can start a daemon via `gotgt daemon` and control it via `gotgt list/create/rm`.
gotgt is a simple SCSI Target framework implemented by golang, built for performance and density..
## Build
### What is SCSI?
Small Computer System Interface (SCSI) is a set of standards for physically connecting and transferring data between computers and peripheral devices. The SCSI standards define commands, protocols, electrical and optical interfaces. SCSI is most commonly used for hard disk drives and tape drives, but it can connect a wide range of other devices, including scanners and CD drives, although not all controllers can handle all devices.
### What is iSCSI?
The iSCSI is an acronym for Internet Small Computer Systems Interface, an Internet Protocol (IP)-based storage networking standard for linking data storage facilities. In a nutshell, it provides block-level access to storage devices over a TCP/IP network.
## Getting started
Currently, the gotgt is under heavy development, so there is no any release binaries so far, you have to build it from source.
There is a only on binary name `gotgt`, you can start a daemon via `gotgt daemon` and control it via `gotgt list/create/rm`.
### Build
You will need to make sure that you have Go installed on your system and the `gotgt` repository is cloned in your $GOPATH.
```
$ mkdir -p $GOPATH/src/github.com/gostor/
@@ -14,60 +28,22 @@ $ ./configure
$ make
```
## How to use
### How to use
Currenty, there is lack of commands to operate the target and LU, however you can init the target/LU with config file in `~/.gotgt/config.json`, such as:
Now, there is lack of commands to operate the target and LU, however you can init the target/LU with config file in `~/.gotgt/config.json`, you may find a example at [here](./examples/config.json).
Please note, if you want use that exmaple, you have to make sure file `/var/tmp/disk.img` is existed.
```
{
"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:02:example-tgt-0":{
"tpgts":{
"1":[0]
},
"luns":{
"0":1000
}
}
}
}
```
> Note: make sure file `/var/tmp/disk.img` is existed, you can use `dd` to create it.
## Test
### Test
You can test this with [open-iscsi](http://www.open-iscsi.com/) or [libiscsi](https://github.com/gostor/libiscsi).
## Performance
TBD
## Roadmap
* Auth (p3)
* Login Process (orzhang, p2)
* ACL (Access control) (p3)
* SCSI Task Management (p3)
* iSCSI Task Management (p3)
* SCSI Command Queue (p2)
* More SCSI commands (p2)
* Refactor (carmark, p1)
* Command Line (carmark, p1)
* More Backstore Plugins(such as `ceph` and `raw device`) (orzhang, p1)
* Redirect iSCSI Target (orzhang, p2)
* Homepage (p3)
* More test cases (p2)
* IDM implementation (p3) https://tools.ietf.org/html/rfc5047
The current roadmap and milestones for alpha and beta completion are in the github issues on this repository. Please refer to these issues for what is being worked on and completed for the various stages of development.
## Contributing

View File

@@ -20,7 +20,6 @@ import (
"fmt"
"os"
"os/signal"
"runtime"
"strings"
"syscall"
@@ -89,7 +88,8 @@ func createDaemon(host, driver, level string) error {
targetDriver.NewTarget(tgtname, config)
}
runtime.GOMAXPROCS(runtime.NumCPU())
// comment this to avoid concurrent issue
// runtime.GOMAXPROCS(runtime.NumCPU())
// run a service
go targetDriver.Run()

25
examples/config.json Normal file
View File

@@ -0,0 +1,25 @@
{
"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:02:example-tgt-0":{
"tpgts":{
"1":[0]
},
"luns":{
"0":1000
}
}
}
}

View File

@@ -22,6 +22,7 @@ import (
"sync"
"github.com/gostor/gotgt/pkg/api"
"github.com/gostor/gotgt/pkg/util"
)
const (
@@ -140,3 +141,70 @@ func (conn *iscsiConnection) ReInstatement(newConn *iscsiConnection) {
conn.close()
conn.conn = newConn.conn
}
func (conn *iscsiConnection) buildRespPackage(oc OpCode) error {
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}}
conn.txIOState = IOSTATE_TX_BHS
conn.statSN += 1
task := conn.rxTask
resp := &ISCSICommand{
StatSN: conn.req.ExpStatSN,
TaskTag: conn.req.TaskTag,
ExpCmdSN: conn.session.ExpCmdSN,
MaxCmdSN: conn.session.ExpCmdSN + conn.session.MaxQueueCommand,
}
switch oc {
case OpReady:
resp.OpCode = OpReady
resp.R2TSN = task.r2tSN
resp.BufferOffset = uint32(task.offset)
resp.DesiredLength = uint32(task.r2tCount)
if val := conn.loginParam.sessionParam[ISCSI_PARAM_MAX_BURST].Value; task.r2tCount > int(val) {
resp.DesiredLength = uint32(val)
}
case OpSCSIIn, OpSCSIResp:
resp.OpCode = oc
resp.Immediate = true
resp.Final = true
resp.SCSIResponse = 0x00
resp.HasStatus = true
scmd := task.scmd
resp.Status = scmd.Result
if scmd.Direction == api.SCSIDataRead || scmd.Direction == api.SCSIDataWrite {
if scmd.InSDBBuffer.Buffer != nil {
buf := scmd.InSDBBuffer.Buffer.Bytes()
resp.RawData = buf
} else {
resp.RawData = []byte{}
}
}
if scmd.Result != 0 && scmd.SenseBuffer != nil {
resp.RawData = scmd.SenseBuffer.Bytes()
}
case OpNoopIn, OpSCSITaskResp, OpReject:
resp.OpCode = oc
resp.Final = true
resp.NSG = FullFeaturePhase
resp.ExpCmdSN = conn.req.CmdSN + 1
case OpLoginResp:
resp.OpCode = OpLoginResp
resp.Transit = conn.loginParam.tgtTrans
resp.CSG = conn.req.CSG
resp.NSG = conn.loginParam.tgtNSG
resp.ExpCmdSN = conn.req.CmdSN
resp.MaxCmdSN = conn.req.CmdSN
negoKeys, err := conn.processLoginData()
if err != nil {
return err
}
if !conn.loginParam.keyDeclared {
negoKeys = loginKVDeclare(conn, negoKeys)
conn.loginParam.keyDeclared = true
}
resp.RawData = util.MarshalKVText(negoKeys)
conn.txTask = nil
}
conn.resp = resp
return nil
}

View File

@@ -320,11 +320,7 @@ func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) {
}
func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error {
var (
cmd = conn.req
err error
negoKeys []util.KeyValue
)
var cmd = conn.req
conn.cid = cmd.ConnID
conn.loginParam.iniCSG = cmd.CSG
@@ -334,23 +330,16 @@ func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error {
conn.loginParam.isid = cmd.ISID
conn.loginParam.tsih = cmd.TSIH
conn.expCmdSN = cmd.CmdSN
conn.statSN += 1
if conn.loginParam.iniCSG == SecurityNegotiation {
conn.state = CONN_STATE_EXIT
return fmt.Errorf("Doesn't support Auth")
}
pairs := util.ParseKVText(cmd.RawData)
negoKeys, err = loginKVProcess(conn, pairs)
_, err := conn.processLoginData()
if err != nil {
return err
}
if !conn.loginParam.keyDeclared {
negoKeys = loginKVDeclare(conn, negoKeys)
conn.loginParam.keyDeclared = true
}
if !conn.loginParam.paramInit {
err = s.BindISCSISession(conn)
@@ -367,19 +356,7 @@ func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error {
conn.state = CONN_STATE_LOGIN
}
conn.resp = &ISCSICommand{
OpCode: OpLoginResp,
Transit: conn.loginParam.tgtTrans,
CSG: cmd.CSG,
NSG: conn.loginParam.tgtNSG,
StatSN: cmd.ExpStatSN,
TaskTag: cmd.TaskTag,
ExpCmdSN: cmd.CmdSN,
MaxCmdSN: cmd.CmdSN,
RawData: util.MarshalKVText(negoKeys),
}
return nil
return conn.buildRespPackage(OpLoginResp)
}
func iscsiExecLogout(conn *iscsiConnection) error {
@@ -394,7 +371,7 @@ func iscsiExecLogout(conn *iscsiConnection) error {
conn.resp.MaxCmdSN = cmd.CmdSN
} else {
conn.resp.ExpCmdSN = conn.session.ExpCmdSN
conn.resp.MaxCmdSN = conn.session.ExpCmdSN + 10
conn.resp.MaxCmdSN = conn.session.ExpCmdSN + conn.session.MaxQueueCommand
}
return nil
}
@@ -434,62 +411,19 @@ func (s *ISCSITargetDriver) iscsiExecText(conn *iscsiConnection) error {
}
func iscsiExecNoopOut(conn *iscsiConnection) error {
cmd := conn.req
conn.resp = &ISCSICommand{
OpCode: OpNoopIn,
Final: true,
NSG: FullFeaturePhase,
StatSN: cmd.ExpStatSN,
TaskTag: cmd.TaskTag,
ExpCmdSN: cmd.CmdSN + 1,
MaxCmdSN: cmd.CmdSN + 10,
}
return nil
return conn.buildRespPackage(OpNoopIn)
}
func iscsiExecTMFunction(conn *iscsiConnection) error {
cmd := conn.req
conn.resp = &ISCSICommand{
OpCode: OpSCSITaskResp,
Final: true,
NSG: FullFeaturePhase,
StatSN: cmd.ExpStatSN,
TaskTag: cmd.TaskTag,
ExpCmdSN: cmd.CmdSN + 1,
MaxCmdSN: cmd.CmdSN + 10,
}
return nil
return conn.buildRespPackage(OpSCSITaskResp)
}
func iscsiExecReject(conn *iscsiConnection) error {
conn.resp = &ISCSICommand{
OpCode: OpReject,
}
return nil
return conn.buildRespPackage(OpReject)
}
func iscsiExecR2T(conn *iscsiConnection) error {
var val uint
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}}
conn.txIOState = IOSTATE_TX_BHS
conn.statSN += 1
task := conn.rxTask
resp := &ISCSICommand{
OpCode: OpReady,
StatSN: conn.req.ExpStatSN,
TaskTag: conn.req.TaskTag,
ExpCmdSN: conn.session.ExpCmdSN,
MaxCmdSN: conn.session.ExpCmdSN + 10,
R2TSN: task.r2tSN,
BufferOffset: uint32(task.offset),
DesiredLength: uint32(task.r2tCount),
}
if val = conn.loginParam.sessionParam[ISCSI_PARAM_MAX_BURST].Value; task.r2tCount > int(val) {
resp.DesiredLength = uint32(val)
}
conn.resp = resp
return nil
return conn.buildRespPackage(OpReady)
}
func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) {
@@ -625,46 +559,12 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error
if err = s.iscsiTaskQueueHandler(task); err != nil {
return
} else {
if scmd.Direction == api.SCSIDataRead {
conn.buildRespPackage(OpSCSIIn)
} else {
conn.buildRespPackage(OpSCSIResp)
}
conn.rxTask = nil
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}}
conn.txIOState = IOSTATE_TX_BHS
conn.statSN += 1
resp := &ISCSICommand{
Immediate: true,
Final: true,
StatSN: req.ExpStatSN,
TaskTag: req.TaskTag,
ExpCmdSN: conn.session.ExpCmdSN,
MaxCmdSN: conn.session.ExpCmdSN + 10,
Status: scmd.Result,
SCSIResponse: 0x00,
HasStatus: true,
}
switch scmd.Direction {
case api.SCSIDataRead:
resp.OpCode = OpSCSIIn
if scmd.InSDBBuffer.Buffer != nil {
buf := scmd.InSDBBuffer.Buffer.Bytes()
resp.RawData = buf
} else {
resp.RawData = []byte{}
}
case api.SCSIDataWrite:
resp.OpCode = OpSCSIResp
if scmd.InSDBBuffer.Buffer != nil {
buf := scmd.InSDBBuffer.Buffer.Bytes()
resp.RawData = buf
} else {
resp.RawData = []byte{}
}
case api.SCSIDataBidirection:
case api.SCSIDataNone:
resp.OpCode = OpSCSIResp
}
if scmd.Result != 0 && scmd.SenseBuffer != nil {
resp.RawData = scmd.SenseBuffer.Bytes()
}
conn.resp = resp
}
case OpSCSITaskReq:
// task management function
@@ -712,27 +612,10 @@ func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error
if err = s.iscsiExecTask(task); err != nil {
return
} else {
conn.buildRespPackage(OpSCSIResp)
conn.rxTask = nil
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: &api.SCSICommand{}, state: taskSCSI}
conn.txIOState = IOSTATE_TX_BHS
conn.statSN += 1
resp := &ISCSICommand{
OpCode: OpSCSIResp,
Immediate: true,
Final: true,
StatSN: req.ExpStatSN,
TaskTag: req.TaskTag,
ExpCmdSN: conn.session.ExpCmdSN,
MaxCmdSN: conn.session.ExpCmdSN + 10,
Status: task.scmd.Result,
SCSIResponse: 0x00,
HasStatus: true,
}
conn.resp = resp
}
case OpNoopOut:
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag}
conn.txIOState = IOSTATE_TX_BHS
iscsiExecNoopOut(conn)
case OpLogoutReq:
conn.txTask = &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag}

View File

@@ -54,7 +54,7 @@ func loginKVDeclare(conn *iscsiConnection, negoKV []util.KeyValue) []util.KeyVal
return negoKV
}
func loginKVProcess(conn *iscsiConnection, loginKV map[string]string) ([]util.KeyValue, error) {
func (conn *iscsiConnection) processLoginData() ([]util.KeyValue, error) {
var (
uintVal uint
ok bool
@@ -62,6 +62,7 @@ func loginKVProcess(conn *iscsiConnection, loginKV map[string]string) ([]util.Ke
negoKV []util.KeyValue
kvChanges int
)
loginKV := util.ParseKVText(conn.req.RawData)
for key, val := range loginKV {
// The MaxRecvDataSegmentLength of initiator