From c8d2d89619d9b9116debe8d28aa56e3299b83837 Mon Sep 17 00:00:00 2001 From: Lei Xue Date: Sun, 11 Dec 2016 18:21:21 +0800 Subject: [PATCH 1/4] update README.md to move Roardmap into github issues --- README.md | 76 +++++++++++++++++++------------------------------------ 1 file changed, 26 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index e450b46..1dc39d4 100644 --- a/README.md +++ b/README.md @@ -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 From 42949563ac778f2b9008763126e1814b576c42d3 Mon Sep 17 00:00:00 2001 From: Lei Xue Date: Sun, 11 Dec 2016 18:21:53 +0800 Subject: [PATCH 2/4] small refactor: combine all package response generation --- pkg/port/iscsit/conn.go | 68 ++++++++++++++++++ pkg/port/iscsit/iscsid.go | 145 ++++---------------------------------- pkg/port/iscsit/login.go | 3 +- 3 files changed, 84 insertions(+), 132 deletions(-) diff --git a/pkg/port/iscsit/conn.go b/pkg/port/iscsit/conn.go index 927e8d6..a8aed77 100644 --- a/pkg/port/iscsit/conn.go +++ b/pkg/port/iscsit/conn.go @@ -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 +} diff --git a/pkg/port/iscsit/iscsid.go b/pkg/port/iscsit/iscsid.go index c8fb2ad..6feda45 100644 --- a/pkg/port/iscsit/iscsid.go +++ b/pkg/port/iscsit/iscsid.go @@ -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} diff --git a/pkg/port/iscsit/login.go b/pkg/port/iscsit/login.go index 044c8a8..55564cb 100644 --- a/pkg/port/iscsit/login.go +++ b/pkg/port/iscsit/login.go @@ -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 From c85b0f045fdce75e087cf56f9ab96fc2ada0d976 Mon Sep 17 00:00:00 2001 From: Lei Xue Date: Sun, 11 Dec 2016 20:56:12 +0800 Subject: [PATCH 3/4] comment runtime.GOMAXPROCS to avoid concurrent issue --- cmd/daemon.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/daemon.go b/cmd/daemon.go index 1ea9a2a..e5f0c5d 100644 --- a/cmd/daemon.go +++ b/cmd/daemon.go @@ -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() From 33f78efe0fa13019c31b0f6a8ce3665c0eaeaa60 Mon Sep 17 00:00:00 2001 From: Lei Xue Date: Sun, 11 Dec 2016 21:04:40 +0800 Subject: [PATCH 4/4] add examples directory --- .gitignore | 1 - examples/config.json | 25 +++++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 examples/config.json diff --git a/.gitignore b/.gitignore index b3c13ee..87b28bf 100644 --- a/.gitignore +++ b/.gitignore @@ -44,5 +44,4 @@ _testmain.go .idea /gotgt *.out -examples/* *.bak diff --git a/examples/config.json b/examples/config.json new file mode 100644 index 0000000..2143c2b --- /dev/null +++ b/examples/config.json @@ -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 + } + } + } +}