Merge pull request #29 from carmark/refactor
some doc fix and refactor for the packages
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -42,8 +42,7 @@ _testmain.go
|
||||
|
||||
# intellij idea configure
|
||||
.idea
|
||||
/citadm
|
||||
/citd
|
||||
/gotgt
|
||||
*.out
|
||||
examples/*
|
||||
*.bak
|
||||
|
||||
29
.travis.yml
29
.travis.yml
@@ -27,7 +27,7 @@ script:
|
||||
- dd if=/dev/zero of=/var/tmp/disk.img bs=1024 count=10240
|
||||
- mkdir ${HOME}/.gotgt
|
||||
- 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 &
|
||||
- ./gotgt daemon --log debug 1>/dev/null 2>&1 &
|
||||
# libiscsi test
|
||||
- mkdir ${HOME}/libiscsi
|
||||
- git clone https://github.com/gostor/libiscsi ${HOME}/libiscsi
|
||||
@@ -36,18 +36,21 @@ script:
|
||||
- ./autogen.sh
|
||||
- ./configure
|
||||
- make
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.TestUnitReady iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.ReadCapacity10 iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.ReadCapacity16 iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.Read10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.Write10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.Read16.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.Write16.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.Read32.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.Write32.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.WriteVerify10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.WriteVerify16.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -f --test=SCSI.WriteVerify32.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.TestUnitReady iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.ReadCapacity10 iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.ReadCapacity16 iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Read10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Write10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Read16.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Write16.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Read32.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Write32.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.WriteVerify10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.WriteVerify16.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.WriteVerify32.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Verify10.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Verify12.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./test-tool/iscsi-test-cu -d -A --test=SCSI.Verify16.Simple iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./utils/iscsi-ls -s iscsi://127.0.0.1:3260/${TARGET}
|
||||
- ./utils/iscsi-inq iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
- ./utils/iscsi-readcapacity16 iscsi://127.0.0.1:3260/${TARGET}/0
|
||||
|
||||
4
Godeps/Godeps.json
generated
4
Godeps/Godeps.json
generated
@@ -26,10 +26,6 @@
|
||||
"Comment": "v0.2.0-8-g990a1a1",
|
||||
"Rev": "990a1a1a70b0da4c4cb70e117971a4f0babfbf1a"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/golang/glog",
|
||||
"Rev": "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/gorilla/context",
|
||||
"Rev": "215affda49addc4c8ef7e2534915df2c8c35c6cd"
|
||||
|
||||
191
Godeps/_workspace/src/github.com/golang/glog/LICENSE
generated
vendored
191
Godeps/_workspace/src/github.com/golang/glog/LICENSE
generated
vendored
@@ -1,191 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction, and
|
||||
distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
||||
owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
||||
that control, are controlled by, or are under common control with that entity.
|
||||
For the purposes of this definition, "control" means (i) the power, direct or
|
||||
indirect, to cause the direction or management of such entity, whether by
|
||||
contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity exercising
|
||||
permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications, including
|
||||
but not limited to software source code, documentation source, and configuration
|
||||
files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical transformation or
|
||||
translation of a Source form, including but not limited to compiled object code,
|
||||
generated documentation, and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or Object form, made
|
||||
available under the License, as indicated by a copyright notice that is included
|
||||
in or attached to the work (an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object form, that
|
||||
is based on (or derived from) the Work and for which the editorial revisions,
|
||||
annotations, elaborations, or other modifications represent, as a whole, an
|
||||
original work of authorship. For the purposes of this License, Derivative Works
|
||||
shall not include works that remain separable from, or merely link (or bind by
|
||||
name) to the interfaces of, the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including the original version
|
||||
of the Work and any modifications or additions to that Work or Derivative Works
|
||||
thereof, that is intentionally submitted to Licensor for inclusion in the Work
|
||||
by the copyright owner or by an individual or Legal Entity authorized to submit
|
||||
on behalf of the copyright owner. For the purposes of this definition,
|
||||
"submitted" means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems, and
|
||||
issue tracking systems that are managed by, or on behalf of, the Licensor for
|
||||
the purpose of discussing and improving the Work, but excluding communication
|
||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
||||
owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
||||
of whom a Contribution has been received by Licensor and subsequently
|
||||
incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the Work and such
|
||||
Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License.
|
||||
|
||||
Subject to the terms and conditions of this License, each Contributor hereby
|
||||
grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free,
|
||||
irrevocable (except as stated in this section) patent license to make, have
|
||||
made, use, offer to sell, sell, import, and otherwise transfer the Work, where
|
||||
such license applies only to those patent claims licensable by such Contributor
|
||||
that are necessarily infringed by their Contribution(s) alone or by combination
|
||||
of their Contribution(s) with the Work to which such Contribution(s) was
|
||||
submitted. If You institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work or a
|
||||
Contribution incorporated within the Work constitutes direct or contributory
|
||||
patent infringement, then any patent licenses granted to You under this License
|
||||
for that Work shall terminate as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution.
|
||||
|
||||
You may reproduce and distribute copies of the Work or Derivative Works thereof
|
||||
in any medium, with or without modifications, and in Source or Object form,
|
||||
provided that You meet the following conditions:
|
||||
|
||||
You must give any other recipients of the Work or Derivative Works a copy of
|
||||
this License; and
|
||||
You must cause any modified files to carry prominent notices stating that You
|
||||
changed the files; and
|
||||
You must retain, in the Source form of any Derivative Works that You distribute,
|
||||
all copyright, patent, trademark, and attribution notices from the Source form
|
||||
of the Work, excluding those notices that do not pertain to any part of the
|
||||
Derivative Works; and
|
||||
If the Work includes a "NOTICE" text file as part of its distribution, then any
|
||||
Derivative Works that You distribute must include a readable copy of the
|
||||
attribution notices contained within such NOTICE file, excluding those notices
|
||||
that do not pertain to any part of the Derivative Works, in at least one of the
|
||||
following places: within a NOTICE text file distributed as part of the
|
||||
Derivative Works; within the Source form or documentation, if provided along
|
||||
with the Derivative Works; or, within a display generated by the Derivative
|
||||
Works, if and wherever such third-party notices normally appear. The contents of
|
||||
the NOTICE file are for informational purposes only and do not modify the
|
||||
License. You may add Your own attribution notices within Derivative Works that
|
||||
You distribute, alongside or as an addendum to the NOTICE text from the Work,
|
||||
provided that such additional attribution notices cannot be construed as
|
||||
modifying the License.
|
||||
You may add Your own copyright statement to Your modifications and may provide
|
||||
additional or different license terms and conditions for use, reproduction, or
|
||||
distribution of Your modifications, or for any such Derivative Works as a whole,
|
||||
provided Your use, reproduction, and distribution of the Work otherwise complies
|
||||
with the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions.
|
||||
|
||||
Unless You explicitly state otherwise, any Contribution intentionally submitted
|
||||
for inclusion in the Work by You to the Licensor shall be under the terms and
|
||||
conditions of this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify the terms of
|
||||
any separate license agreement you may have executed with Licensor regarding
|
||||
such Contributions.
|
||||
|
||||
6. Trademarks.
|
||||
|
||||
This License does not grant permission to use the trade names, trademarks,
|
||||
service marks, or product names of the Licensor, except as required for
|
||||
reasonable and customary use in describing the origin of the Work and
|
||||
reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty.
|
||||
|
||||
Unless required by applicable law or agreed to in writing, Licensor provides the
|
||||
Work (and each Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied,
|
||||
including, without limitation, any warranties or conditions of TITLE,
|
||||
NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are
|
||||
solely responsible for determining the appropriateness of using or
|
||||
redistributing the Work and assume any risks associated with Your exercise of
|
||||
permissions under this License.
|
||||
|
||||
8. Limitation of Liability.
|
||||
|
||||
In no event and under no legal theory, whether in tort (including negligence),
|
||||
contract, or otherwise, unless required by applicable law (such as deliberate
|
||||
and grossly negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special, incidental,
|
||||
or consequential damages of any character arising as a result of this License or
|
||||
out of the use or inability to use the Work (including but not limited to
|
||||
damages for loss of goodwill, work stoppage, computer failure or malfunction, or
|
||||
any and all other commercial damages or losses), even if such Contributor has
|
||||
been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability.
|
||||
|
||||
While redistributing the Work or Derivative Works thereof, You may choose to
|
||||
offer, and charge a fee for, acceptance of support, warranty, indemnity, or
|
||||
other liability obligations and/or rights consistent with this License. However,
|
||||
in accepting such obligations, You may act only on Your own behalf and on Your
|
||||
sole responsibility, not on behalf of any other Contributor, and only if You
|
||||
agree to indemnify, defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason of your
|
||||
accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work
|
||||
|
||||
To apply the Apache License to your work, attach the following boilerplate
|
||||
notice, with the fields enclosed by brackets "[]" replaced with your own
|
||||
identifying information. (Don't include the brackets!) The text should be
|
||||
enclosed in the appropriate comment syntax for the file format. We also
|
||||
recommend that a file or class name and description of purpose be included on
|
||||
the same "printed page" as the copyright notice for easier identification within
|
||||
third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
44
Godeps/_workspace/src/github.com/golang/glog/README
generated
vendored
44
Godeps/_workspace/src/github.com/golang/glog/README
generated
vendored
@@ -1,44 +0,0 @@
|
||||
glog
|
||||
====
|
||||
|
||||
Leveled execution logs for Go.
|
||||
|
||||
This is an efficient pure Go implementation of leveled logs in the
|
||||
manner of the open source C++ package
|
||||
https://github.com/google/glog
|
||||
|
||||
By binding methods to booleans it is possible to use the log package
|
||||
without paying the expense of evaluating the arguments to the log.
|
||||
Through the -vmodule flag, the package also provides fine-grained
|
||||
control over logging at the file level.
|
||||
|
||||
The comment from glog.go introduces the ideas:
|
||||
|
||||
Package glog implements logging analogous to the Google-internal
|
||||
C++ INFO/ERROR/V setup. It provides functions Info, Warning,
|
||||
Error, Fatal, plus formatting variants such as Infof. It
|
||||
also provides V-style logging controlled by the -v and
|
||||
-vmodule=file=2 flags.
|
||||
|
||||
Basic examples:
|
||||
|
||||
glog.Info("Prepare to repel boarders")
|
||||
|
||||
glog.Fatalf("Initialization failed: %s", err)
|
||||
|
||||
See the documentation for the V function for an explanation
|
||||
of these examples:
|
||||
|
||||
if glog.V(2) {
|
||||
glog.Info("Starting transaction...")
|
||||
}
|
||||
|
||||
glog.V(2).Infoln("Processed", nItems, "elements")
|
||||
|
||||
|
||||
The repository contains an open source version of the log package
|
||||
used inside Google. The master copy of the source lives inside
|
||||
Google, not here. The code in this repo is for export only and is not itself
|
||||
under development. Feature requests will be ignored.
|
||||
|
||||
Send bug reports to golang-nuts@googlegroups.com.
|
||||
1180
Godeps/_workspace/src/github.com/golang/glog/glog.go
generated
vendored
1180
Godeps/_workspace/src/github.com/golang/glog/glog.go
generated
vendored
File diff suppressed because it is too large
Load Diff
124
Godeps/_workspace/src/github.com/golang/glog/glog_file.go
generated
vendored
124
Godeps/_workspace/src/github.com/golang/glog/glog_file.go
generated
vendored
@@ -1,124 +0,0 @@
|
||||
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
|
||||
//
|
||||
// Copyright 2013 Google Inc. 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.
|
||||
|
||||
// File I/O for logs.
|
||||
|
||||
package glog
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
// MaxSize is the maximum size of a log file in bytes.
|
||||
var MaxSize uint64 = 1024 * 1024 * 1800
|
||||
|
||||
// logDirs lists the candidate directories for new log files.
|
||||
var logDirs []string
|
||||
|
||||
// If non-empty, overrides the choice of directory in which to write logs.
|
||||
// See createLogDirs for the full list of possible destinations.
|
||||
var logDir = flag.String("log_dir", "", "If non-empty, write log files in this directory")
|
||||
|
||||
func createLogDirs() {
|
||||
if *logDir != "" {
|
||||
logDirs = append(logDirs, *logDir)
|
||||
}
|
||||
logDirs = append(logDirs, os.TempDir())
|
||||
}
|
||||
|
||||
var (
|
||||
pid = os.Getpid()
|
||||
program = filepath.Base(os.Args[0])
|
||||
host = "unknownhost"
|
||||
userName = "unknownuser"
|
||||
)
|
||||
|
||||
func init() {
|
||||
h, err := os.Hostname()
|
||||
if err == nil {
|
||||
host = shortHostname(h)
|
||||
}
|
||||
|
||||
current, err := user.Current()
|
||||
if err == nil {
|
||||
userName = current.Username
|
||||
}
|
||||
|
||||
// Sanitize userName since it may contain filepath separators on Windows.
|
||||
userName = strings.Replace(userName, `\`, "_", -1)
|
||||
}
|
||||
|
||||
// shortHostname returns its argument, truncating at the first period.
|
||||
// For instance, given "www.google.com" it returns "www".
|
||||
func shortHostname(hostname string) string {
|
||||
if i := strings.Index(hostname, "."); i >= 0 {
|
||||
return hostname[:i]
|
||||
}
|
||||
return hostname
|
||||
}
|
||||
|
||||
// logName returns a new log file name containing tag, with start time t, and
|
||||
// the name for the symlink for tag.
|
||||
func logName(tag string, t time.Time) (name, link string) {
|
||||
name = fmt.Sprintf("%s.%s.%s.log.%s.%04d%02d%02d-%02d%02d%02d.%d",
|
||||
program,
|
||||
host,
|
||||
userName,
|
||||
tag,
|
||||
t.Year(),
|
||||
t.Month(),
|
||||
t.Day(),
|
||||
t.Hour(),
|
||||
t.Minute(),
|
||||
t.Second(),
|
||||
pid)
|
||||
return name, program + "." + tag
|
||||
}
|
||||
|
||||
var onceLogDirs sync.Once
|
||||
|
||||
// create creates a new log file and returns the file and its filename, which
|
||||
// contains tag ("INFO", "FATAL", etc.) and t. If the file is created
|
||||
// successfully, create also attempts to update the symlink for that tag, ignoring
|
||||
// errors.
|
||||
func create(tag string, t time.Time) (f *os.File, filename string, err error) {
|
||||
onceLogDirs.Do(createLogDirs)
|
||||
if len(logDirs) == 0 {
|
||||
return nil, "", errors.New("log: no log dirs")
|
||||
}
|
||||
name, link := logName(tag, t)
|
||||
var lastErr error
|
||||
for _, dir := range logDirs {
|
||||
fname := filepath.Join(dir, name)
|
||||
f, err := os.Create(fname)
|
||||
if err == nil {
|
||||
symlink := filepath.Join(dir, link)
|
||||
os.Remove(symlink) // ignore err
|
||||
os.Symlink(name, symlink) // ignore err
|
||||
return f, fname, nil
|
||||
}
|
||||
lastErr = err
|
||||
}
|
||||
return nil, "", fmt.Errorf("log: cannot create log: %v", lastErr)
|
||||
}
|
||||
415
Godeps/_workspace/src/github.com/golang/glog/glog_test.go
generated
vendored
415
Godeps/_workspace/src/github.com/golang/glog/glog_test.go
generated
vendored
@@ -1,415 +0,0 @@
|
||||
// Go support for leveled logs, analogous to https://code.google.com/p/google-glog/
|
||||
//
|
||||
// Copyright 2013 Google Inc. 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 glog
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
stdLog "log"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Test that shortHostname works as advertised.
|
||||
func TestShortHostname(t *testing.T) {
|
||||
for hostname, expect := range map[string]string{
|
||||
"": "",
|
||||
"host": "host",
|
||||
"host.google.com": "host",
|
||||
} {
|
||||
if got := shortHostname(hostname); expect != got {
|
||||
t.Errorf("shortHostname(%q): expected %q, got %q", hostname, expect, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// flushBuffer wraps a bytes.Buffer to satisfy flushSyncWriter.
|
||||
type flushBuffer struct {
|
||||
bytes.Buffer
|
||||
}
|
||||
|
||||
func (f *flushBuffer) Flush() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (f *flushBuffer) Sync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// swap sets the log writers and returns the old array.
|
||||
func (l *loggingT) swap(writers [numSeverity]flushSyncWriter) (old [numSeverity]flushSyncWriter) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
old = l.file
|
||||
for i, w := range writers {
|
||||
logging.file[i] = w
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// newBuffers sets the log writers to all new byte buffers and returns the old array.
|
||||
func (l *loggingT) newBuffers() [numSeverity]flushSyncWriter {
|
||||
return l.swap([numSeverity]flushSyncWriter{new(flushBuffer), new(flushBuffer), new(flushBuffer), new(flushBuffer)})
|
||||
}
|
||||
|
||||
// contents returns the specified log value as a string.
|
||||
func contents(s severity) string {
|
||||
return logging.file[s].(*flushBuffer).String()
|
||||
}
|
||||
|
||||
// contains reports whether the string is contained in the log.
|
||||
func contains(s severity, str string, t *testing.T) bool {
|
||||
return strings.Contains(contents(s), str)
|
||||
}
|
||||
|
||||
// setFlags configures the logging flags how the test expects them.
|
||||
func setFlags() {
|
||||
logging.toStderr = false
|
||||
}
|
||||
|
||||
// Test that Info works as advertised.
|
||||
func TestInfo(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
Info("test")
|
||||
if !contains(infoLog, "I", t) {
|
||||
t.Errorf("Info has wrong character: %q", contents(infoLog))
|
||||
}
|
||||
if !contains(infoLog, "test", t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
func TestInfoDepth(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
|
||||
f := func() { InfoDepth(1, "depth-test1") }
|
||||
|
||||
// The next three lines must stay together
|
||||
_, _, wantLine, _ := runtime.Caller(0)
|
||||
InfoDepth(0, "depth-test0")
|
||||
f()
|
||||
|
||||
msgs := strings.Split(strings.TrimSuffix(contents(infoLog), "\n"), "\n")
|
||||
if len(msgs) != 2 {
|
||||
t.Fatalf("Got %d lines, expected 2", len(msgs))
|
||||
}
|
||||
|
||||
for i, m := range msgs {
|
||||
if !strings.HasPrefix(m, "I") {
|
||||
t.Errorf("InfoDepth[%d] has wrong character: %q", i, m)
|
||||
}
|
||||
w := fmt.Sprintf("depth-test%d", i)
|
||||
if !strings.Contains(m, w) {
|
||||
t.Errorf("InfoDepth[%d] missing %q: %q", i, w, m)
|
||||
}
|
||||
|
||||
// pull out the line number (between : and ])
|
||||
msg := m[strings.LastIndex(m, ":")+1:]
|
||||
x := strings.Index(msg, "]")
|
||||
if x < 0 {
|
||||
t.Errorf("InfoDepth[%d]: missing ']': %q", i, m)
|
||||
continue
|
||||
}
|
||||
line, err := strconv.Atoi(msg[:x])
|
||||
if err != nil {
|
||||
t.Errorf("InfoDepth[%d]: bad line number: %q", i, m)
|
||||
continue
|
||||
}
|
||||
wantLine++
|
||||
if wantLine != line {
|
||||
t.Errorf("InfoDepth[%d]: got line %d, want %d", i, line, wantLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
CopyStandardLogTo("INFO")
|
||||
}
|
||||
|
||||
// Test that CopyStandardLogTo panics on bad input.
|
||||
func TestCopyStandardLogToPanic(t *testing.T) {
|
||||
defer func() {
|
||||
if s, ok := recover().(string); !ok || !strings.Contains(s, "LOG") {
|
||||
t.Errorf(`CopyStandardLogTo("LOG") should have panicked: %v`, s)
|
||||
}
|
||||
}()
|
||||
CopyStandardLogTo("LOG")
|
||||
}
|
||||
|
||||
// Test that using the standard log package logs to INFO.
|
||||
func TestStandardLog(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
stdLog.Print("test")
|
||||
if !contains(infoLog, "I", t) {
|
||||
t.Errorf("Info has wrong character: %q", contents(infoLog))
|
||||
}
|
||||
if !contains(infoLog, "test", t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that the header has the correct format.
|
||||
func TestHeader(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
defer func(previous func() time.Time) { timeNow = previous }(timeNow)
|
||||
timeNow = func() time.Time {
|
||||
return time.Date(2006, 1, 2, 15, 4, 5, .067890e9, time.Local)
|
||||
}
|
||||
pid = 1234
|
||||
Info("test")
|
||||
var line int
|
||||
format := "I0102 15:04:05.067890 1234 glog_test.go:%d] test\n"
|
||||
n, err := fmt.Sscanf(contents(infoLog), format, &line)
|
||||
if n != 1 || err != nil {
|
||||
t.Errorf("log format error: %d elements, error %s:\n%s", n, err, contents(infoLog))
|
||||
}
|
||||
// Scanf treats multiple spaces as equivalent to a single space,
|
||||
// so check for correct space-padding also.
|
||||
want := fmt.Sprintf(format, line)
|
||||
if contents(infoLog) != want {
|
||||
t.Errorf("log format error: got:\n\t%q\nwant:\t%q", contents(infoLog), want)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that an Error log goes to Warning and Info.
|
||||
// Even in the Info log, the source character will be E, so the data should
|
||||
// all be identical.
|
||||
func TestError(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
Error("test")
|
||||
if !contains(errorLog, "E", t) {
|
||||
t.Errorf("Error has wrong character: %q", contents(errorLog))
|
||||
}
|
||||
if !contains(errorLog, "test", t) {
|
||||
t.Error("Error failed")
|
||||
}
|
||||
str := contents(errorLog)
|
||||
if !contains(warningLog, str, t) {
|
||||
t.Error("Warning failed")
|
||||
}
|
||||
if !contains(infoLog, str, t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a Warning log goes to Info.
|
||||
// Even in the Info log, the source character will be W, so the data should
|
||||
// all be identical.
|
||||
func TestWarning(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
Warning("test")
|
||||
if !contains(warningLog, "W", t) {
|
||||
t.Errorf("Warning has wrong character: %q", contents(warningLog))
|
||||
}
|
||||
if !contains(warningLog, "test", t) {
|
||||
t.Error("Warning failed")
|
||||
}
|
||||
str := contents(warningLog)
|
||||
if !contains(infoLog, str, t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a V log goes to Info.
|
||||
func TestV(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
logging.verbosity.Set("2")
|
||||
defer logging.verbosity.Set("0")
|
||||
V(2).Info("test")
|
||||
if !contains(infoLog, "I", t) {
|
||||
t.Errorf("Info has wrong character: %q", contents(infoLog))
|
||||
}
|
||||
if !contains(infoLog, "test", t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a vmodule enables a log in this file.
|
||||
func TestVmoduleOn(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
logging.vmodule.Set("glog_test=2")
|
||||
defer logging.vmodule.Set("")
|
||||
if !V(1) {
|
||||
t.Error("V not enabled for 1")
|
||||
}
|
||||
if !V(2) {
|
||||
t.Error("V not enabled for 2")
|
||||
}
|
||||
if V(3) {
|
||||
t.Error("V enabled for 3")
|
||||
}
|
||||
V(2).Info("test")
|
||||
if !contains(infoLog, "I", t) {
|
||||
t.Errorf("Info has wrong character: %q", contents(infoLog))
|
||||
}
|
||||
if !contains(infoLog, "test", t) {
|
||||
t.Error("Info failed")
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a vmodule of another file does not enable a log in this file.
|
||||
func TestVmoduleOff(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
logging.vmodule.Set("notthisfile=2")
|
||||
defer logging.vmodule.Set("")
|
||||
for i := 1; i <= 3; i++ {
|
||||
if V(Level(i)) {
|
||||
t.Errorf("V enabled for %d", i)
|
||||
}
|
||||
}
|
||||
V(2).Info("test")
|
||||
if contents(infoLog) != "" {
|
||||
t.Error("V logged incorrectly")
|
||||
}
|
||||
}
|
||||
|
||||
// vGlobs are patterns that match/don't match this file at V=2.
|
||||
var vGlobs = map[string]bool{
|
||||
// Easy to test the numeric match here.
|
||||
"glog_test=1": false, // If -vmodule sets V to 1, V(2) will fail.
|
||||
"glog_test=2": true,
|
||||
"glog_test=3": true, // If -vmodule sets V to 1, V(3) will succeed.
|
||||
// These all use 2 and check the patterns. All are true.
|
||||
"*=2": true,
|
||||
"?l*=2": true,
|
||||
"????_*=2": true,
|
||||
"??[mno]?_*t=2": true,
|
||||
// These all use 2 and check the patterns. All are false.
|
||||
"*x=2": false,
|
||||
"m*=2": false,
|
||||
"??_*=2": false,
|
||||
"?[abc]?_*t=2": false,
|
||||
}
|
||||
|
||||
// Test that vmodule globbing works as advertised.
|
||||
func testVmoduleGlob(pat string, match bool, t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
defer logging.vmodule.Set("")
|
||||
logging.vmodule.Set(pat)
|
||||
if V(2) != Verbose(match) {
|
||||
t.Errorf("incorrect match for %q: got %t expected %t", pat, V(2), match)
|
||||
}
|
||||
}
|
||||
|
||||
// Test that a vmodule globbing works as advertised.
|
||||
func TestVmoduleGlob(t *testing.T) {
|
||||
for glob, match := range vGlobs {
|
||||
testVmoduleGlob(glob, match, t)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRollover(t *testing.T) {
|
||||
setFlags()
|
||||
var err error
|
||||
defer func(previous func(error)) { logExitFunc = previous }(logExitFunc)
|
||||
logExitFunc = func(e error) {
|
||||
err = e
|
||||
}
|
||||
defer func(previous uint64) { MaxSize = previous }(MaxSize)
|
||||
MaxSize = 512
|
||||
|
||||
Info("x") // Be sure we have a file.
|
||||
info, ok := logging.file[infoLog].(*syncBuffer)
|
||||
if !ok {
|
||||
t.Fatal("info wasn't created")
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatalf("info has initial error: %v", err)
|
||||
}
|
||||
fname0 := info.file.Name()
|
||||
Info(strings.Repeat("x", int(MaxSize))) // force a rollover
|
||||
if err != nil {
|
||||
t.Fatalf("info has error after big write: %v", err)
|
||||
}
|
||||
|
||||
// Make sure the next log file gets a file name with a different
|
||||
// time stamp.
|
||||
//
|
||||
// TODO: determine whether we need to support subsecond log
|
||||
// rotation. C++ does not appear to handle this case (nor does it
|
||||
// handle Daylight Savings Time properly).
|
||||
time.Sleep(1 * time.Second)
|
||||
|
||||
Info("x") // create a new file
|
||||
if err != nil {
|
||||
t.Fatalf("error after rotation: %v", err)
|
||||
}
|
||||
fname1 := info.file.Name()
|
||||
if fname0 == fname1 {
|
||||
t.Errorf("info.f.Name did not change: %v", fname0)
|
||||
}
|
||||
if info.nbytes >= MaxSize {
|
||||
t.Errorf("file size was not reset: %d", info.nbytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLogBacktraceAt(t *testing.T) {
|
||||
setFlags()
|
||||
defer logging.swap(logging.newBuffers())
|
||||
// The peculiar style of this code simplifies line counting and maintenance of the
|
||||
// tracing block below.
|
||||
var infoLine string
|
||||
setTraceLocation := func(file string, line int, ok bool, delta int) {
|
||||
if !ok {
|
||||
t.Fatal("could not get file:line")
|
||||
}
|
||||
_, file = filepath.Split(file)
|
||||
infoLine = fmt.Sprintf("%s:%d", file, line+delta)
|
||||
err := logging.traceLocation.Set(infoLine)
|
||||
if err != nil {
|
||||
t.Fatal("error setting log_backtrace_at: ", err)
|
||||
}
|
||||
}
|
||||
{
|
||||
// Start of tracing block. These lines know about each other's relative position.
|
||||
_, file, line, ok := runtime.Caller(0)
|
||||
setTraceLocation(file, line, ok, +2) // Two lines between Caller and Info calls.
|
||||
Info("we want a stack trace here")
|
||||
}
|
||||
numAppearances := strings.Count(contents(infoLog), infoLine)
|
||||
if numAppearances < 2 {
|
||||
// Need 2 appearances, one in the log header and one in the trace:
|
||||
// log_test.go:281: I0511 16:36:06.952398 02238 log_test.go:280] we want a stack trace here
|
||||
// ...
|
||||
// github.com/glog/glog_test.go:280 (0x41ba91)
|
||||
// ...
|
||||
// We could be more precise but that would require knowing the details
|
||||
// of the traceback format, which may not be dependable.
|
||||
t.Fatal("got no trace back; log is ", contents(infoLog))
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkHeader(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
buf, _, _ := logging.header(infoLog, 0)
|
||||
logging.putBuffer(buf)
|
||||
}
|
||||
}
|
||||
13
Makefile.am
13
Makefile.am
@@ -1,13 +1,10 @@
|
||||
export GOPATH:=$(abs_top_srcdir)/Godeps/_workspace:$(GOPATH)
|
||||
|
||||
all-local: build-cli build-daemon
|
||||
all-local: build
|
||||
clean-local:
|
||||
-rm -f citadm citd
|
||||
-rm -f gotgt
|
||||
install-exec-local:
|
||||
$(INSTALL_PROGRAM) citadm $(bindir)
|
||||
$(INSTALL_PROGRAM) citd $(bindir)
|
||||
$(INSTALL_PROGRAM) gotgt $(bindir)
|
||||
|
||||
build-cli:
|
||||
go build citadm.go
|
||||
build-daemon:
|
||||
go build citd.go
|
||||
build:
|
||||
go build gotgt.go
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
## gotgt [](https://travis-ci.org/gostor/gotgt)
|
||||
|
||||
Simple Golang SCSI Target framework, this includes two binaries, one is `citadm` which is command line to config and control, the other is `citd` which is a target daemon.
|
||||
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`.
|
||||
|
||||
## Build
|
||||
|
||||
@@ -62,10 +62,6 @@ You can test this with [open-iscsi](http://www.open-iscsi.com/) or [libiscsi](ht
|
||||
* SCSI Command Queue (p2)
|
||||
* More SCSI commands
|
||||
* SPC3/SAM2
|
||||
* Page83(Inquiry) (orzhang, p1)
|
||||
* Page0 (Inquiry) (orzhang, p1)
|
||||
* Define Device UUID
|
||||
* Verify (carmark, p1)
|
||||
* Support `Target Group` and `Target Port` (p3)
|
||||
* Refactor (carmark, p1)
|
||||
* Command Line (carmark, p1)
|
||||
|
||||
19
cmd/cmd.go
19
cmd/cmd.go
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2016 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 cmd
|
||||
|
||||
import (
|
||||
@@ -10,7 +26,7 @@ import (
|
||||
|
||||
func NewCommand(cli *client.Client) *cobra.Command {
|
||||
var cmd = &cobra.Command{
|
||||
Use: "citadm",
|
||||
Use: "gotgt",
|
||||
Short: "Gotgt is a very fast and stable SCSI target framework",
|
||||
Long: ``,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
@@ -18,6 +34,7 @@ func NewCommand(cli *client.Client) *cobra.Command {
|
||||
},
|
||||
}
|
||||
cmd.AddCommand(
|
||||
newDaemonCommand(cli),
|
||||
newCreateCommand(cli),
|
||||
newRemoveCommand(cli),
|
||||
newListCommand(cli),
|
||||
|
||||
@@ -14,11 +14,9 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// SCSI target daemon
|
||||
package main
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -26,54 +24,65 @@ import (
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/golang/glog"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gostor/gotgt/pkg/api/client"
|
||||
"github.com/gostor/gotgt/pkg/apiserver"
|
||||
"github.com/gostor/gotgt/pkg/config"
|
||||
"github.com/gostor/gotgt/pkg/port"
|
||||
_ "github.com/gostor/gotgt/pkg/port/iscsit"
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
_ "github.com/gostor/gotgt/pkg/scsi/backingstore"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
flHelp := flag.Bool("help", false, "Print help message for Hyperd daemon")
|
||||
flHost := flag.String("host", "tcp://127.0.0.1:23457", "Host for SCSI target daemon")
|
||||
flDriver := flag.String("driver", "iscsi", "SCSI low level driver")
|
||||
flag.Usage = func() { *flHelp = true }
|
||||
flag.Parse()
|
||||
flag.Set("logtostderr", "true")
|
||||
if *flHelp == true {
|
||||
fmt.Println(`Usage:
|
||||
xxxd [OPTIONS]
|
||||
|
||||
Application Options:
|
||||
--host="" Host for SCSI target daemon
|
||||
--driver=iscsi SCSI low level driver
|
||||
|
||||
Help Options:
|
||||
-h, --help Show this help message
|
||||
`)
|
||||
return
|
||||
func newDaemonCommand(cli *client.Client) *cobra.Command {
|
||||
var host string
|
||||
var driver string
|
||||
var logLevel string
|
||||
var cmd = &cobra.Command{
|
||||
Use: "daemon",
|
||||
Short: "Setup a daemon",
|
||||
Long: `Setup the Gotgt's daemon`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return createDaemon(host, driver, logLevel)
|
||||
},
|
||||
}
|
||||
flags := cmd.Flags()
|
||||
flags.StringVar(&logLevel, "log", "info", "Log level of SCSI target daemon")
|
||||
flags.StringVar(&host, "host", "tcp://127.0.0.1:23457", "Host for SCSI target daemon")
|
||||
flags.StringVar(&driver, "driver", "iscsi", "SCSI low level driver")
|
||||
return cmd
|
||||
}
|
||||
|
||||
func createDaemon(host, driver, level string) error {
|
||||
switch level {
|
||||
case "info":
|
||||
log.SetLevel(log.InfoLevel)
|
||||
case "warn":
|
||||
log.SetLevel(log.WarnLevel)
|
||||
case "debug":
|
||||
log.SetLevel(log.DebugLevel)
|
||||
case "panic", "fatal", "error":
|
||||
log.SetLevel(log.ErrorLevel)
|
||||
default:
|
||||
return fmt.Errorf("unknown log level: %v", level)
|
||||
}
|
||||
config, err := config.Load(config.ConfigDir())
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
os.Exit(1)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = scsi.InitSCSILUMap(config)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
os.Exit(1)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
scsiTarget := scsi.NewSCSITargetService()
|
||||
targetDriver, err := port.NewTargetService(*flDriver, scsiTarget)
|
||||
targetDriver, err := scsi.NewTargetDriver(driver, scsiTarget)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
os.Exit(1)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
for tgtname := range config.ISCSITargets {
|
||||
@@ -89,22 +98,23 @@ Help Options:
|
||||
}
|
||||
|
||||
hosts := []string{}
|
||||
if *flHost != "" {
|
||||
hosts = append(hosts, *flHost)
|
||||
if host != "" {
|
||||
hosts = append(hosts, host)
|
||||
}
|
||||
for _, protoAddr := range hosts {
|
||||
protoAddrParts := strings.SplitN(protoAddr, "://", 2)
|
||||
if len(protoAddrParts) != 2 {
|
||||
glog.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
|
||||
return
|
||||
err = fmt.Errorf("bad format %s, expected PROTO://ADDR", protoAddr)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
serverConfig.Addrs = append(serverConfig.Addrs, apiserver.Addr{Proto: protoAddrParts[0], Addr: protoAddrParts[1]})
|
||||
}
|
||||
|
||||
s, err := apiserver.New(serverConfig)
|
||||
if err != nil {
|
||||
glog.Errorf(err.Error())
|
||||
return
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
s.InitRouters()
|
||||
// The serve API routine never exits unless an error occurs
|
||||
@@ -123,10 +133,11 @@ Help Options:
|
||||
// If we have an error here it is unique to API (as daemonErr would have
|
||||
// exited the daemon process above)
|
||||
if errAPI != nil {
|
||||
glog.Warningf("Shutting down due to ServeAPI error: %v", errAPI)
|
||||
log.Warnf("Shutting down due to ServeAPI error: %v", errAPI)
|
||||
}
|
||||
case <-stopAll:
|
||||
break
|
||||
}
|
||||
s.Close()
|
||||
return nil
|
||||
}
|
||||
@@ -14,7 +14,7 @@ func newVersionCommand(cli *client.Client) *cobra.Command {
|
||||
Short: "Print the version number of gotgt",
|
||||
Long: `All software has versions. This is Gotgt 's`,
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
fmt.Printf("Gotgt %s -- HEAD\n", version.VERSION)
|
||||
fmt.Printf("Gotgt %s -- HEAD\n", version.Version)
|
||||
},
|
||||
}
|
||||
return cmd
|
||||
|
||||
@@ -25,9 +25,11 @@ import (
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/gostor/gotgt/cmd"
|
||||
"github.com/gostor/gotgt/pkg/api/client"
|
||||
"github.com/gostor/gotgt/pkg/version"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
host := "tcp://127.0.0.1:23457"
|
||||
httpClient, err := newHTTPClient(host)
|
||||
if err != nil {
|
||||
@@ -35,7 +37,7 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
cli, err := client.NewClient(host, "0.1", httpClient, nil)
|
||||
cli, err := client.NewClient(host, version.Version, httpClient, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v", err)
|
||||
os.Exit(1)
|
||||
@@ -177,8 +177,10 @@ type SCSICommand struct {
|
||||
}
|
||||
|
||||
type ITNexus struct {
|
||||
ID uuid.UUID `json:"id"` /*UUIDv1*/
|
||||
Tag string `json:"Tag"` /*For protocal spec identifer*/
|
||||
// UUID v1
|
||||
ID uuid.UUID `json:"id"`
|
||||
// For protocal spec identifer
|
||||
Tag string `json:"Tag"`
|
||||
}
|
||||
|
||||
type ITNexusLuInfo struct {
|
||||
@@ -333,7 +335,10 @@ type BackingStore interface {
|
||||
Init(dev *SCSILu, Opts string) error
|
||||
Exit(dev *SCSILu) error
|
||||
Size(dev *SCSILu) uint64
|
||||
CommandSubmit(cmd *SCSICommand) error
|
||||
Read(offset, tl int64) ([]byte, error)
|
||||
Write([]byte, int64) error
|
||||
DataSync() error
|
||||
DataAdvise(int64, int64, uint32) error
|
||||
}
|
||||
|
||||
type SCSIDeviceProtocol interface {
|
||||
@@ -346,13 +351,17 @@ type SCSIDeviceProtocol interface {
|
||||
ExitLu(lu *SCSILu) error
|
||||
}
|
||||
type ModePage struct {
|
||||
Pcode uint8 // Page code
|
||||
SubPcode uint8 // Sub page code
|
||||
Data []byte // Rest of mode page info
|
||||
// Page code
|
||||
PageCode uint8
|
||||
// Sub page code
|
||||
SubPageCode uint8
|
||||
// Rest of mode page info
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type SCSIReservation struct {
|
||||
ID uuid.UUID //Internal Reservation ID
|
||||
// Internal reservation ID
|
||||
ID uuid.UUID
|
||||
Key uint64
|
||||
ITNexusID uuid.UUID
|
||||
Scope uint8
|
||||
|
||||
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package apiserver contains the code that provides a rest.ful api service.
|
||||
// Package apiserver contains the code that provides a rest.ful API service.
|
||||
package apiserver
|
||||
|
||||
import (
|
||||
@@ -25,9 +25,9 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
systemdActivation "github.com/coreos/go-systemd/activation"
|
||||
"github.com/docker/go-connections/sockets"
|
||||
"github.com/golang/glog"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/gostor/gotgt/pkg/apiserver/httputils"
|
||||
"github.com/gostor/gotgt/pkg/apiserver/router"
|
||||
@@ -79,7 +79,7 @@ func New(cfg *Config) (*Server, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
glog.V(3).Infof("Server created for HTTP on %s (%s)", addr.Proto, addr.Addr)
|
||||
log.Infof("Server created for HTTP on %s (%s)", addr.Proto, addr.Addr)
|
||||
s.servers = append(s.servers, srv...)
|
||||
}
|
||||
return s, nil
|
||||
@@ -89,7 +89,7 @@ func New(cfg *Config) (*Server, error) {
|
||||
func (s *Server) Close() {
|
||||
for _, srv := range s.servers {
|
||||
if err := srv.Close(); err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -104,7 +104,7 @@ func (s *Server) serveAPI() error {
|
||||
srv.srv.Handler = s.routerSwapper
|
||||
go func(srv *HTTPServer) {
|
||||
var err error
|
||||
glog.V(3).Infof("API listen on %s", srv.l.Addr())
|
||||
log.Infof("API listen on %s", srv.l.Addr())
|
||||
if err = srv.Serve(); err != nil && strings.Contains(err.Error(), "use of closed network connection") {
|
||||
err = nil
|
||||
}
|
||||
@@ -142,7 +142,7 @@ func (s *HTTPServer) Close() error {
|
||||
|
||||
func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) {
|
||||
if s.cfg.TLSConfig == nil || s.cfg.TLSConfig.ClientAuth != tls.RequireAndVerifyClientCert {
|
||||
glog.Warning("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
||||
log.Warning("/!\\ DON'T BIND ON ANY IP ADDRESS WITHOUT setting -tlsverify IF YOU DON'T KNOW WHAT YOU'RE DOING /!\\")
|
||||
}
|
||||
if l, err = sockets.NewTCPSocket(addr, s.cfg.TLSConfig); err != nil {
|
||||
return nil, err
|
||||
@@ -154,7 +154,7 @@ func (s *Server) initTCPSocket(addr string) (l net.Listener, err error) {
|
||||
func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
// log the handler call
|
||||
glog.V(3).Infof("Calling %s %s", r.Method, r.URL.Path)
|
||||
log.Infof("Calling %s %s", r.Method, r.URL.Path)
|
||||
|
||||
// Define the context that we'll pass around to share info
|
||||
// like the docker-request-id.
|
||||
@@ -172,7 +172,7 @@ func (s *Server) makeHTTPHandler(handler httputils.APIFunc) http.HandlerFunc {
|
||||
}
|
||||
|
||||
if err := handlerFunc(ctx, w, r, vars); err != nil {
|
||||
glog.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
||||
log.Errorf("Handler for %s %s returned error: %v", r.Method, r.URL.Path, err)
|
||||
httputils.WriteError(w, err)
|
||||
}
|
||||
}
|
||||
@@ -195,12 +195,12 @@ func (s *Server) addRouter(r router.Router) {
|
||||
func (s *Server) createMux() *mux.Router {
|
||||
m := mux.NewRouter()
|
||||
|
||||
glog.V(3).Infof("Registering routers")
|
||||
log.Infof("Registering routers")
|
||||
for _, apiRouter := range s.routers {
|
||||
for _, r := range apiRouter.Routes() {
|
||||
f := s.makeHTTPHandler(r.Handler())
|
||||
|
||||
glog.V(3).Infof("Registering %s, %s", r.Method(), r.Path())
|
||||
log.Infof("Registering %s, %s", r.Method(), r.Path())
|
||||
m.Path(versionMatcher + r.Path()).Methods(r.Method()).Handler(f)
|
||||
m.Path(r.Path()).Methods(r.Method()).Handler(f)
|
||||
}
|
||||
@@ -214,7 +214,7 @@ func (s *Server) createMux() *mux.Router {
|
||||
// the API execution.
|
||||
func (s *Server) Wait(waitChan chan error) {
|
||||
if err := s.serveAPI(); err != nil {
|
||||
glog.Errorf("ServeAPI error: %v", err)
|
||||
log.Errorf("ServeAPI error: %v", err)
|
||||
waitChan <- err
|
||||
return
|
||||
}
|
||||
@@ -307,7 +307,7 @@ func listenFD(addr string, tlsConfig *tls.Config) ([]net.Listener, error) {
|
||||
continue
|
||||
}
|
||||
if err := ls.Close(); err != nil {
|
||||
glog.Errorf("Failed to close systemd activated file at fd %d: %v", fdOffset+3, err)
|
||||
log.Errorf("Failed to close systemd activated file at fd %d: %v", fdOffset+3, err)
|
||||
}
|
||||
}
|
||||
return []net.Listener{listeners[fdOffset]}, nil
|
||||
|
||||
@@ -13,6 +13,8 @@ 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 httputils contains a bunch of HTTP utility code, some generic
|
||||
package httputils
|
||||
|
||||
import (
|
||||
@@ -157,11 +159,11 @@ func WriteJSON(w http.ResponseWriter, code int, v interface{}) error {
|
||||
// It panics if the context value does not have version.Version type.
|
||||
func VersionFromContext(ctx context.Context) string {
|
||||
if ctx == nil {
|
||||
return version.VERSION
|
||||
return version.Version
|
||||
}
|
||||
val := ctx.Value(APIVersionKey)
|
||||
if val == nil {
|
||||
return version.VERSION
|
||||
return version.Version
|
||||
}
|
||||
return val.(string)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package config contains how to get/save config parameters from file.
|
||||
package config
|
||||
|
||||
import (
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
Copyright 2016 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 port
|
||||
|
||||
import "github.com/gostor/gotgt/pkg/api"
|
||||
|
||||
type SCSITargetDriver interface {
|
||||
Init() error
|
||||
Exit() error
|
||||
|
||||
CreateTarget(target *api.SCSITarget) error
|
||||
DestroyTarget(target *api.SCSITarget) error
|
||||
CreatePortal(name string) error
|
||||
DestroyPortal(name string) error
|
||||
CreateLu(lu *api.SCSILu) error
|
||||
GetLu(lun uint8) (uint64, error)
|
||||
|
||||
CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error
|
||||
}
|
||||
|
||||
type fakeSCSITargetDriver struct {
|
||||
api.SCSITargetDriverCommon
|
||||
}
|
||||
|
||||
func (fake *fakeSCSITargetDriver) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakeSCSITargetDriver) Exit() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakeSCSITargetDriver) CreateTarget(target *api.SCSITarget) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakeSCSITargetDriver) DestroyTarget(target *api.SCSITarget) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakeSCSITargetDriver) CreatePortal(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakeSCSITargetDriver) DestroyPortal(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakeSCSITargetDriver) CreateLu(lu *api.SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakeSCSITargetDriver) GetLun(lun uint8) (uint64, error) {
|
||||
return 0, nil
|
||||
}
|
||||
func (fake *fakeSCSITargetDriver) CommandNotify(nid uint64, result int, cmd *api.SCSICommand) error {
|
||||
return nil
|
||||
}
|
||||
@@ -67,18 +67,27 @@ type ISCSICommand struct {
|
||||
ExpCmdSN, MaxCmdSN uint32
|
||||
AHSLen int
|
||||
|
||||
ConnID uint16 // Connection ID.
|
||||
CmdSN uint32 // Command serial number.
|
||||
ExpStatSN uint32 // Expected status serial.
|
||||
// Connection ID.
|
||||
ConnID uint16
|
||||
// Command serial number.
|
||||
CmdSN uint32
|
||||
// Expected status serial.
|
||||
ExpStatSN uint32
|
||||
|
||||
Read, Write bool
|
||||
LUN [8]uint8
|
||||
Transit bool // Transit bit.
|
||||
Cont bool // Continue bit.
|
||||
CSG, NSG Stage // Current Stage, Next Stage.
|
||||
ISID uint64 // Initiator part of the SSID.
|
||||
TSIH uint16 // Target-assigned Session Identifying Handle.
|
||||
StatSN uint32 // Status serial number.
|
||||
// Transit bit.
|
||||
Transit bool
|
||||
// Continue bit.
|
||||
Cont bool
|
||||
// Current Stage, Next Stage.
|
||||
CSG, NSG Stage
|
||||
// Initiator part of the SSID.
|
||||
ISID uint64
|
||||
// Target-assigned Session Identifying Handle.
|
||||
TSIH uint16
|
||||
// Status serial number.
|
||||
StatSN uint32
|
||||
|
||||
// For login response.
|
||||
StatusClass uint8
|
||||
@@ -252,6 +261,12 @@ func (m *ISCSICommand) scsiCmdRespBytes() []byte {
|
||||
for i := 0; i < 3*4; i++ {
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
buf.Write(m.RawData)
|
||||
dl := len(m.RawData)
|
||||
for dl%4 > 0 {
|
||||
dl++
|
||||
buf.WriteByte(0x00)
|
||||
}
|
||||
|
||||
return buf.Bytes()
|
||||
}
|
||||
|
||||
@@ -18,46 +18,44 @@ package iscsit
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"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"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type ISCSITargetService struct {
|
||||
type ISCSITargetDriver struct {
|
||||
SCSI *scsi.SCSITargetService
|
||||
Name string
|
||||
iSCSITargets map[string]*ISCSITarget
|
||||
}
|
||||
|
||||
func init() {
|
||||
port.RegisterTargetService("iscsi", NewISCSITargetService)
|
||||
scsi.RegisterTargetDriver("iscsi", NewISCSITargetDriver)
|
||||
}
|
||||
|
||||
func NewISCSITargetService(base *scsi.SCSITargetService) (port.SCSITargetService, error) {
|
||||
return &ISCSITargetService{
|
||||
func NewISCSITargetDriver(base *scsi.SCSITargetService) (scsi.SCSITargetDriver, error) {
|
||||
return &ISCSITargetDriver{
|
||||
Name: "iscsi",
|
||||
iSCSITargets: map[string]*ISCSITarget{},
|
||||
SCSI: base,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) NewTarget(tgtName string, configInfo *config.Config) (port.SCSITargetDriver, error) {
|
||||
func (s *ISCSITargetDriver) NewTarget(tgtName string, configInfo *config.Config) error {
|
||||
if _, ok := s.iSCSITargets[tgtName]; ok {
|
||||
return nil, fmt.Errorf("target name has been existed")
|
||||
return fmt.Errorf("target name has been existed")
|
||||
}
|
||||
stgt, err := s.SCSI.NewSCSITarget(len(s.iSCSITargets), "iscsi", tgtName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return err
|
||||
}
|
||||
tgt := newISCSITarget(stgt)
|
||||
s.iSCSITargets[tgtName] = tgt
|
||||
@@ -73,39 +71,35 @@ func (s *ISCSITargetService) NewTarget(tgtName string, configInfo *config.Config
|
||||
s.AddiSCSIPortal(tgtName, uint16(tpgtNumber), portal.Portal)
|
||||
}
|
||||
}
|
||||
return tgt, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) AddiSCSIPortal(tgtName string, tpgt uint16, portal string) error {
|
||||
func (s *ISCSITargetDriver) 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)
|
||||
return fmt.Errorf("no target %s", tgtName)
|
||||
}
|
||||
|
||||
if tpgtInfo, ok = target.TPGTs[tpgt]; !ok {
|
||||
errMsg = fmt.Sprintf("no tpgt %d", tpgt)
|
||||
return errors.New(errMsg)
|
||||
return fmt.Errorf("no tpgt %d", tpgt)
|
||||
}
|
||||
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 fmt.Errorf("duplicate portal %s,in %s,%d", portal, tgtName, tpgt)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) HasPortal(tgtName string, tpgt uint16, portal string) bool {
|
||||
func (s *ISCSITargetDriver) HasPortal(tgtName string, tpgt uint16, portal string) bool {
|
||||
var (
|
||||
ok bool
|
||||
target *ISCSITarget
|
||||
@@ -127,51 +121,51 @@ func (s *ISCSITargetService) HasPortal(tgtName string, tpgt uint16, portal strin
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) Run() error {
|
||||
func (s *ISCSITargetDriver) Run() error {
|
||||
l, err := net.Listen("tcp", ":3260")
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer l.Close()
|
||||
|
||||
for {
|
||||
glog.Info("Listening ...")
|
||||
log.Info("Listening ...")
|
||||
conn, err := l.Accept()
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
continue
|
||||
}
|
||||
glog.Info(conn.LocalAddr().String())
|
||||
glog.Info("Accepting ...")
|
||||
log.Info(conn.LocalAddr().String())
|
||||
log.Info("Accepting ...")
|
||||
iscsiConn := &iscsiConnection{conn: conn}
|
||||
iscsiConn.init()
|
||||
iscsiConn.rxIOState = IOSTATE_RX_BHS
|
||||
|
||||
glog.Infof("connection is connected from %s...\n", conn.RemoteAddr().String())
|
||||
log.Infof("connection is connected from %s...\n", conn.RemoteAddr().String())
|
||||
// start a new thread to do with this command
|
||||
go s.handler(DATAIN, iscsiConn)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) handler(events byte, conn *iscsiConnection) {
|
||||
func (s *ISCSITargetDriver) handler(events byte, conn *iscsiConnection) {
|
||||
|
||||
if events&DATAIN != 0 {
|
||||
glog.V(1).Infof("rx handler processing...")
|
||||
log.Debug("rx handler processing...")
|
||||
go s.rxHandler(conn)
|
||||
}
|
||||
if conn.state != CONN_STATE_CLOSE && events&DATAOUT != 0 {
|
||||
glog.V(1).Infof("tx handler processing...")
|
||||
log.Debug("tx handler processing...")
|
||||
s.txHandler(conn)
|
||||
}
|
||||
if conn.state == CONN_STATE_CLOSE {
|
||||
glog.Warningf("iscsi connection[%d] closed", conn.CID)
|
||||
log.Warningf("iscsi connection[%d] closed", conn.CID)
|
||||
conn.close()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) rxHandler(conn *iscsiConnection) {
|
||||
func (s *ISCSITargetDriver) rxHandler(conn *iscsiConnection) {
|
||||
var (
|
||||
hdigest uint = 0
|
||||
ddigest uint = 0
|
||||
@@ -187,22 +181,22 @@ func (s *ISCSITargetService) rxHandler(conn *iscsiConnection) {
|
||||
for {
|
||||
switch conn.rxIOState {
|
||||
case IOSTATE_RX_BHS:
|
||||
glog.Infof("rx handler: IOSTATE_RX_BHS")
|
||||
log.Debug("rx handler: IOSTATE_RX_BHS")
|
||||
buf, length, err := conn.readData(BHS_SIZE)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
if length == 0 {
|
||||
glog.Warningf("set connection to close")
|
||||
log.Warningf("set connection to close")
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
return
|
||||
}
|
||||
conn.rxBuffer = buf
|
||||
cmd, err = parseHeader(buf)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
glog.Warningf("set connection to close")
|
||||
log.Error(err)
|
||||
log.Warningf("set connection to close")
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
return
|
||||
}
|
||||
@@ -211,8 +205,8 @@ func (s *ISCSITargetService) rxHandler(conn *iscsiConnection) {
|
||||
conn.rxIOState = IOSTATE_RX_INIT_AHS
|
||||
break
|
||||
}
|
||||
glog.V(2).Infof("got command: \n%s", cmd.String())
|
||||
glog.V(2).Infof("got buffer: %v", buf)
|
||||
log.Debugf("got command: \n%s", cmd.String())
|
||||
log.Debugf("got buffer: %v", buf)
|
||||
final = true
|
||||
case IOSTATE_RX_INIT_AHS:
|
||||
conn.rxIOState = IOSTATE_RX_DATA
|
||||
@@ -233,24 +227,24 @@ func (s *ISCSITargetService) rxHandler(conn *iscsiConnection) {
|
||||
for length < dl {
|
||||
b, l, err := conn.readData(dl - length)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
length += l
|
||||
buf = append(buf, b...)
|
||||
}
|
||||
if length != dl {
|
||||
glog.V(2).Infof("get length is %d, but expected %d", length, dl)
|
||||
glog.Warningf("set connection to close")
|
||||
log.Debugf("get length is %d, but expected %d", length, dl)
|
||||
log.Warning("set connection to close")
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
return
|
||||
}
|
||||
cmd.RawData = buf[:length]
|
||||
conn.rxBuffer = append(conn.rxBuffer, buf...)
|
||||
final = true
|
||||
glog.Infof("got command: \n%s", cmd.String())
|
||||
log.Debugf("got command: \n%s", cmd.String())
|
||||
default:
|
||||
glog.Errorf("error %d %d\n", conn.state, conn.rxIOState)
|
||||
log.Errorf("error %d %d\n", conn.state, conn.rxIOState)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -266,34 +260,34 @@ func (s *ISCSITargetService) rxHandler(conn *iscsiConnection) {
|
||||
conn.resp = &ISCSICommand{}
|
||||
switch conn.req.OpCode {
|
||||
case OpLoginReq:
|
||||
glog.Infof("OpLoginReq")
|
||||
log.Debug("OpLoginReq")
|
||||
if err := s.iscsiExecLogin(conn); err != nil {
|
||||
glog.Error(err)
|
||||
glog.Warningf("set connection to close")
|
||||
log.Error(err)
|
||||
log.Warningf("set connection to close")
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
}
|
||||
case OpLogoutReq:
|
||||
glog.Infof("OpLogoutReq")
|
||||
log.Debug("OpLogoutReq")
|
||||
if err := iscsiExecLogout(conn); err != nil {
|
||||
glog.Warningf("set connection to close")
|
||||
log.Warningf("set connection to close")
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
}
|
||||
case OpTextReq:
|
||||
glog.Infof("OpTextReq")
|
||||
log.Debug("OpTextReq")
|
||||
if err := s.iscsiExecText(conn); err != nil {
|
||||
glog.Warningf("set connection to close")
|
||||
log.Warningf("set connection to close")
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
}
|
||||
default:
|
||||
iscsiExecReject(conn)
|
||||
}
|
||||
glog.V(2).Infof("connection state is %v", conn.state)
|
||||
glog.V(2).Infof("%#v", conn.resp.String())
|
||||
log.Debugf("connection state is %v", conn.state)
|
||||
log.Debugf("%#v", conn.resp.String())
|
||||
s.handler(DATAOUT, conn)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
func (s *ISCSITargetDriver) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
var (
|
||||
target *ISCSITarget
|
||||
cmd = conn.req
|
||||
@@ -380,7 +374,7 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
// create a new session
|
||||
sess, err := s.NewISCSISession(conn, cmd.ISID)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(sess)}
|
||||
@@ -412,15 +406,15 @@ func iscsiExecLogout(conn *iscsiConnection) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) iscsiExecText(conn *iscsiConnection) error {
|
||||
func (s *ISCSITargetDriver) iscsiExecText(conn *iscsiConnection) error {
|
||||
var result = []util.KeyValue{}
|
||||
cmd := conn.req
|
||||
keys := util.ParseKVText(cmd.RawData)
|
||||
if st, ok := keys["SendTargets"]; ok {
|
||||
if st == "All" {
|
||||
for name, tgt := range s.iSCSITargets {
|
||||
glog.V(2).Infof("iscsi target: %v", name)
|
||||
//glog.V(2).Infof("iscsi target portals: %v", tgt.Portals)
|
||||
log.Debugf("iscsi target: %v", name)
|
||||
//log.Debugf("iscsi target portals: %v", tgt.Portals)
|
||||
|
||||
result = append(result, util.KeyValue{"TargetName", name})
|
||||
for _, tpgt := range tgt.TPGTs {
|
||||
@@ -505,7 +499,7 @@ func iscsiExecR2T(conn *iscsiConnection) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) txHandler(conn *iscsiConnection) {
|
||||
func (s *ISCSITargetDriver) txHandler(conn *iscsiConnection) {
|
||||
var (
|
||||
hdigest uint = 0
|
||||
ddigest uint = 0
|
||||
@@ -518,23 +512,23 @@ func (s *ISCSITargetService) txHandler(conn *iscsiConnection) {
|
||||
if conn.state == CONN_STATE_SCSI && conn.txTask == nil {
|
||||
err := s.scsiCommandHandler(conn)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
for {
|
||||
switch conn.txIOState {
|
||||
case IOSTATE_TX_BHS:
|
||||
glog.V(2).Infof("ready to write response")
|
||||
glog.V(2).Infof("%s", conn.resp.String())
|
||||
glog.V(2).Infof("length of RawData is %d", len(conn.resp.RawData))
|
||||
glog.V(2).Infof("length of resp is %d", len(conn.resp.Bytes()))
|
||||
log.Debug("ready to write response")
|
||||
log.Debugf("%s", conn.resp.String())
|
||||
log.Debugf("length of RawData is %d", len(conn.resp.RawData))
|
||||
log.Debugf("length of resp is %d", len(conn.resp.Bytes()))
|
||||
if l, err := conn.write(conn.resp.Bytes()); err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return
|
||||
} else {
|
||||
conn.txIOState = IOSTATE_TX_INIT_AHS
|
||||
glog.V(2).Infof("success to write %d length", l)
|
||||
log.Debugf("success to write %d length", l)
|
||||
}
|
||||
case IOSTATE_TX_INIT_AHS:
|
||||
if hdigest != 0 {
|
||||
@@ -553,7 +547,7 @@ func (s *ISCSITargetService) txHandler(conn *iscsiConnection) {
|
||||
conn.txIOState = IOSTATE_TX_INIT_DDIGEST
|
||||
}
|
||||
default:
|
||||
glog.Errorf("error %d %d\n", conn.state, conn.txIOState)
|
||||
log.Errorf("error %d %d\n", conn.state, conn.txIOState)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -562,41 +556,41 @@ func (s *ISCSITargetService) txHandler(conn *iscsiConnection) {
|
||||
}
|
||||
}
|
||||
|
||||
glog.V(3).Infof("connection state: %d", conn.state)
|
||||
log.Debugf("connection state: %d", conn.state)
|
||||
switch conn.state {
|
||||
case CONN_STATE_CLOSE, CONN_STATE_EXIT:
|
||||
glog.Warningf("set connection to close")
|
||||
log.Warnf("set connection to close")
|
||||
conn.state = CONN_STATE_CLOSE
|
||||
case CONN_STATE_SECURITY_LOGIN:
|
||||
conn.state = CONN_STATE_LOGIN
|
||||
glog.V(3).Infof("CONN_STATE_LOGIN")
|
||||
log.Debugf("CONN_STATE_LOGIN")
|
||||
case CONN_STATE_SECURITY_FULL, CONN_STATE_LOGIN_FULL:
|
||||
if conn.sessionType == SESSION_NORMAL {
|
||||
conn.state = CONN_STATE_KERNEL
|
||||
glog.Infof("CONN_STATE_KERNEL")
|
||||
log.Infof("CONN_STATE_KERNEL")
|
||||
conn.state = CONN_STATE_SCSI
|
||||
glog.V(3).Infof("CONN_STATE_SCSI")
|
||||
log.Debugf("CONN_STATE_SCSI")
|
||||
} else {
|
||||
conn.state = CONN_STATE_FULL
|
||||
glog.V(3).Infof("CONN_STATE_FULL")
|
||||
log.Debugf("CONN_STATE_FULL")
|
||||
}
|
||||
conn.rxIOState = IOSTATE_RX_BHS
|
||||
s.handler(DATAIN, conn)
|
||||
case CONN_STATE_SCSI:
|
||||
conn.txTask = nil
|
||||
default:
|
||||
glog.Warningf("unexpected connection state: %d", conn.state)
|
||||
log.Warnf("unexpected connection state: %d", conn.state)
|
||||
conn.rxIOState = IOSTATE_RX_BHS
|
||||
s.handler(DATAIN, conn)
|
||||
}
|
||||
glog.Infof("%d", conn.state)
|
||||
log.Infof("%d", conn.state)
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err error) {
|
||||
func (s *ISCSITargetDriver) scsiCommandHandler(conn *iscsiConnection) (err error) {
|
||||
req := conn.req
|
||||
switch req.OpCode {
|
||||
case OpSCSICmd:
|
||||
glog.V(2).Infof("SCSI Command processing...")
|
||||
log.Debugf("SCSI Command processing...")
|
||||
scmd := &api.SCSICommand{}
|
||||
task := &iscsiTask{conn: conn, cmd: conn.req, tag: conn.req.TaskTag, scmd: scmd}
|
||||
if req.Write {
|
||||
@@ -605,7 +599,7 @@ func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err erro
|
||||
if !req.Final {
|
||||
task.unsolCount = 1
|
||||
}
|
||||
glog.V(2).Infof("SCSI write, R2T count: %d, unsol Count: %d, offset: %d", task.r2tCount, task.unsolCount, task.offset)
|
||||
log.Debugf("SCSI write, R2T count: %d, unsol Count: %d, offset: %d", task.r2tCount, task.unsolCount, task.offset)
|
||||
|
||||
if task.scmd.OutSDBBuffer.Buffer == nil {
|
||||
task.scmd.OutSDBBuffer.Buffer = bytes.NewBuffer([]byte{})
|
||||
@@ -673,7 +667,7 @@ func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err erro
|
||||
conn.txIOState = IOSTATE_TX_BHS
|
||||
iscsiExecTMFunction(conn)
|
||||
case OpSCSIOut:
|
||||
glog.V(1).Infof("iSCSI Data-out processing...")
|
||||
log.Debugf("iSCSI Data-out processing...")
|
||||
var task *iscsiTask
|
||||
for _, t := range conn.session.PendingTasks {
|
||||
if t.tag == conn.req.TaskTag {
|
||||
@@ -682,16 +676,16 @@ func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err erro
|
||||
}
|
||||
if task == nil {
|
||||
err = fmt.Errorf("Cannot find iSCSI task with tag[%v]", conn.req.TaskTag)
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
task.offset = task.offset + conn.req.DataLen
|
||||
task.r2tCount = task.r2tCount - conn.req.DataLen
|
||||
task.scmd.OutSDBBuffer.Buffer.Write(conn.req.RawData)
|
||||
glog.V(2).Infof("Final: %v", conn.req.Final)
|
||||
glog.V(2).Infof("r2tCount: %v", task.r2tCount)
|
||||
log.Debugf("Final: %v", conn.req.Final)
|
||||
log.Debugf("r2tCount: %v", task.r2tCount)
|
||||
if !conn.req.Final {
|
||||
glog.V(1).Infof("Not ready to exec the task")
|
||||
log.Debugf("Not ready to exec the task")
|
||||
conn.rxIOState = IOSTATE_RX_BHS
|
||||
s.handler(DATAIN, conn)
|
||||
return nil
|
||||
@@ -703,7 +697,7 @@ func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err erro
|
||||
break
|
||||
}
|
||||
task.offset = 0
|
||||
glog.V(1).Infof("Process the Data-out package")
|
||||
log.Debugf("Process the Data-out package")
|
||||
conn.rxTask = task
|
||||
if err = s.iscsiExecTask(task); err != nil {
|
||||
return
|
||||
@@ -736,11 +730,11 @@ func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err erro
|
||||
iscsiExecLogout(conn)
|
||||
case OpTextReq, OpSNACKReq:
|
||||
err = fmt.Errorf("Cannot handle yet %s", opCodeMap[conn.req.OpCode])
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return
|
||||
default:
|
||||
err = fmt.Errorf("Unknown op %s", opCodeMap[conn.req.OpCode])
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return
|
||||
}
|
||||
conn.rxIOState = IOSTATE_RX_BHS
|
||||
@@ -748,7 +742,7 @@ func (s *ISCSITargetService) scsiCommandHandler(conn *iscsiConnection) (err erro
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) iscsiTaskQueueHandler(task *iscsiTask) error {
|
||||
func (s *ISCSITargetDriver) iscsiTaskQueueHandler(task *iscsiTask) error {
|
||||
conn := task.conn
|
||||
sess := conn.session
|
||||
cmd := task.cmd
|
||||
@@ -756,16 +750,16 @@ func (s *ISCSITargetService) iscsiTaskQueueHandler(task *iscsiTask) error {
|
||||
return s.iscsiExecTask(task)
|
||||
}
|
||||
cmdsn := cmd.CmdSN
|
||||
glog.V(2).Infof("CmdSN of command is %d, ExpCmdSN of session is %d", cmdsn, sess.ExpCmdSN)
|
||||
log.Debugf("CmdSN of command is %d, ExpCmdSN of session is %d", cmdsn, sess.ExpCmdSN)
|
||||
if cmdsn == sess.ExpCmdSN {
|
||||
retry:
|
||||
cmdsn += 1
|
||||
sess.ExpCmdSN = cmdsn
|
||||
glog.V(2).Infof("session's ExpCmdSN is %d", cmdsn)
|
||||
log.Debugf("session's ExpCmdSN is %d", cmdsn)
|
||||
|
||||
glog.V(2).Infof("process task(%d)", task.cmd.CmdSN)
|
||||
log.Debugf("process task(%d)", task.cmd.CmdSN)
|
||||
if err := s.iscsiExecTask(task); err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
}
|
||||
if len(sess.PendingTasks) == 0 {
|
||||
return nil
|
||||
@@ -781,10 +775,10 @@ func (s *ISCSITargetService) iscsiTaskQueueHandler(task *iscsiTask) error {
|
||||
} else {
|
||||
if cmd.CmdSN < sess.ExpCmdSN {
|
||||
err := fmt.Errorf("unexpected cmd serial number: (%d, %d)", cmd.CmdSN, sess.ExpCmdSN)
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
glog.V(1).Infof("add task(%d) into task queue", task.cmd.CmdSN)
|
||||
log.Debugf("add task(%d) into task queue", task.cmd.CmdSN)
|
||||
// add this connection into queue and set this task as pending task
|
||||
task.state = taskPending
|
||||
sess.PendingTasks.Push(task)
|
||||
@@ -794,7 +788,7 @@ func (s *ISCSITargetService) iscsiTaskQueueHandler(task *iscsiTask) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *ISCSITargetService) iscsiExecTask(task *iscsiTask) error {
|
||||
func (s *ISCSITargetDriver) iscsiExecTask(task *iscsiTask) error {
|
||||
cmd := task.cmd
|
||||
switch cmd.OpCode {
|
||||
case OpSCSICmd, OpSCSIOut:
|
||||
|
||||
@@ -18,7 +18,6 @@ limitations under the License.
|
||||
package iscsit
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
@@ -71,15 +70,18 @@ type ISCSIRedirectInfo struct {
|
||||
}
|
||||
|
||||
type iSCSITPGT struct {
|
||||
TPGT uint16 /* Mapping to SCSI Reltive Target Port ID */
|
||||
// Mapping to SCSI Reltive Target Port ID
|
||||
TPGT uint16
|
||||
Portals map[string]struct{}
|
||||
}
|
||||
|
||||
type ISCSITarget struct {
|
||||
api.SCSITarget
|
||||
api.SCSITargetDriverCommon
|
||||
TPGTs map[uint16]*iSCSITPGT /* Key is a TPGT number */
|
||||
Sessions map[uint16]*ISCSISession /* Key is an TSIH */
|
||||
// TPGT number is the key
|
||||
TPGTs map[uint16]*iSCSITPGT
|
||||
// TSIH is the key
|
||||
Sessions map[uint16]*ISCSISession
|
||||
SessionParam []ISCSISessionParam
|
||||
Alias string
|
||||
MaxSessions int
|
||||
@@ -114,8 +116,7 @@ func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
errMsg := fmt.Sprintf("No TPGT found with IP(%s)", portal)
|
||||
return 0, errors.New(errMsg)
|
||||
return 0, fmt.Errorf("No TPGT found with IP(%s)", portal)
|
||||
}
|
||||
|
||||
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
||||
|
||||
@@ -192,33 +192,8 @@ func (tq *taskQueue) Pop() interface{} {
|
||||
return item
|
||||
}
|
||||
|
||||
// The BHS is 48 bytes long. The Opcode and DataSegmentLength fields
|
||||
// appear in all iSCSI PDUs. In addition, when used, the Initiator Task
|
||||
// Tag and Logical Unit Number always appear in the same location in the
|
||||
// header.
|
||||
type iscsiHeader struct {
|
||||
opcode uint8
|
||||
flags uint8 // Final bit
|
||||
rsvd2 [2]uint8
|
||||
hlength uint8 // AHSs total length
|
||||
dlength [3]uint8 // Data length
|
||||
lun [8]uint8
|
||||
itt uint8 // Initiator Task Tag
|
||||
ttt uint8 // Target Task Tag
|
||||
statsn uint8
|
||||
expStatSN uint8
|
||||
maxStatSN uint8
|
||||
other [12]uint8
|
||||
}
|
||||
|
||||
type iscsiPdu struct {
|
||||
bhs iscsiHeader
|
||||
ahsSize uint
|
||||
dataSize uint
|
||||
}
|
||||
|
||||
// New creates a new session.
|
||||
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection, isid uint64) (*ISCSISession, error) {
|
||||
func (s *ISCSITargetDriver) NewISCSISession(conn *iscsiConnection, isid uint64) (*ISCSISession, error) {
|
||||
var (
|
||||
target *ISCSITarget
|
||||
tsih uint64
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
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 port
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/config"
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
)
|
||||
|
||||
type SCSITargetService interface {
|
||||
Run() error
|
||||
NewTarget(string, *config.Config) (SCSITargetDriver, error)
|
||||
}
|
||||
|
||||
type TargetServiceFunc func(*scsi.SCSITargetService) (SCSITargetService, error)
|
||||
|
||||
var registeredPlugins = map[string](TargetServiceFunc){}
|
||||
|
||||
func RegisterTargetService(name string, f TargetServiceFunc) {
|
||||
registeredPlugins[name] = f
|
||||
}
|
||||
|
||||
func NewTargetService(targetDriverName string, s *scsi.SCSITargetService) (SCSITargetService, error) {
|
||||
if targetDriverName == "" {
|
||||
return nil, nil
|
||||
}
|
||||
targetInitFunc, ok := registeredPlugins[targetDriverName]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("SCSI target driver %s is not found.", targetDriverName)
|
||||
}
|
||||
return targetInitFunc(s)
|
||||
}
|
||||
@@ -17,9 +17,13 @@ limitations under the License.
|
||||
package scsi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
type BaseBackingStore struct {
|
||||
@@ -30,43 +34,161 @@ type BaseBackingStore struct {
|
||||
|
||||
type BackingStoreFunc func() (api.BackingStore, error)
|
||||
|
||||
var registeredPlugins = map[string](BackingStoreFunc){}
|
||||
var registeredBSPlugins = map[string](BackingStoreFunc){}
|
||||
|
||||
func RegisterBackingStore(name string, f BackingStoreFunc) {
|
||||
registeredPlugins[name] = f
|
||||
registeredBSPlugins[name] = f
|
||||
}
|
||||
|
||||
func NewBackingStore(name string) (api.BackingStore, error) {
|
||||
if name == "" {
|
||||
return nil, nil
|
||||
}
|
||||
f, ok := registeredPlugins[name]
|
||||
f, ok := registeredBSPlugins[name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("BackingStore %s is not found.", name)
|
||||
return nil, fmt.Errorf("Backend storage %s is not found.", name)
|
||||
}
|
||||
return f()
|
||||
}
|
||||
|
||||
type fakeBackingStore struct {
|
||||
BaseBackingStore
|
||||
}
|
||||
func bsPerformCommand(bs api.BackingStore, cmd *api.SCSICommand) (err error) {
|
||||
var (
|
||||
scb = cmd.SCB.Bytes()
|
||||
offset = cmd.Offset
|
||||
opcode = api.SCSICommandType(scb[0])
|
||||
lu = cmd.Device
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_INVALID_FIELD_IN_CDB
|
||||
wbuf []byte = []byte{}
|
||||
tl int64 = int64(cmd.TL)
|
||||
rbuf = make([]byte, tl)
|
||||
length int
|
||||
doVerify bool = false
|
||||
doWrite bool = false
|
||||
)
|
||||
switch opcode {
|
||||
case api.ORWRITE_16:
|
||||
tmpbuf := []byte{}
|
||||
tmpbuf, err = bs.Read(int64(offset), tl)
|
||||
if err != nil {
|
||||
key = MEDIUM_ERROR
|
||||
asc = ASC_READ_ERROR
|
||||
break
|
||||
}
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(tmpbuf)
|
||||
|
||||
func (fake *fakeBackingStore) Open(dev *api.SCSILu, path string) error {
|
||||
return nil
|
||||
}
|
||||
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
doWrite = true
|
||||
goto write
|
||||
case api.COMPARE_AND_WRITE:
|
||||
// TODO
|
||||
doWrite = true
|
||||
goto write
|
||||
case api.SYNCHRONIZE_CACHE, api.SYNCHRONIZE_CACHE_16:
|
||||
if err = bs.DataSync(); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
break
|
||||
case api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16:
|
||||
doVerify = true
|
||||
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16:
|
||||
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
doWrite = true
|
||||
goto write
|
||||
case api.WRITE_SAME, api.WRITE_SAME_16:
|
||||
// TODO
|
||||
break
|
||||
case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
|
||||
rbuf, err = bs.Read(int64(offset), tl)
|
||||
if err != nil && err != io.EOF {
|
||||
key = MEDIUM_ERROR
|
||||
asc = ASC_READ_ERROR
|
||||
break
|
||||
}
|
||||
length = len(rbuf)
|
||||
for i := 0; i < int(tl)-length; i++ {
|
||||
rbuf = append(rbuf, 0)
|
||||
}
|
||||
|
||||
func (fake *fakeBackingStore) Close(dev *api.SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
if (opcode != api.READ_6) && (scb[1]&0x10 != 0) {
|
||||
bs.DataAdvise(int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
|
||||
}
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(rbuf)
|
||||
case api.PRE_FETCH_10, api.PRE_FETCH_16:
|
||||
err = bs.DataAdvise(int64(offset), tl, util.POSIX_FADV_WILLNEED)
|
||||
if err != nil {
|
||||
key = MEDIUM_ERROR
|
||||
asc = ASC_READ_ERROR
|
||||
}
|
||||
case api.VERIFY_10, api.VERIFY_12, api.VERIFY_16:
|
||||
doVerify = true
|
||||
goto verify
|
||||
case api.UNMAP:
|
||||
// TODO
|
||||
default:
|
||||
break
|
||||
}
|
||||
write:
|
||||
if doWrite {
|
||||
// hack: wbuf = []byte("hello world!")
|
||||
err = bs.Write(wbuf, int64(offset))
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
key = MEDIUM_ERROR
|
||||
asc = ASC_READ_ERROR
|
||||
goto sense
|
||||
}
|
||||
log.Debugf("write data at %d for length %d", offset, len(wbuf))
|
||||
var pg *api.ModePage
|
||||
for _, p := range lu.ModePages {
|
||||
if p.PageCode == 0x08 && p.SubPageCode == 0 {
|
||||
pg = &p
|
||||
break
|
||||
}
|
||||
}
|
||||
if pg == nil {
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_INVALID_FIELD_IN_CDB
|
||||
goto sense
|
||||
}
|
||||
if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) {
|
||||
if err = bs.DataSync(); err != nil {
|
||||
key = MEDIUM_ERROR
|
||||
asc = ASC_READ_ERROR
|
||||
goto sense
|
||||
}
|
||||
}
|
||||
|
||||
func (fake *fakeBackingStore) Init(dev *api.SCSILu, Opts string) error {
|
||||
if (opcode != api.WRITE_6) && (scb[1]&0x10 != 0) {
|
||||
bs.DataAdvise(int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
|
||||
}
|
||||
}
|
||||
verify:
|
||||
if doVerify {
|
||||
rbuf, err = bs.Read(int64(offset), tl)
|
||||
if err != nil {
|
||||
key = MEDIUM_ERROR
|
||||
asc = ASC_READ_ERROR
|
||||
goto sense
|
||||
}
|
||||
if !bytes.Equal(cmd.OutSDBBuffer.Buffer.Bytes(), rbuf) {
|
||||
err = fmt.Errorf("verify fail between out buffer and read buffer")
|
||||
key = MISCOMPARE
|
||||
asc = ASC_MISCOMPARE_DURING_VERIFY_OPERATION
|
||||
goto sense
|
||||
}
|
||||
if scb[1]&0x10 != 0 {
|
||||
bs.DataAdvise(int64(offset), int64(length), util.POSIX_FADV_WILLNEED)
|
||||
}
|
||||
}
|
||||
log.Infof("io done %s", string(scb))
|
||||
return nil
|
||||
}
|
||||
sense:
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
func (fake *fakeBackingStore) Exit(dev *api.SCSILu) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (fake *fakeBackingStore) CommandSubmit(cmd *api.SCSICommand) error {
|
||||
return nil
|
||||
err = fmt.Errorf("sense data encounter, key: %v, asc: %v", key, asc)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -14,12 +14,5 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
package iscsit
|
||||
|
||||
type ISCSIPortal struct {
|
||||
Address string
|
||||
Port int
|
||||
Tpgt int
|
||||
Fd int
|
||||
Af int
|
||||
}
|
||||
// Package backingstore provides the plugin to connect backend storage.
|
||||
package backingstore
|
||||
@@ -17,30 +17,32 @@ limitations under the License.
|
||||
package backingstore
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/golang/glog"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
)
|
||||
|
||||
const (
|
||||
FileBackingStorage = "file"
|
||||
)
|
||||
|
||||
func init() {
|
||||
scsi.RegisterBackingStore("file", new)
|
||||
scsi.RegisterBackingStore(FileBackingStorage, new)
|
||||
}
|
||||
|
||||
type FileBackingStore struct {
|
||||
scsi.BaseBackingStore
|
||||
File *os.File
|
||||
file *os.File
|
||||
}
|
||||
|
||||
func new() (api.BackingStore, error) {
|
||||
return &FileBackingStore{
|
||||
BaseBackingStore: scsi.BaseBackingStore{
|
||||
Name: "file",
|
||||
Name: FileBackingStorage,
|
||||
DataSize: 0,
|
||||
OflagsSupported: 0,
|
||||
},
|
||||
@@ -57,12 +59,12 @@ func (bs *FileBackingStore) Open(dev *api.SCSILu, path string) error {
|
||||
|
||||
f, err := os.OpenFile(path, os.O_RDWR, os.ModePerm)
|
||||
|
||||
bs.File = f
|
||||
bs.file = f
|
||||
return err
|
||||
}
|
||||
|
||||
func (bs *FileBackingStore) Close(dev *api.SCSILu) error {
|
||||
return bs.File.Close()
|
||||
return bs.file.Close()
|
||||
}
|
||||
|
||||
func (bs *FileBackingStore) Init(dev *api.SCSILu, Opts string) error {
|
||||
@@ -76,142 +78,38 @@ func (bs *FileBackingStore) Exit(dev *api.SCSILu) error {
|
||||
func (bs *FileBackingStore) Size(dev *api.SCSILu) uint64 {
|
||||
return bs.DataSize
|
||||
}
|
||||
func (bs *FileBackingStore) CommandSubmit(cmd *api.SCSICommand) (err error) {
|
||||
var (
|
||||
scb = cmd.SCB.Bytes()
|
||||
offset = cmd.Offset
|
||||
opcode = api.SCSICommandType(scb[0])
|
||||
lu = cmd.Device
|
||||
key = scsi.ILLEGAL_REQUEST
|
||||
asc = scsi.ASC_INVALID_FIELD_IN_CDB
|
||||
wbuf []byte = []byte{}
|
||||
rbuf = make([]byte, cmd.TL)
|
||||
length int
|
||||
doVerify bool = false
|
||||
doWrite bool = false
|
||||
)
|
||||
switch opcode {
|
||||
case api.ORWRITE_16:
|
||||
tmpbuf := []byte{}
|
||||
length, err = bs.File.ReadAt(tmpbuf, int64(offset))
|
||||
if length != len(tmpbuf) {
|
||||
key = scsi.MEDIUM_ERROR
|
||||
asc = scsi.ASC_READ_ERROR
|
||||
break
|
||||
}
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(tmpbuf)
|
||||
|
||||
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
doWrite = true
|
||||
goto write
|
||||
case api.COMPARE_AND_WRITE:
|
||||
// TODO
|
||||
doWrite = true
|
||||
goto write
|
||||
case api.SYNCHRONIZE_CACHE, api.SYNCHRONIZE_CACHE_16:
|
||||
if err = util.Fdatasync(bs.File); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
break
|
||||
case api.WRITE_VERIFY, api.WRITE_VERIFY_12, api.WRITE_VERIFY_16:
|
||||
doVerify = true
|
||||
case api.WRITE_6, api.WRITE_10, api.WRITE_12, api.WRITE_16:
|
||||
wbuf = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||
doWrite = true
|
||||
goto write
|
||||
case api.WRITE_SAME, api.WRITE_SAME_16:
|
||||
// TODO
|
||||
break
|
||||
case api.READ_6, api.READ_10, api.READ_12, api.READ_16:
|
||||
length, err = bs.File.ReadAt(rbuf, int64(offset))
|
||||
if err != nil && err != io.EOF {
|
||||
key = scsi.MEDIUM_ERROR
|
||||
asc = scsi.ASC_READ_ERROR
|
||||
break
|
||||
}
|
||||
for i := 0; i < int(cmd.TL)-length; i++ {
|
||||
rbuf = append(rbuf, 0)
|
||||
}
|
||||
|
||||
if (opcode != api.READ_6) && (scb[1]&0x10 != 0) {
|
||||
util.Fadvise(bs.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
|
||||
}
|
||||
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(rbuf)
|
||||
case api.PRE_FETCH_10, api.PRE_FETCH_16:
|
||||
err = util.Fadvise(bs.File, int64(offset), int64(cmd.TL), util.POSIX_FADV_WILLNEED)
|
||||
if err != nil {
|
||||
key = scsi.MEDIUM_ERROR
|
||||
asc = scsi.ASC_READ_ERROR
|
||||
}
|
||||
case api.VERIFY_10, api.VERIFY_12, api.VERIFY_16:
|
||||
doVerify = true
|
||||
goto verify
|
||||
case api.UNMAP:
|
||||
// TODO
|
||||
default:
|
||||
break
|
||||
func (bs *FileBackingStore) Read(offset, tl int64) ([]byte, error) {
|
||||
if bs.file == nil {
|
||||
return nil, fmt.Errorf("Backend store is nil")
|
||||
}
|
||||
write:
|
||||
if doWrite {
|
||||
// hack: wbuf = []byte("hello world!")
|
||||
length, err = bs.File.WriteAt(wbuf, int64(offset))
|
||||
if err != nil || length != len(wbuf) {
|
||||
glog.Error(err)
|
||||
key = scsi.MEDIUM_ERROR
|
||||
asc = scsi.ASC_READ_ERROR
|
||||
goto sense
|
||||
}
|
||||
glog.V(2).Infof("write data at %d for length %d", offset, length)
|
||||
var pg *api.ModePage
|
||||
for _, p := range lu.ModePages {
|
||||
if p.Pcode == 0x08 && p.SubPcode == 0 {
|
||||
pg = &p
|
||||
break
|
||||
}
|
||||
}
|
||||
if pg == nil {
|
||||
key = scsi.ILLEGAL_REQUEST
|
||||
asc = scsi.ASC_INVALID_FIELD_IN_CDB
|
||||
goto sense
|
||||
}
|
||||
if ((opcode != api.WRITE_6) && (scb[1]&0x8 != 0)) || (pg.Data[0]&0x04 == 0) {
|
||||
if err = util.Fdatasync(bs.File); err != nil {
|
||||
key = scsi.MEDIUM_ERROR
|
||||
asc = scsi.ASC_READ_ERROR
|
||||
goto sense
|
||||
}
|
||||
}
|
||||
|
||||
if (opcode != api.WRITE_6) && (scb[1]&0x10 != 0) {
|
||||
util.Fadvise(bs.File, int64(offset), int64(length), util.POSIX_FADV_NOREUSE)
|
||||
}
|
||||
}
|
||||
verify:
|
||||
if doVerify {
|
||||
length, err = bs.File.ReadAt(rbuf, int64(offset))
|
||||
if length != len(rbuf) {
|
||||
key = scsi.MEDIUM_ERROR
|
||||
asc = scsi.ASC_READ_ERROR
|
||||
goto sense
|
||||
}
|
||||
if !bytes.Equal(cmd.OutSDBBuffer.Buffer.Bytes(), rbuf) {
|
||||
err = fmt.Errorf("verify fail between out buffer and read buffer")
|
||||
key = scsi.MISCOMPARE
|
||||
asc = scsi.ASC_MISCOMPARE_DURING_VERIFY_OPERATION
|
||||
goto sense
|
||||
}
|
||||
if scb[1]&0x10 != 0 {
|
||||
util.Fadvise(bs.File, int64(offset), int64(length), util.POSIX_FADV_WILLNEED)
|
||||
}
|
||||
}
|
||||
glog.Infof("io done %s", string(scb))
|
||||
return nil
|
||||
sense:
|
||||
tmpbuf := make([]byte, tl)
|
||||
length, err := bs.file.ReadAt(tmpbuf, offset)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return nil, err
|
||||
}
|
||||
if length != len(tmpbuf) {
|
||||
return nil, fmt.Errorf("read is not same length of length")
|
||||
}
|
||||
return tmpbuf, nil
|
||||
}
|
||||
|
||||
func (bs *FileBackingStore) Write(wbuf []byte, offset int64) error {
|
||||
length, err := bs.file.WriteAt(wbuf, offset)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return err
|
||||
}
|
||||
|
||||
err = fmt.Errorf("sense data encounter, key: %v, asc: %v", key, asc)
|
||||
return err
|
||||
if length != len(wbuf) {
|
||||
return fmt.Errorf("write is not same length of length")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *FileBackingStore) DataSync() error {
|
||||
return util.Fdatasync(bs.file)
|
||||
}
|
||||
|
||||
func (bs *FileBackingStore) DataAdvise(offset, length int64, advise uint32) error {
|
||||
return util.Fadvise(bs.file, offset, length, advise)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -59,7 +59,18 @@ func (bs *NullBackingStore) Size(dev *api.SCSILu) uint64 {
|
||||
return 0
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) CommandSubmit(cmd *api.SCSICommand) error {
|
||||
cmd.Result = api.SAM_STAT_GOOD
|
||||
func (bs *NullBackingStore) Read(offset, tl int64) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) Write(wbuf []byte, offset int64) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) DataSync() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (bs *NullBackingStore) DataAdvise(offset, length int64, advise uint32) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,13 +21,13 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
/* PERSISTENT_RESERVE_IN service action codes */
|
||||
// PERSISTENT_RESERVE_IN service action codes
|
||||
PR_IN_READ_KEYS byte = 0x00
|
||||
PR_IN_READ_RESERVATION byte = 0x01
|
||||
PR_IN_REPORT_CAPABILITIES byte = 0x02
|
||||
PR_IN_READ_FULL_STATUS byte = 0x03
|
||||
|
||||
/* PERSISTENT_RESERVE_OUT service action codes */
|
||||
// PERSISTENT_RESERVE_OUT service action codes
|
||||
PR_OUT_REGISTER byte = 0x00
|
||||
PR_OUT_RESERVE byte = 0x01
|
||||
PR_OUT_RELEASE byte = 0x02
|
||||
@@ -40,7 +40,7 @@ const (
|
||||
// Persistent Reservation scope
|
||||
PR_LU_SCOPE byte = 0x00
|
||||
|
||||
/* Persistent Reservation Type Mask format */
|
||||
// Persistent Reservation Type Mask format
|
||||
PR_TYPE_WRITE_EXCLUSIVE byte = 0x01
|
||||
PR_TYPE_EXCLUSIVE_ACCESS byte = 0x03
|
||||
PR_TYPE_WRITE_EXCLUSIVE_REGONLY byte = 0x05
|
||||
@@ -50,14 +50,22 @@ const (
|
||||
)
|
||||
|
||||
const (
|
||||
CDB_GROUPID_0 = 6 /* 6-byte commands */
|
||||
CDB_GROUPID_1 = 10 /* 10-byte commands */
|
||||
CDB_GROUPID_2 = 10 /* 10-byte commands */
|
||||
CDB_GROUPID_3 = 0 /* reserved */
|
||||
CDB_GROUPID_4 = 16 /* 16-byte commands */
|
||||
CDB_GROUPID_5 = 12 /* 12-byte commands */
|
||||
CDB_GROUPID_6 = 0 /* vendor specific */
|
||||
CDB_GROUPID_7 = 0 /* vendor specific */
|
||||
// 6-byte commands
|
||||
CDB_GROUPID_0 = 6
|
||||
// 10-byte commands
|
||||
CDB_GROUPID_1 = 10
|
||||
// 10-byte commands
|
||||
CDB_GROUPID_2 = 10
|
||||
// reserved
|
||||
CDB_GROUPID_3 = 0
|
||||
// 16-byte commands
|
||||
CDB_GROUPID_4 = 16
|
||||
// 12-byte commands
|
||||
CDB_GROUPID_5 = 12
|
||||
// vendor specific
|
||||
CDB_GROUPID_6 = 0
|
||||
// vendor specific
|
||||
CDB_GROUPID_7 = 0
|
||||
)
|
||||
|
||||
func SCSICDBGroupID(opcode byte) byte {
|
||||
@@ -106,7 +114,6 @@ const (
|
||||
* 2 - Designator field contains ASCII printable chars
|
||||
* 3 - Designaotor field contains UTF-8
|
||||
*/
|
||||
|
||||
const (
|
||||
INQ_CODE_BIN = byte(1)
|
||||
INQ_CODE_ASCII = byte(2)
|
||||
@@ -121,7 +128,6 @@ const (
|
||||
* 10b - Associated with SCSI Target device
|
||||
* 11b - Reserved
|
||||
*/
|
||||
|
||||
const (
|
||||
ASS_LU = byte(0x00)
|
||||
ASS_TGT_PORT = byte(0x01)
|
||||
@@ -147,7 +153,6 @@ const (
|
||||
* device type values are reserved for this peripheral qualifier.
|
||||
* 100b to 111b Vendor specific
|
||||
*/
|
||||
|
||||
const (
|
||||
PQ_DEVICE_CONNECTED = byte(0x00 << 5)
|
||||
PQ_DEVICE_NOT_CONNECT = byte(0x01 << 5)
|
||||
@@ -204,7 +209,6 @@ const (
|
||||
* 7 - MD5 logical unit identifier - 7.6.3.10
|
||||
* 8 - SCSI name string - 7.6.3.11
|
||||
*/
|
||||
|
||||
const (
|
||||
DESG_VENDOR = iota
|
||||
DESG_T10
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -16,3 +16,33 @@ limitations under the License.
|
||||
|
||||
// Target Driver Interface
|
||||
package scsi
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gostor/gotgt/pkg/config"
|
||||
)
|
||||
|
||||
type SCSITargetDriver interface {
|
||||
Run() error
|
||||
NewTarget(string, *config.Config) error
|
||||
}
|
||||
|
||||
type TargetDriverFunc func(*SCSITargetService) (SCSITargetDriver, error)
|
||||
|
||||
var registeredPlugins = map[string](TargetDriverFunc){}
|
||||
|
||||
func RegisterTargetDriver(name string, f TargetDriverFunc) {
|
||||
registeredPlugins[name] = f
|
||||
}
|
||||
|
||||
func NewTargetDriver(tgtDriver string, s *SCSITargetService) (SCSITargetDriver, error) {
|
||||
if tgtDriver == "" {
|
||||
return nil, nil
|
||||
}
|
||||
targetInitFunc, ok := registeredPlugins[tgtDriver]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("SCSI target driver %s is not found.", tgtDriver)
|
||||
}
|
||||
return targetInitFunc(s)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -20,7 +20,7 @@ import "errors"
|
||||
|
||||
type SCSIError struct {
|
||||
errno byte
|
||||
Err error
|
||||
err error
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -58,7 +58,7 @@ var (
|
||||
type SCSISubError uint16
|
||||
|
||||
var (
|
||||
/* Key 0: No Sense Errors */
|
||||
// Key 0: No Sense Errors
|
||||
NO_ADDITIONAL_SENSE SCSISubError = 0x0000
|
||||
ASC_MARK SCSISubError = 0x0001
|
||||
ASC_EOM SCSISubError = 0x0002
|
||||
@@ -67,14 +67,14 @@ var (
|
||||
ASC_OP_IN_PROGRESS SCSISubError = 0x0016
|
||||
ASC_DRIVE_REQUIRES_CLEANING SCSISubError = 0x8282
|
||||
|
||||
/* Key 1: Recovered Errors */
|
||||
// Key 1: Recovered Errors
|
||||
ASC_WRITE_ERROR SCSISubError = 0x0c00
|
||||
ASC_READ_ERROR SCSISubError = 0x1100
|
||||
ASC_RECOVERED_WITH_RETRYS SCSISubError = 0x1701
|
||||
ASC_MEDIA_LOAD_EJECT_ERROR SCSISubError = 0x5300
|
||||
ASC_FAILURE_PREDICTION SCSISubError = 0x5d00
|
||||
|
||||
/* Key 2: Not ready */
|
||||
// Key 2: Not ready
|
||||
ASC_CAUSE_NOT_REPORTABLE SCSISubError = 0x0400
|
||||
ASC_BECOMING_READY SCSISubError = 0x0401
|
||||
ASC_INITIALIZING_REQUIRED SCSISubError = 0x0402
|
||||
@@ -83,7 +83,7 @@ var (
|
||||
ASC_MEDIUM_NOT_PRESENT SCSISubError = 0x3a00
|
||||
ASC_LOGICAL_UNIT_NOT_CONFIG SCSISubError = 0x3e00
|
||||
|
||||
/* Key 3: Medium Errors */
|
||||
// Key 3: Medium Errors
|
||||
ASC_UNRECOVERED_READ SCSISubError = 0x1100
|
||||
ASC_RECORDED_ENTITY_NOT_FOUND SCSISubError = 0x1400
|
||||
ASC_UNKNOWN_FORMAT SCSISubError = 0x3001
|
||||
@@ -94,7 +94,7 @@ var (
|
||||
ASC_CARTRIDGE_FAULT SCSISubError = 0x5200
|
||||
ASC_MEDIA_LOAD_OR_EJECT_FAILED SCSISubError = 0x5300
|
||||
|
||||
/* Key 4: Hardware Failure */
|
||||
// Key 4: Hardware Failure
|
||||
ASC_COMPRESSION_CHECK SCSISubError = 0x0c04
|
||||
ASC_DECOMPRESSION_CRC SCSISubError = 0x110d
|
||||
ASC_MECHANICAL_POSITIONING_ERROR SCSISubError = 0x1501
|
||||
@@ -103,7 +103,7 @@ var (
|
||||
ASC_INTERNAL_TGT_FAILURE SCSISubError = 0x4400
|
||||
ASC_ERASE_FAILURE SCSISubError = 0x5100
|
||||
|
||||
/* Key 5: Illegal Request */
|
||||
// Key 5: Illegal Request
|
||||
ASC_PARAMETER_LIST_LENGTH_ERR SCSISubError = 0x1a00
|
||||
ASC_INVALID_OP_CODE SCSISubError = 0x2000
|
||||
ASC_LBA_OUT_OF_RANGE SCSISubError = 0x2100
|
||||
@@ -120,7 +120,7 @@ var (
|
||||
ASC_INSUFFICENT_REGISTRATION_RESOURCES SCSISubError = 0x5504
|
||||
ASC_BAD_MICROCODE_DETECTED SCSISubError = 0x8283
|
||||
|
||||
/* Key 6: Unit Attention */
|
||||
// Key 6: Unit Attention
|
||||
ASC_NOT_READY_TO_TRANSITION SCSISubError = 0x2800
|
||||
ASC_POWERON_RESET SCSISubError = 0x2900
|
||||
ASC_I_T_NEXUS_LOSS_OCCURRED SCSISubError = 0x2907
|
||||
@@ -134,10 +134,10 @@ var (
|
||||
ASC_REPORTED_LUNS_DATA_HAS_CHANGED SCSISubError = 0x3f0e
|
||||
ASC_FAILURE_PREDICTION_FALSE SCSISubError = 0x5dff
|
||||
|
||||
/* Data Protect */
|
||||
// Data Protect
|
||||
ASC_WRITE_PROTECT SCSISubError = 0x2700
|
||||
ASC_MEDIUM_OVERWRITE_ATTEMPTED SCSISubError = 0x300c
|
||||
|
||||
/* Miscompare */
|
||||
// Miscompare
|
||||
ASC_MISCOMPARE_DURING_VERIFY_OPERATION SCSISubError = 0x1d00
|
||||
)
|
||||
|
||||
@@ -22,10 +22,8 @@ import (
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
)
|
||||
|
||||
/*
|
||||
* path format <protocol>:/absolute/file/path
|
||||
*/
|
||||
|
||||
// NewSCSILu: create a new SCSI LU
|
||||
// path format <protocol>:/absolute/file/path
|
||||
func NewSCSILu(device_uuid uint64, path string, online bool) (*api.SCSILu, error) {
|
||||
|
||||
pathinfo := strings.SplitN(path, ":", 2)
|
||||
@@ -60,6 +58,7 @@ func NewSCSILu(device_uuid uint64, path string, online bool) (*api.SCSILu, error
|
||||
return lu, nil
|
||||
}
|
||||
|
||||
// NewLUN0: create a new fake SCSI LU
|
||||
func NewLUN0() *api.SCSILu {
|
||||
|
||||
sbc := NewSBCDevice(api.TYPE_UNKNOWN)
|
||||
@@ -88,7 +87,7 @@ func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
|
||||
fn := cmd.Device.DeviceProtocol.PerformCommand(op)
|
||||
if fn != nil {
|
||||
fnop := fn.(SCSIDeviceOperation)
|
||||
// host := cmd.ITNexus.Host
|
||||
// TODO host := cmd.ITNexus.Host
|
||||
host := 0
|
||||
return fnop.CommandPerformFunc(host, cmd)
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"encoding/binary"
|
||||
"unsafe"
|
||||
|
||||
"github.com/golang/glog"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
"github.com/gostor/gotgt/pkg/version"
|
||||
@@ -66,7 +66,7 @@ func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
|
||||
lu.Attrs.SenseFormat = false
|
||||
lu.Attrs.VendorID = SCSIVendorID
|
||||
lu.Attrs.ProductID = SCSIProductID
|
||||
lu.Attrs.ProductRev = version.SCSI_VERSION
|
||||
lu.Attrs.ProductRev = version.SCSIVersion
|
||||
|
||||
/*
|
||||
SCSIID for PAGE83 T10 VENDOR IDENTIFICATION field
|
||||
@@ -331,7 +331,7 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
if dev.Attrs.Removable && !dev.Attrs.Online {
|
||||
key = NOT_READY
|
||||
asc = ASC_MEDIUM_NOT_PRESENT
|
||||
glog.Warningf("sense")
|
||||
log.Warnf("sense")
|
||||
goto sense
|
||||
}
|
||||
|
||||
@@ -343,7 +343,7 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
if scb[1]&0xe0 != 0 {
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_INVALID_FIELD_IN_CDB
|
||||
glog.Warningf("sense")
|
||||
log.Warnf("sense")
|
||||
goto sense
|
||||
}
|
||||
*/
|
||||
@@ -384,7 +384,7 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
api.PRE_FETCH_10, api.PRE_FETCH_16, api.COMPARE_AND_WRITE:
|
||||
key = DATA_PROTECT
|
||||
asc = ASC_WRITE_PROTECT
|
||||
glog.Warningf("sense")
|
||||
log.Warnf("sense")
|
||||
goto sense
|
||||
}
|
||||
}
|
||||
@@ -397,14 +397,14 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
if lba+uint64(tl) < lba || lba+uint64(tl) > dev.Size>>dev.BlockShift {
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_LBA_OUT_OF_RANGE
|
||||
glog.Warningf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift)
|
||||
log.Warnf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift)
|
||||
goto sense
|
||||
}
|
||||
} else {
|
||||
if lba >= dev.Size>>dev.BlockShift {
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_LBA_OUT_OF_RANGE
|
||||
glog.Warningf("sense")
|
||||
log.Warnf("sense")
|
||||
goto sense
|
||||
}
|
||||
}
|
||||
@@ -435,9 +435,9 @@ func SBCReadWrite(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
*/
|
||||
}
|
||||
|
||||
err = dev.Storage.CommandSubmit(cmd)
|
||||
err = bsPerformCommand(dev.Storage, cmd)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
key = HARDWARE_ERROR
|
||||
asc = ASC_INTERNAL_TGT_FAILURE
|
||||
} else {
|
||||
@@ -532,8 +532,13 @@ func SBCVerify(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
var (
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_INVALID_FIELD_IN_CDB
|
||||
dev = cmd.Device
|
||||
scb = cmd.SCB.Bytes()
|
||||
lba uint64
|
||||
tl uint32
|
||||
err error
|
||||
)
|
||||
if cmd.Device.Attrs.Removable && !cmd.Device.Attrs.Online {
|
||||
if dev.Attrs.Removable && !dev.Attrs.Online {
|
||||
key = NOT_READY
|
||||
asc = ASC_MEDIUM_NOT_PRESENT
|
||||
goto sense
|
||||
@@ -550,7 +555,36 @@ func SBCVerify(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
// no data compare with the media
|
||||
return api.SAMStatGood
|
||||
}
|
||||
// TODO
|
||||
lba = getSCSIReadWriteOffset(scb)
|
||||
tl = getSCSIReadWriteCount(scb)
|
||||
|
||||
// Verify that we are not doing i/o beyond the end-of-lun
|
||||
if tl != 0 {
|
||||
if lba+uint64(tl) < lba || lba+uint64(tl) > dev.Size>>dev.BlockShift {
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_LBA_OUT_OF_RANGE
|
||||
log.Warnf("sense: lba: %d, tl: %d, size: %d", lba, tl, dev.Size>>dev.BlockShift)
|
||||
goto sense
|
||||
}
|
||||
} else {
|
||||
if lba >= dev.Size>>dev.BlockShift {
|
||||
key = ILLEGAL_REQUEST
|
||||
asc = ASC_LBA_OUT_OF_RANGE
|
||||
log.Warnf("sense")
|
||||
goto sense
|
||||
}
|
||||
}
|
||||
|
||||
cmd.Offset = lba << dev.BlockShift
|
||||
cmd.TL = tl << dev.BlockShift
|
||||
err = bsPerformCommand(dev.Storage, cmd)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
key = HARDWARE_ERROR
|
||||
asc = ASC_INTERNAL_TGT_FAILURE
|
||||
} else {
|
||||
return api.SAMStatGood
|
||||
}
|
||||
sense:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, key, asc)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -23,7 +23,7 @@ import (
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/golang/glog"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
@@ -80,21 +80,21 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro
|
||||
lun := *(*uint64)(unsafe.Pointer(&scmd.Lun))
|
||||
scmd.Device = target.Devices[lun]
|
||||
|
||||
glog.V(2).Infof("scsi opcode: 0x%x, LUN: %d:", int(scmd.SCB.Bytes()[0]), binary.LittleEndian.Uint64(scmd.Lun[:]))
|
||||
log.Debugf("scsi opcode: 0x%x, LUN: %d", int(scmd.SCB.Bytes()[0]), binary.LittleEndian.Uint64(scmd.Lun[:]))
|
||||
|
||||
if scmd.Device == nil {
|
||||
scmd.Device = target.LUN0
|
||||
if lun != 0 {
|
||||
BuildSenseData(scmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
scmd.Result = api.SAMStatCheckCondition.Stat
|
||||
glog.Warningf("%v", api.SAMStatCheckCondition.Err)
|
||||
log.Warnf("%v", api.SAMStatCheckCondition.Err)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
result := scmd.Device.PerformCommand(tid, scmd)
|
||||
if result != api.SAMStatGood {
|
||||
scmd.Result = result.Stat
|
||||
glog.Warningf("%v", result.Err)
|
||||
log.Warnf("%v", result.Err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -162,7 +162,7 @@ func BuildSenseData(cmd *api.SCSICommand, key byte, asc SCSISubError) {
|
||||
senseBuffer.Truncate(int(inBufLen))
|
||||
}
|
||||
} else {
|
||||
glog.V(2).Infof("cannot calc cbd alloc length. truncate failed")
|
||||
log.Debugf("cannot calc cbd alloc length. truncate failed")
|
||||
}
|
||||
cmd.Result = key
|
||||
cmd.SenseBuffer = senseBuffer
|
||||
|
||||
@@ -1,3 +1,19 @@
|
||||
/*
|
||||
Copyright 2016 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 (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -17,7 +17,7 @@ limitations under the License.
|
||||
package scsi
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"sync"
|
||||
|
||||
@@ -28,9 +28,11 @@ import (
|
||||
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*/
|
||||
mutex sync.RWMutex
|
||||
// use UUID as the key for all LUs
|
||||
AllDevices api.LUNMap
|
||||
// use target name as the key for target's LUN map
|
||||
TargetsLUNMap map[string]api.LUNMap
|
||||
}
|
||||
|
||||
var globalSCSILUMap = SCSILUMap{AllDevices: make(api.LUNMap), TargetsLUNMap: make(map[string]api.LUNMap)}
|
||||
@@ -73,7 +75,7 @@ func InitSCSILUMap(config *config.Config) error {
|
||||
for _, bs := range config.Storages {
|
||||
lu, err := NewSCSILu(bs.DeviceID, bs.Path, bs.Online)
|
||||
if err != nil {
|
||||
return errors.New("Init SCSI LU map error.")
|
||||
return fmt.Errorf("Init SCSI LU map error: %v", err)
|
||||
}
|
||||
globalSCSILUMap.AllDevices[bs.DeviceID] = lu
|
||||
}
|
||||
@@ -82,10 +84,10 @@ func InitSCSILUMap(config *config.Config) error {
|
||||
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")
|
||||
return fmt.Errorf("LU Number must be a number")
|
||||
}
|
||||
mappingLUN(deviceID, lun, tgtName)
|
||||
//Init SCSISimpleReservationOperator
|
||||
// Init SCSISimpleReservationOperator
|
||||
op := GetSCSIReservationOperator()
|
||||
if simpleOp, ok = op.(*SCSISimpleReservationOperator); ok {
|
||||
simpleOp.InitLUReservation(tgtName, deviceID)
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/golang/glog"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
"github.com/satori/go.uuid"
|
||||
@@ -341,7 +341,7 @@ func SPCReportLuns(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
// Get Allocation Length
|
||||
allocationLength = util.GetUnalignedUint32(scb.Bytes()[6:10])
|
||||
if allocationLength < 16 {
|
||||
glog.Warningf("goto sense, allocationLength < 16")
|
||||
log.Warn("goto sense, allocationLength < 16")
|
||||
goto sense
|
||||
}
|
||||
|
||||
@@ -577,24 +577,24 @@ func SPCReportSupportedOperationCodes(host int, cmd *api.SCSICommand) api.SAMSta
|
||||
rsa := util.GetUnalignedUint16(scb[4:])
|
||||
switch reporting_options {
|
||||
case 0x00: /* report all */
|
||||
glog.V(3).Infof("Service Action: report all")
|
||||
log.Debugf("Service Action: report all")
|
||||
err := reportOpcodesAll(cmd, rctd)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
goto sense
|
||||
}
|
||||
case 0x01: /* report one no service action*/
|
||||
glog.V(3).Infof("Service Action: report one no service action")
|
||||
log.Debugf("Service Action: report one no service action")
|
||||
err := reportOpcodeOne(cmd, rctd, opcode, rsa, false)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
goto sense
|
||||
}
|
||||
case 0x02: /* report one service action */
|
||||
glog.V(3).Infof("Service Action: report one service action")
|
||||
log.Debugf("Service Action: report one service action")
|
||||
err := reportOpcodeOne(cmd, rctd, opcode, rsa, true)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
log.Error(err)
|
||||
goto sense
|
||||
}
|
||||
default:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -20,7 +20,7 @@ import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
"github.com/golang/glog"
|
||||
log "github.com/Sirupsen/logrus"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
@@ -97,12 +97,12 @@ func deviceReserve(cmd *api.SCSICommand) error {
|
||||
}
|
||||
}
|
||||
if lu == nil {
|
||||
glog.Errorf("invalid target and lun %d %s", cmd.Target.TID, lun)
|
||||
log.Errorf("invalid target and lun %d %s", cmd.Target.TID, lun)
|
||||
return nil
|
||||
}
|
||||
|
||||
if !uuid.Equal(lu.ReserveID, uuid.Nil) && uuid.Equal(lu.ReserveID, cmd.ITNexusID) {
|
||||
glog.Errorf("already reserved %d, %d", lu.ReserveID, cmd.ITNexusID)
|
||||
log.Errorf("already reserved %d, %d", lu.ReserveID, cmd.ITNexusID)
|
||||
return fmt.Errorf("already reserved")
|
||||
}
|
||||
lu.ReserveID = cmd.ITNexusID
|
||||
|
||||
@@ -14,6 +14,7 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package util provides some basic util functions.
|
||||
package util
|
||||
|
||||
import (
|
||||
@@ -121,9 +122,9 @@ const (
|
||||
POSIX_FADV_NOREUSE
|
||||
)
|
||||
|
||||
func Fadvise(file *os.File, off, length int64, advice uint32) error {
|
||||
func Fadvise(file *os.File, off, length int64, advise uint32) error {
|
||||
// syscall.SYS_FADVISE64 = 221
|
||||
_, _, err := syscall.Syscall6(221, file.Fd(), uintptr(off), uintptr(length), uintptr(advice), 0, 0)
|
||||
_, _, err := syscall.Syscall6(221, file.Fd(), uintptr(off), uintptr(length), uintptr(advise), 0, 0)
|
||||
if err != 0 {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2015 The GoStor Authors All rights reserved.
|
||||
Copyright 2016 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.
|
||||
@@ -14,9 +14,11 @@ See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package version provides the Version information.
|
||||
package version
|
||||
|
||||
const (
|
||||
VERSION = "0.1"
|
||||
SCSI_VERSION = "0.1" /* SCSI version string MUST be shorter than 4 characters*/
|
||||
Version = "0.1"
|
||||
// SCSI version string MUST be shorter than 4 characters
|
||||
SCSIVersion = "0.1"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user