21
Godeps/_workspace/src/github.com/satori/go.uuid/.travis.yml
generated
vendored
Normal file
21
Godeps/_workspace/src/github.com/satori/go.uuid/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
language: go
|
||||||
|
sudo: false
|
||||||
|
go:
|
||||||
|
- 1.2
|
||||||
|
- 1.3
|
||||||
|
- 1.4
|
||||||
|
- 1.5
|
||||||
|
- 1.6
|
||||||
|
- 1.7
|
||||||
|
- tip
|
||||||
|
matrix:
|
||||||
|
allow_failures:
|
||||||
|
- go: tip
|
||||||
|
fast_finish: true
|
||||||
|
before_install:
|
||||||
|
- go get github.com/mattn/goveralls
|
||||||
|
- go get golang.org/x/tools/cmd/cover
|
||||||
|
script:
|
||||||
|
- $HOME/gopath/bin/goveralls -service=travis-ci
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
20
Godeps/_workspace/src/github.com/satori/go.uuid/LICENSE
generated
vendored
Normal file
20
Godeps/_workspace/src/github.com/satori/go.uuid/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
Copyright (C) 2013-2016 by Maxim Bublis <b@codemonkey.ru>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of this software and associated documentation files (the
|
||||||
|
"Software"), to deal in the Software without restriction, including
|
||||||
|
without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be
|
||||||
|
included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
65
Godeps/_workspace/src/github.com/satori/go.uuid/README.md
generated
vendored
Normal file
65
Godeps/_workspace/src/github.com/satori/go.uuid/README.md
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
# UUID package for Go language
|
||||||
|
|
||||||
|
[](https://travis-ci.org/satori/go.uuid)
|
||||||
|
[](https://coveralls.io/github/satori/go.uuid)
|
||||||
|
[](http://godoc.org/github.com/satori/go.uuid)
|
||||||
|
|
||||||
|
This package provides pure Go implementation of Universally Unique Identifier (UUID). Supported both creation and parsing of UUIDs.
|
||||||
|
|
||||||
|
With 100% test coverage and benchmarks out of box.
|
||||||
|
|
||||||
|
Supported versions:
|
||||||
|
* Version 1, based on timestamp and MAC address (RFC 4122)
|
||||||
|
* Version 2, based on timestamp, MAC address and POSIX UID/GID (DCE 1.1)
|
||||||
|
* Version 3, based on MD5 hashing (RFC 4122)
|
||||||
|
* Version 4, based on random numbers (RFC 4122)
|
||||||
|
* Version 5, based on SHA-1 hashing (RFC 4122)
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
Use the `go` command:
|
||||||
|
|
||||||
|
$ go get github.com/satori/go.uuid
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
UUID package requires Go >= 1.2.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Creating UUID Version 4
|
||||||
|
u1 := uuid.NewV4()
|
||||||
|
fmt.Printf("UUIDv4: %s\n", u1)
|
||||||
|
|
||||||
|
// Parsing UUID from string input
|
||||||
|
u2, err := uuid.FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Something gone wrong: %s", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Successfully parsed: %s", u2)
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
[Documentation](http://godoc.org/github.com/satori/go.uuid) is hosted at GoDoc project.
|
||||||
|
|
||||||
|
## Links
|
||||||
|
* [RFC 4122](http://tools.ietf.org/html/rfc4122)
|
||||||
|
* [DCE 1.1: Authentication and Security Services](http://pubs.opengroup.org/onlinepubs/9696989899/chap5.htm#tagcjh_08_02_01_01)
|
||||||
|
|
||||||
|
## Copyright
|
||||||
|
|
||||||
|
Copyright (C) 2013-2016 by Maxim Bublis <b@codemonkey.ru>.
|
||||||
|
|
||||||
|
UUID package released under MIT License.
|
||||||
|
See [LICENSE](https://github.com/satori/go.uuid/blob/master/LICENSE) for details.
|
||||||
123
Godeps/_workspace/src/github.com/satori/go.uuid/benchmarks_test.go
generated
vendored
Normal file
123
Godeps/_workspace/src/github.com/satori/go.uuid/benchmarks_test.go
generated
vendored
Normal file
@@ -0,0 +1,123 @@
|
|||||||
|
// Copyright (C) 2013-2015 by Maxim Bublis <b@codemonkey.ru>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func BenchmarkFromBytes(b *testing.B) {
|
||||||
|
bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
FromBytes(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFromString(b *testing.B) {
|
||||||
|
s := "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
FromString(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFromStringUrn(b *testing.B) {
|
||||||
|
s := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
FromString(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkFromStringWithBrackets(b *testing.B) {
|
||||||
|
s := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}"
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
FromString(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewV1(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
NewV1()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewV2(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
NewV2(DomainPerson)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewV3(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
NewV3(NamespaceDNS, "www.example.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewV4(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
NewV4()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkNewV5(b *testing.B) {
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
NewV5(NamespaceDNS, "www.example.com")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMarshalBinary(b *testing.B) {
|
||||||
|
u := NewV4()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
u.MarshalBinary()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMarshalText(b *testing.B) {
|
||||||
|
u := NewV4()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
u.MarshalText()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUnmarshalBinary(b *testing.B) {
|
||||||
|
bytes := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
u := UUID{}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
u.UnmarshalBinary(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkUnmarshalText(b *testing.B) {
|
||||||
|
bytes := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
u := UUID{}
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
u.UnmarshalText(bytes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sink string
|
||||||
|
|
||||||
|
func BenchmarkMarshalToString(b *testing.B) {
|
||||||
|
u := NewV4()
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
sink = u.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
481
Godeps/_workspace/src/github.com/satori/go.uuid/uuid.go
generated
vendored
Normal file
481
Godeps/_workspace/src/github.com/satori/go.uuid/uuid.go
generated
vendored
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
// Copyright (C) 2013-2015 by Maxim Bublis <b@codemonkey.ru>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
// Package uuid provides implementation of Universally Unique Identifier (UUID).
|
||||||
|
// Supported versions are 1, 3, 4 and 5 (as specified in RFC 4122) and
|
||||||
|
// version 2 (as specified in DCE 1.1).
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha1"
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/binary"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// UUID layout variants.
|
||||||
|
const (
|
||||||
|
VariantNCS = iota
|
||||||
|
VariantRFC4122
|
||||||
|
VariantMicrosoft
|
||||||
|
VariantFuture
|
||||||
|
)
|
||||||
|
|
||||||
|
// UUID DCE domains.
|
||||||
|
const (
|
||||||
|
DomainPerson = iota
|
||||||
|
DomainGroup
|
||||||
|
DomainOrg
|
||||||
|
)
|
||||||
|
|
||||||
|
// Difference in 100-nanosecond intervals between
|
||||||
|
// UUID epoch (October 15, 1582) and Unix epoch (January 1, 1970).
|
||||||
|
const epochStart = 122192928000000000
|
||||||
|
|
||||||
|
// Used in string method conversion
|
||||||
|
const dash byte = '-'
|
||||||
|
|
||||||
|
// UUID v1/v2 storage.
|
||||||
|
var (
|
||||||
|
storageMutex sync.Mutex
|
||||||
|
storageOnce sync.Once
|
||||||
|
epochFunc = unixTimeFunc
|
||||||
|
clockSequence uint16
|
||||||
|
lastTime uint64
|
||||||
|
hardwareAddr [6]byte
|
||||||
|
posixUID = uint32(os.Getuid())
|
||||||
|
posixGID = uint32(os.Getgid())
|
||||||
|
)
|
||||||
|
|
||||||
|
// String parse helpers.
|
||||||
|
var (
|
||||||
|
urnPrefix = []byte("urn:uuid:")
|
||||||
|
byteGroups = []int{8, 4, 4, 4, 12}
|
||||||
|
)
|
||||||
|
|
||||||
|
func initClockSequence() {
|
||||||
|
buf := make([]byte, 2)
|
||||||
|
safeRandom(buf)
|
||||||
|
clockSequence = binary.BigEndian.Uint16(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func initHardwareAddr() {
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err == nil {
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
if len(iface.HardwareAddr) >= 6 {
|
||||||
|
copy(hardwareAddr[:], iface.HardwareAddr)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize hardwareAddr randomly in case
|
||||||
|
// of real network interfaces absence
|
||||||
|
safeRandom(hardwareAddr[:])
|
||||||
|
|
||||||
|
// Set multicast bit as recommended in RFC 4122
|
||||||
|
hardwareAddr[0] |= 0x01
|
||||||
|
}
|
||||||
|
|
||||||
|
func initStorage() {
|
||||||
|
initClockSequence()
|
||||||
|
initHardwareAddr()
|
||||||
|
}
|
||||||
|
|
||||||
|
func safeRandom(dest []byte) {
|
||||||
|
if _, err := rand.Read(dest); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns difference in 100-nanosecond intervals between
|
||||||
|
// UUID epoch (October 15, 1582) and current time.
|
||||||
|
// This is default epoch calculation function.
|
||||||
|
func unixTimeFunc() uint64 {
|
||||||
|
return epochStart + uint64(time.Now().UnixNano()/100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UUID representation compliant with specification
|
||||||
|
// described in RFC 4122.
|
||||||
|
type UUID [16]byte
|
||||||
|
|
||||||
|
// NullUUID can be used with the standard sql package to represent a
|
||||||
|
// UUID value that can be NULL in the database
|
||||||
|
type NullUUID struct {
|
||||||
|
UUID UUID
|
||||||
|
Valid bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// The nil UUID is special form of UUID that is specified to have all
|
||||||
|
// 128 bits set to zero.
|
||||||
|
var Nil = UUID{}
|
||||||
|
|
||||||
|
// Predefined namespace UUIDs.
|
||||||
|
var (
|
||||||
|
NamespaceDNS, _ = FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NamespaceURL, _ = FromString("6ba7b811-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NamespaceOID, _ = FromString("6ba7b812-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
NamespaceX500, _ = FromString("6ba7b814-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
)
|
||||||
|
|
||||||
|
// And returns result of binary AND of two UUIDs.
|
||||||
|
func And(u1 UUID, u2 UUID) UUID {
|
||||||
|
u := UUID{}
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
u[i] = u1[i] & u2[i]
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Or returns result of binary OR of two UUIDs.
|
||||||
|
func Or(u1 UUID, u2 UUID) UUID {
|
||||||
|
u := UUID{}
|
||||||
|
for i := 0; i < 16; i++ {
|
||||||
|
u[i] = u1[i] | u2[i]
|
||||||
|
}
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Equal returns true if u1 and u2 equals, otherwise returns false.
|
||||||
|
func Equal(u1 UUID, u2 UUID) bool {
|
||||||
|
return bytes.Equal(u1[:], u2[:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// Version returns algorithm version used to generate UUID.
|
||||||
|
func (u UUID) Version() uint {
|
||||||
|
return uint(u[6] >> 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Variant returns UUID layout variant.
|
||||||
|
func (u UUID) Variant() uint {
|
||||||
|
switch {
|
||||||
|
case (u[8] & 0x80) == 0x00:
|
||||||
|
return VariantNCS
|
||||||
|
case (u[8]&0xc0)|0x80 == 0x80:
|
||||||
|
return VariantRFC4122
|
||||||
|
case (u[8]&0xe0)|0xc0 == 0xc0:
|
||||||
|
return VariantMicrosoft
|
||||||
|
}
|
||||||
|
return VariantFuture
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns bytes slice representation of UUID.
|
||||||
|
func (u UUID) Bytes() []byte {
|
||||||
|
return u[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns canonical string representation of UUID:
|
||||||
|
// xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx.
|
||||||
|
func (u UUID) String() string {
|
||||||
|
buf := make([]byte, 36)
|
||||||
|
|
||||||
|
hex.Encode(buf[0:8], u[0:4])
|
||||||
|
buf[8] = dash
|
||||||
|
hex.Encode(buf[9:13], u[4:6])
|
||||||
|
buf[13] = dash
|
||||||
|
hex.Encode(buf[14:18], u[6:8])
|
||||||
|
buf[18] = dash
|
||||||
|
hex.Encode(buf[19:23], u[8:10])
|
||||||
|
buf[23] = dash
|
||||||
|
hex.Encode(buf[24:], u[10:])
|
||||||
|
|
||||||
|
return string(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetVersion sets version bits.
|
||||||
|
func (u *UUID) SetVersion(v byte) {
|
||||||
|
u[6] = (u[6] & 0x0f) | (v << 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetVariant sets variant bits as described in RFC 4122.
|
||||||
|
func (u *UUID) SetVariant() {
|
||||||
|
u[8] = (u[8] & 0xbf) | 0x80
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalText implements the encoding.TextMarshaler interface.
|
||||||
|
// The encoding is the same as returned by String.
|
||||||
|
func (u UUID) MarshalText() (text []byte, err error) {
|
||||||
|
text = []byte(u.String())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaler interface.
|
||||||
|
// Following formats are supported:
|
||||||
|
// "6ba7b810-9dad-11d1-80b4-00c04fd430c8",
|
||||||
|
// "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
|
||||||
|
// "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||||
|
func (u *UUID) UnmarshalText(text []byte) (err error) {
|
||||||
|
if len(text) < 32 {
|
||||||
|
err = fmt.Errorf("uuid: UUID string too short: %s", text)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t := text[:]
|
||||||
|
braced := false
|
||||||
|
|
||||||
|
if bytes.Equal(t[:9], urnPrefix) {
|
||||||
|
t = t[9:]
|
||||||
|
} else if t[0] == '{' {
|
||||||
|
braced = true
|
||||||
|
t = t[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
b := u[:]
|
||||||
|
|
||||||
|
for i, byteGroup := range byteGroups {
|
||||||
|
if i > 0 {
|
||||||
|
if t[0] != '-' {
|
||||||
|
err = fmt.Errorf("uuid: invalid string format")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t = t[1:]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(t) < byteGroup {
|
||||||
|
err = fmt.Errorf("uuid: UUID string too short: %s", text)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if i == 4 && len(t) > byteGroup &&
|
||||||
|
((braced && t[byteGroup] != '}') || len(t[byteGroup:]) > 1 || !braced) {
|
||||||
|
err = fmt.Errorf("uuid: UUID string too long: %s", text)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = hex.Decode(b[:byteGroup/2], t[:byteGroup])
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
t = t[byteGroup:]
|
||||||
|
b = b[byteGroup/2:]
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalBinary implements the encoding.BinaryMarshaler interface.
|
||||||
|
func (u UUID) MarshalBinary() (data []byte, err error) {
|
||||||
|
data = u.Bytes()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface.
|
||||||
|
// It will return error if the slice isn't 16 bytes long.
|
||||||
|
func (u *UUID) UnmarshalBinary(data []byte) (err error) {
|
||||||
|
if len(data) != 16 {
|
||||||
|
err = fmt.Errorf("uuid: UUID must be exactly 16 bytes long, got %d bytes", len(data))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
copy(u[:], data)
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver.Valuer interface.
|
||||||
|
func (u UUID) Value() (driver.Value, error) {
|
||||||
|
return u.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the sql.Scanner interface.
|
||||||
|
// A 16-byte slice is handled by UnmarshalBinary, while
|
||||||
|
// a longer byte slice or a string is handled by UnmarshalText.
|
||||||
|
func (u *UUID) Scan(src interface{}) error {
|
||||||
|
switch src := src.(type) {
|
||||||
|
case []byte:
|
||||||
|
if len(src) == 16 {
|
||||||
|
return u.UnmarshalBinary(src)
|
||||||
|
}
|
||||||
|
return u.UnmarshalText(src)
|
||||||
|
|
||||||
|
case string:
|
||||||
|
return u.UnmarshalText([]byte(src))
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("uuid: cannot convert %T to UUID", src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Value implements the driver.Valuer interface.
|
||||||
|
func (u NullUUID) Value() (driver.Value, error) {
|
||||||
|
if !u.Valid {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
// Delegate to UUID Value function
|
||||||
|
return u.UUID.Value()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scan implements the sql.Scanner interface.
|
||||||
|
func (u *NullUUID) Scan(src interface{}) error {
|
||||||
|
if src == nil {
|
||||||
|
u.UUID, u.Valid = Nil, false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delegate to UUID Scan function
|
||||||
|
u.Valid = true
|
||||||
|
return u.UUID.Scan(src)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromBytes returns UUID converted from raw byte slice input.
|
||||||
|
// It will return error if the slice isn't 16 bytes long.
|
||||||
|
func FromBytes(input []byte) (u UUID, err error) {
|
||||||
|
err = u.UnmarshalBinary(input)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromBytesOrNil returns UUID converted from raw byte slice input.
|
||||||
|
// Same behavior as FromBytes, but returns a Nil UUID on error.
|
||||||
|
func FromBytesOrNil(input []byte) UUID {
|
||||||
|
uuid, err := FromBytes(input)
|
||||||
|
if err != nil {
|
||||||
|
return Nil
|
||||||
|
}
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromString returns UUID parsed from string input.
|
||||||
|
// Input is expected in a form accepted by UnmarshalText.
|
||||||
|
func FromString(input string) (u UUID, err error) {
|
||||||
|
err = u.UnmarshalText([]byte(input))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStringOrNil returns UUID parsed from string input.
|
||||||
|
// Same behavior as FromString, but returns a Nil UUID on error.
|
||||||
|
func FromStringOrNil(input string) UUID {
|
||||||
|
uuid, err := FromString(input)
|
||||||
|
if err != nil {
|
||||||
|
return Nil
|
||||||
|
}
|
||||||
|
return uuid
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns UUID v1/v2 storage state.
|
||||||
|
// Returns epoch timestamp, clock sequence, and hardware address.
|
||||||
|
func getStorage() (uint64, uint16, []byte) {
|
||||||
|
storageOnce.Do(initStorage)
|
||||||
|
|
||||||
|
storageMutex.Lock()
|
||||||
|
defer storageMutex.Unlock()
|
||||||
|
|
||||||
|
timeNow := epochFunc()
|
||||||
|
// Clock changed backwards since last UUID generation.
|
||||||
|
// Should increase clock sequence.
|
||||||
|
if timeNow <= lastTime {
|
||||||
|
clockSequence++
|
||||||
|
}
|
||||||
|
lastTime = timeNow
|
||||||
|
|
||||||
|
return timeNow, clockSequence, hardwareAddr[:]
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV1 returns UUID based on current timestamp and MAC address.
|
||||||
|
func NewV1() UUID {
|
||||||
|
u := UUID{}
|
||||||
|
|
||||||
|
timeNow, clockSeq, hardwareAddr := getStorage()
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint32(u[0:], uint32(timeNow))
|
||||||
|
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
|
||||||
|
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
|
||||||
|
binary.BigEndian.PutUint16(u[8:], clockSeq)
|
||||||
|
|
||||||
|
copy(u[10:], hardwareAddr)
|
||||||
|
|
||||||
|
u.SetVersion(1)
|
||||||
|
u.SetVariant()
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV2 returns DCE Security UUID based on POSIX UID/GID.
|
||||||
|
func NewV2(domain byte) UUID {
|
||||||
|
u := UUID{}
|
||||||
|
|
||||||
|
timeNow, clockSeq, hardwareAddr := getStorage()
|
||||||
|
|
||||||
|
switch domain {
|
||||||
|
case DomainPerson:
|
||||||
|
binary.BigEndian.PutUint32(u[0:], posixUID)
|
||||||
|
case DomainGroup:
|
||||||
|
binary.BigEndian.PutUint32(u[0:], posixGID)
|
||||||
|
}
|
||||||
|
|
||||||
|
binary.BigEndian.PutUint16(u[4:], uint16(timeNow>>32))
|
||||||
|
binary.BigEndian.PutUint16(u[6:], uint16(timeNow>>48))
|
||||||
|
binary.BigEndian.PutUint16(u[8:], clockSeq)
|
||||||
|
u[9] = domain
|
||||||
|
|
||||||
|
copy(u[10:], hardwareAddr)
|
||||||
|
|
||||||
|
u.SetVersion(2)
|
||||||
|
u.SetVariant()
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV3 returns UUID based on MD5 hash of namespace UUID and name.
|
||||||
|
func NewV3(ns UUID, name string) UUID {
|
||||||
|
u := newFromHash(md5.New(), ns, name)
|
||||||
|
u.SetVersion(3)
|
||||||
|
u.SetVariant()
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV4 returns random generated UUID.
|
||||||
|
func NewV4() UUID {
|
||||||
|
u := UUID{}
|
||||||
|
safeRandom(u[:])
|
||||||
|
u.SetVersion(4)
|
||||||
|
u.SetVariant()
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewV5 returns UUID based on SHA-1 hash of namespace UUID and name.
|
||||||
|
func NewV5(ns UUID, name string) UUID {
|
||||||
|
u := newFromHash(sha1.New(), ns, name)
|
||||||
|
u.SetVersion(5)
|
||||||
|
u.SetVariant()
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns UUID based on hashing of namespace UUID and name.
|
||||||
|
func newFromHash(h hash.Hash, ns UUID, name string) UUID {
|
||||||
|
u := UUID{}
|
||||||
|
h.Write(ns[:])
|
||||||
|
h.Write([]byte(name))
|
||||||
|
copy(u[:], h.Sum(nil))
|
||||||
|
|
||||||
|
return u
|
||||||
|
}
|
||||||
633
Godeps/_workspace/src/github.com/satori/go.uuid/uuid_test.go
generated
vendored
Normal file
633
Godeps/_workspace/src/github.com/satori/go.uuid/uuid_test.go
generated
vendored
Normal file
@@ -0,0 +1,633 @@
|
|||||||
|
// Copyright (C) 2013, 2015 by Maxim Bublis <b@codemonkey.ru>
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
// a copy of this software and associated documentation files (the
|
||||||
|
// "Software"), to deal in the Software without restriction, including
|
||||||
|
// without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
// permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
// the following conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
package uuid
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestBytes(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
|
||||||
|
bytes1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
|
||||||
|
if !bytes.Equal(u.Bytes(), bytes1) {
|
||||||
|
t.Errorf("Incorrect bytes representation for UUID: %s", u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestString(t *testing.T) {
|
||||||
|
if NamespaceDNS.String() != "6ba7b810-9dad-11d1-80b4-00c04fd430c8" {
|
||||||
|
t.Errorf("Incorrect string representation for UUID: %s", NamespaceDNS.String())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEqual(t *testing.T) {
|
||||||
|
if !Equal(NamespaceDNS, NamespaceDNS) {
|
||||||
|
t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceDNS)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Equal(NamespaceDNS, NamespaceURL) {
|
||||||
|
t.Errorf("Incorrect comparison of %s and %s", NamespaceDNS, NamespaceURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestOr(t *testing.T) {
|
||||||
|
u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff}
|
||||||
|
u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00}
|
||||||
|
|
||||||
|
u := UUID{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}
|
||||||
|
|
||||||
|
if !Equal(u, Or(u1, u2)) {
|
||||||
|
t.Errorf("Incorrect bitwise OR result %s", Or(u1, u2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAnd(t *testing.T) {
|
||||||
|
u1 := UUID{0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff}
|
||||||
|
u2 := UUID{0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00}
|
||||||
|
|
||||||
|
u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
|
if !Equal(u, And(u1, u2)) {
|
||||||
|
t.Errorf("Incorrect bitwise AND result %s", And(u1, u2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVersion(t *testing.T) {
|
||||||
|
u := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
|
if u.Version() != 1 {
|
||||||
|
t.Errorf("Incorrect version for UUID: %d", u.Version())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetVersion(t *testing.T) {
|
||||||
|
u := UUID{}
|
||||||
|
u.SetVersion(4)
|
||||||
|
|
||||||
|
if u.Version() != 4 {
|
||||||
|
t.Errorf("Incorrect version for UUID after u.setVersion(4): %d", u.Version())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestVariant(t *testing.T) {
|
||||||
|
u1 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
|
if u1.Variant() != VariantNCS {
|
||||||
|
t.Errorf("Incorrect variant for UUID variant %d: %d", VariantNCS, u1.Variant())
|
||||||
|
}
|
||||||
|
|
||||||
|
u2 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
|
if u2.Variant() != VariantRFC4122 {
|
||||||
|
t.Errorf("Incorrect variant for UUID variant %d: %d", VariantRFC4122, u2.Variant())
|
||||||
|
}
|
||||||
|
|
||||||
|
u3 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
|
if u3.Variant() != VariantMicrosoft {
|
||||||
|
t.Errorf("Incorrect variant for UUID variant %d: %d", VariantMicrosoft, u3.Variant())
|
||||||
|
}
|
||||||
|
|
||||||
|
u4 := UUID{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||||
|
|
||||||
|
if u4.Variant() != VariantFuture {
|
||||||
|
t.Errorf("Incorrect variant for UUID variant %d: %d", VariantFuture, u4.Variant())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestSetVariant(t *testing.T) {
|
||||||
|
u := new(UUID)
|
||||||
|
u.SetVariant()
|
||||||
|
|
||||||
|
if u.Variant() != VariantRFC4122 {
|
||||||
|
t.Errorf("Incorrect variant for UUID after u.setVariant(): %d", u.Variant())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromBytes(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
|
||||||
|
u1, err := FromBytes(b1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing UUID from bytes: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u1) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
||||||
|
}
|
||||||
|
|
||||||
|
b2 := []byte{}
|
||||||
|
|
||||||
|
_, err = FromBytes(b2)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error parsing from empty byte slice, got %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalBinary(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
|
||||||
|
b2, err := u.MarshalBinary()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error marshaling UUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(b1, b2) {
|
||||||
|
t.Errorf("Marshaled UUID should be %s, got %s", b1, b2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalBinary(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
|
||||||
|
u1 := UUID{}
|
||||||
|
err := u1.UnmarshalBinary(b1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error unmarshaling UUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u1) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
||||||
|
}
|
||||||
|
|
||||||
|
b2 := []byte{}
|
||||||
|
u2 := UUID{}
|
||||||
|
|
||||||
|
err = u2.UnmarshalBinary(b2)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error unmarshalling from empty byte slice, got %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromString(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
|
||||||
|
s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||||
|
s2 := "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}"
|
||||||
|
s3 := "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||||
|
|
||||||
|
_, err := FromString("")
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error trying to parse empty string, got %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
u1, err := FromString(s1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing UUID from string: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u1) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
||||||
|
}
|
||||||
|
|
||||||
|
u2, err := FromString(s2)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing UUID from string: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u2) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u2)
|
||||||
|
}
|
||||||
|
|
||||||
|
u3, err := FromString(s3)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing UUID from string: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u3) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u3)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromStringShort(t *testing.T) {
|
||||||
|
// Invalid 35-character UUID string
|
||||||
|
s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c"
|
||||||
|
|
||||||
|
for i := len(s1); i >= 0; i-- {
|
||||||
|
_, err := FromString(s1[:i])
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error trying to parse too short string, got %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromStringLong(t *testing.T) {
|
||||||
|
// Invalid 37+ character UUID string
|
||||||
|
s := []string{
|
||||||
|
"6ba7b810-9dad-11d1-80b4-00c04fd430c8=",
|
||||||
|
"6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
|
||||||
|
"{6ba7b810-9dad-11d1-80b4-00c04fd430c8}f",
|
||||||
|
"6ba7b810-9dad-11d1-80b4-00c04fd430c800c04fd430c8",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, str := range s {
|
||||||
|
_, err := FromString(str)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error trying to parse too long string, passed %s", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromStringInvalid(t *testing.T) {
|
||||||
|
// Invalid UUID string formats
|
||||||
|
s := []string{
|
||||||
|
"6ba7b8109dad11d180b400c04fd430c8",
|
||||||
|
"6ba7b8109dad11d180b400c04fd430c86ba7b8109dad11d180b400c04fd430c8",
|
||||||
|
"urn:uuid:{6ba7b810-9dad-11d1-80b4-00c04fd430c8}",
|
||||||
|
"6ba7b8109-dad-11d1-80b4-00c04fd430c8",
|
||||||
|
"6ba7b810-9dad1-1d1-80b4-00c04fd430c8",
|
||||||
|
"6ba7b810-9dad-11d18-0b4-00c04fd430c8",
|
||||||
|
"6ba7b810-9dad-11d1-80b40-0c04fd430c8",
|
||||||
|
"6ba7b810+9dad+11d1+80b4+00c04fd430c8",
|
||||||
|
"6ba7b810-9dad11d180b400c04fd430c8",
|
||||||
|
"6ba7b8109dad-11d180b400c04fd430c8",
|
||||||
|
"6ba7b8109dad11d1-80b400c04fd430c8",
|
||||||
|
"6ba7b8109dad11d180b4-00c04fd430c8",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, str := range s {
|
||||||
|
_, err := FromString(str)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error trying to parse invalid string, passed %s", str)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromStringOrNil(t *testing.T) {
|
||||||
|
u := FromStringOrNil("")
|
||||||
|
if u != Nil {
|
||||||
|
t.Errorf("Should return Nil UUID on parse failure, got %s", u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestFromBytesOrNil(t *testing.T) {
|
||||||
|
b := []byte{}
|
||||||
|
u := FromBytesOrNil(b)
|
||||||
|
if u != Nil {
|
||||||
|
t.Errorf("Should return Nil UUID on parse failure, got %s", u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMarshalText(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
|
||||||
|
b2, err := u.MarshalText()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error marshaling UUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(b1, b2) {
|
||||||
|
t.Errorf("Marshaled UUID should be %s, got %s", b1, b2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnmarshalText(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
|
||||||
|
u1 := UUID{}
|
||||||
|
err := u1.UnmarshalText(b1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error unmarshaling UUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u1) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
||||||
|
}
|
||||||
|
|
||||||
|
b2 := []byte("")
|
||||||
|
u2 := UUID{}
|
||||||
|
|
||||||
|
err = u2.UnmarshalText(b2)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error trying to unmarshal from empty string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValue(t *testing.T) {
|
||||||
|
u, err := FromString("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error parsing UUID from string: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
val, err := u.Value()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error getting UUID value: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val != u.String() {
|
||||||
|
t.Errorf("Wrong value returned, should be equal: %s and %s", val, u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestValueNil(t *testing.T) {
|
||||||
|
u := UUID{}
|
||||||
|
|
||||||
|
val, err := u.Value()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error getting UUID value: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val != Nil.String() {
|
||||||
|
t.Errorf("Wrong value returned, should be equal to UUID.Nil: %s", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNullUUIDValueNil(t *testing.T) {
|
||||||
|
u := NullUUID{}
|
||||||
|
|
||||||
|
val, err := u.Value()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error getting UUID value: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if val != nil {
|
||||||
|
t.Errorf("Wrong value returned, should be nil: %s", val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScanBinary(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
b1 := []byte{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
|
||||||
|
u1 := UUID{}
|
||||||
|
err := u1.Scan(b1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error unmarshaling UUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u1) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
||||||
|
}
|
||||||
|
|
||||||
|
b2 := []byte{}
|
||||||
|
u2 := UUID{}
|
||||||
|
|
||||||
|
err = u2.Scan(b2)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error unmarshalling from empty byte slice, got %s", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScanString(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||||
|
|
||||||
|
u1 := UUID{}
|
||||||
|
err := u1.Scan(s1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error unmarshaling UUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u1) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
||||||
|
}
|
||||||
|
|
||||||
|
s2 := ""
|
||||||
|
u2 := UUID{}
|
||||||
|
|
||||||
|
err = u2.Scan(s2)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error trying to unmarshal from empty string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScanText(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
b1 := []byte("6ba7b810-9dad-11d1-80b4-00c04fd430c8")
|
||||||
|
|
||||||
|
u1 := UUID{}
|
||||||
|
err := u1.Scan(b1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error unmarshaling UUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u1) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u1)
|
||||||
|
}
|
||||||
|
|
||||||
|
b2 := []byte("")
|
||||||
|
u2 := UUID{}
|
||||||
|
|
||||||
|
err = u2.Scan(b2)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error trying to unmarshal from empty string")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScanUnsupported(t *testing.T) {
|
||||||
|
u := UUID{}
|
||||||
|
|
||||||
|
err := u.Scan(true)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Should return error trying to unmarshal from bool")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestScanNil(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
|
||||||
|
err := u.Scan(nil)
|
||||||
|
if err == nil {
|
||||||
|
t.Errorf("Error UUID shouldn't allow unmarshalling from nil")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNullUUIDScanValid(t *testing.T) {
|
||||||
|
u := UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}
|
||||||
|
s1 := "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
|
||||||
|
|
||||||
|
u1 := NullUUID{}
|
||||||
|
err := u1.Scan(s1)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error unmarshaling NullUUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !u1.Valid {
|
||||||
|
t.Errorf("NullUUID should be valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u, u1.UUID) {
|
||||||
|
t.Errorf("UUIDs should be equal: %s and %s", u, u1.UUID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNullUUIDScanNil(t *testing.T) {
|
||||||
|
u := NullUUID{UUID{0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8}, true}
|
||||||
|
|
||||||
|
err := u.Scan(nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Error unmarshaling NullUUID: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Valid {
|
||||||
|
t.Errorf("NullUUID should not be valid")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !Equal(u.UUID, Nil) {
|
||||||
|
t.Errorf("NullUUID value should be equal to Nil: %v", u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewV1(t *testing.T) {
|
||||||
|
u := NewV1()
|
||||||
|
|
||||||
|
if u.Version() != 1 {
|
||||||
|
t.Errorf("UUIDv1 generated with incorrect version: %d", u.Version())
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Variant() != VariantRFC4122 {
|
||||||
|
t.Errorf("UUIDv1 generated with incorrect variant: %d", u.Variant())
|
||||||
|
}
|
||||||
|
|
||||||
|
u1 := NewV1()
|
||||||
|
u2 := NewV1()
|
||||||
|
|
||||||
|
if Equal(u1, u2) {
|
||||||
|
t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u1, u2)
|
||||||
|
}
|
||||||
|
|
||||||
|
oldFunc := epochFunc
|
||||||
|
epochFunc = func() uint64 { return 0 }
|
||||||
|
|
||||||
|
u3 := NewV1()
|
||||||
|
u4 := NewV1()
|
||||||
|
|
||||||
|
if Equal(u3, u4) {
|
||||||
|
t.Errorf("UUIDv1 generated two equal UUIDs: %s and %s", u3, u4)
|
||||||
|
}
|
||||||
|
|
||||||
|
epochFunc = oldFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewV2(t *testing.T) {
|
||||||
|
u1 := NewV2(DomainPerson)
|
||||||
|
|
||||||
|
if u1.Version() != 2 {
|
||||||
|
t.Errorf("UUIDv2 generated with incorrect version: %d", u1.Version())
|
||||||
|
}
|
||||||
|
|
||||||
|
if u1.Variant() != VariantRFC4122 {
|
||||||
|
t.Errorf("UUIDv2 generated with incorrect variant: %d", u1.Variant())
|
||||||
|
}
|
||||||
|
|
||||||
|
u2 := NewV2(DomainGroup)
|
||||||
|
|
||||||
|
if u2.Version() != 2 {
|
||||||
|
t.Errorf("UUIDv2 generated with incorrect version: %d", u2.Version())
|
||||||
|
}
|
||||||
|
|
||||||
|
if u2.Variant() != VariantRFC4122 {
|
||||||
|
t.Errorf("UUIDv2 generated with incorrect variant: %d", u2.Variant())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewV3(t *testing.T) {
|
||||||
|
u := NewV3(NamespaceDNS, "www.example.com")
|
||||||
|
|
||||||
|
if u.Version() != 3 {
|
||||||
|
t.Errorf("UUIDv3 generated with incorrect version: %d", u.Version())
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Variant() != VariantRFC4122 {
|
||||||
|
t.Errorf("UUIDv3 generated with incorrect variant: %d", u.Variant())
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.String() != "5df41881-3aed-3515-88a7-2f4a814cf09e" {
|
||||||
|
t.Errorf("UUIDv3 generated incorrectly: %s", u.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
u = NewV3(NamespaceDNS, "python.org")
|
||||||
|
|
||||||
|
if u.String() != "6fa459ea-ee8a-3ca4-894e-db77e160355e" {
|
||||||
|
t.Errorf("UUIDv3 generated incorrectly: %s", u.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
u1 := NewV3(NamespaceDNS, "golang.org")
|
||||||
|
u2 := NewV3(NamespaceDNS, "golang.org")
|
||||||
|
if !Equal(u1, u2) {
|
||||||
|
t.Errorf("UUIDv3 generated different UUIDs for same namespace and name: %s and %s", u1, u2)
|
||||||
|
}
|
||||||
|
|
||||||
|
u3 := NewV3(NamespaceDNS, "example.com")
|
||||||
|
if Equal(u1, u3) {
|
||||||
|
t.Errorf("UUIDv3 generated same UUIDs for different names in same namespace: %s and %s", u1, u2)
|
||||||
|
}
|
||||||
|
|
||||||
|
u4 := NewV3(NamespaceURL, "golang.org")
|
||||||
|
if Equal(u1, u4) {
|
||||||
|
t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewV4(t *testing.T) {
|
||||||
|
u := NewV4()
|
||||||
|
|
||||||
|
if u.Version() != 4 {
|
||||||
|
t.Errorf("UUIDv4 generated with incorrect version: %d", u.Version())
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Variant() != VariantRFC4122 {
|
||||||
|
t.Errorf("UUIDv4 generated with incorrect variant: %d", u.Variant())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewV5(t *testing.T) {
|
||||||
|
u := NewV5(NamespaceDNS, "www.example.com")
|
||||||
|
|
||||||
|
if u.Version() != 5 {
|
||||||
|
t.Errorf("UUIDv5 generated with incorrect version: %d", u.Version())
|
||||||
|
}
|
||||||
|
|
||||||
|
if u.Variant() != VariantRFC4122 {
|
||||||
|
t.Errorf("UUIDv5 generated with incorrect variant: %d", u.Variant())
|
||||||
|
}
|
||||||
|
|
||||||
|
u = NewV5(NamespaceDNS, "python.org")
|
||||||
|
|
||||||
|
if u.String() != "886313e1-3b8a-5372-9b90-0c9aee199e5d" {
|
||||||
|
t.Errorf("UUIDv5 generated incorrectly: %s", u.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
u1 := NewV5(NamespaceDNS, "golang.org")
|
||||||
|
u2 := NewV5(NamespaceDNS, "golang.org")
|
||||||
|
if !Equal(u1, u2) {
|
||||||
|
t.Errorf("UUIDv5 generated different UUIDs for same namespace and name: %s and %s", u1, u2)
|
||||||
|
}
|
||||||
|
|
||||||
|
u3 := NewV5(NamespaceDNS, "example.com")
|
||||||
|
if Equal(u1, u3) {
|
||||||
|
t.Errorf("UUIDv5 generated same UUIDs for different names in same namespace: %s and %s", u1, u2)
|
||||||
|
}
|
||||||
|
|
||||||
|
u4 := NewV5(NamespaceURL, "golang.org")
|
||||||
|
if Equal(u1, u4) {
|
||||||
|
t.Errorf("UUIDv3 generated same UUIDs for sane names in different namespaces: %s and %s", u1, u4)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,9 @@ package api
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
"errors"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SCSICommandType byte
|
type SCSICommandType byte
|
||||||
@@ -158,7 +161,7 @@ type SCSICommand struct {
|
|||||||
OutSDBBuffer SCSIDataBuffer
|
OutSDBBuffer SCSIDataBuffer
|
||||||
RelTargetPortID uint16
|
RelTargetPortID uint16
|
||||||
// Command ITN ID
|
// Command ITN ID
|
||||||
CommandITNID uint64
|
ITNexusID uuid.UUID
|
||||||
Offset uint64
|
Offset uint64
|
||||||
TL uint32
|
TL uint32
|
||||||
SCB *bytes.Buffer
|
SCB *bytes.Buffer
|
||||||
@@ -174,12 +177,8 @@ type SCSICommand struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type ITNexus struct {
|
type ITNexus struct {
|
||||||
ID uint64 `json:"id"`
|
ID uuid.UUID `json:"id"` /*UUIDv1*/
|
||||||
Ctime uint64 `json:"ctime"`
|
Tag string `json:"Tag"` /*For protocal spec identifer*/
|
||||||
Commands []SCSICommand `json:"-"`
|
|
||||||
Target *SCSITarget `json:"-"`
|
|
||||||
Host int `json:"host"`
|
|
||||||
Info string `json:"info"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type ITNexusLuInfo struct {
|
type ITNexusLuInfo struct {
|
||||||
@@ -199,15 +198,18 @@ type TargetPortGroup struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SCSITarget struct {
|
type SCSITarget struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
TID int `json:"tid"`
|
TID int `json:"tid"`
|
||||||
LID int `json:"lid"`
|
LID int `json:"lid"`
|
||||||
State SCSITargetState `json:"state"`
|
State SCSITargetState `json:"state"`
|
||||||
Devices LUNMap `json:"-"`
|
Devices LUNMap `json:"-"`
|
||||||
LUN0 *SCSILu `json:"-"`
|
LUN0 *SCSILu `json:"-"`
|
||||||
ITNexus []*ITNexus `json:"itnexus"`
|
|
||||||
TargetPortGroups []*TargetPortGroup `json:"tpg"`
|
TargetPortGroups []*TargetPortGroup `json:"tpg"`
|
||||||
SCSITargetDriver interface{} `json:"-"`
|
SCSITargetDriver interface{} `json:"-"`
|
||||||
|
|
||||||
|
ITNexusMutex sync.Mutex
|
||||||
|
ITNexus map[uuid.UUID]*ITNexus `json:"itnexus"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SCSITargetDriverState int
|
type SCSITargetDriverState int
|
||||||
@@ -336,6 +338,7 @@ type BackingStore interface {
|
|||||||
|
|
||||||
type SCSIDeviceProtocol interface {
|
type SCSIDeviceProtocol interface {
|
||||||
PerformCommand(opcode int) interface{}
|
PerformCommand(opcode int) interface{}
|
||||||
|
PerformServiceAction(opcode int, action uint8) interface{}
|
||||||
InitLu(lu *SCSILu) error
|
InitLu(lu *SCSILu) error
|
||||||
ConfigLu(lu *SCSILu) error
|
ConfigLu(lu *SCSILu) error
|
||||||
OnlineLu(lu *SCSILu) error
|
OnlineLu(lu *SCSILu) error
|
||||||
@@ -348,6 +351,14 @@ type ModePage struct {
|
|||||||
Data []byte // Rest of mode page info
|
Data []byte // Rest of mode page info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type SCSIReservation struct {
|
||||||
|
ID uuid.UUID //Internal Reservation ID
|
||||||
|
Key uint64
|
||||||
|
ITNexusID uuid.UUID
|
||||||
|
Scope uint8
|
||||||
|
Type uint8
|
||||||
|
}
|
||||||
|
|
||||||
type SCSILu struct {
|
type SCSILu struct {
|
||||||
Address uint64
|
Address uint64
|
||||||
Size uint64
|
Size uint64
|
||||||
@@ -355,7 +366,7 @@ type SCSILu struct {
|
|||||||
Path string
|
Path string
|
||||||
BsoFlags int
|
BsoFlags int
|
||||||
BlockShift uint
|
BlockShift uint
|
||||||
ReserveID uint64
|
ReserveID uuid.UUID
|
||||||
Attrs SCSILuPhyAttribute
|
Attrs SCSILuPhyAttribute
|
||||||
ModePages []ModePage
|
ModePages []ModePage
|
||||||
Storage BackingStore
|
Storage BackingStore
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ type iscsiConnection struct {
|
|||||||
sessionType int
|
sessionType int
|
||||||
sessionParam []ISCSISessionParam
|
sessionParam []ISCSISessionParam
|
||||||
tid int
|
tid int
|
||||||
cid uint16
|
CID uint16
|
||||||
rxIOState int
|
rxIOState int
|
||||||
txIOState int
|
txIOState int
|
||||||
refcount int
|
refcount int
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import (
|
|||||||
"github.com/gostor/gotgt/pkg/port"
|
"github.com/gostor/gotgt/pkg/port"
|
||||||
"github.com/gostor/gotgt/pkg/scsi"
|
"github.com/gostor/gotgt/pkg/scsi"
|
||||||
"github.com/gostor/gotgt/pkg/util"
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ISCSITargetService struct {
|
type ISCSITargetService struct {
|
||||||
@@ -165,7 +166,7 @@ func (s *ISCSITargetService) handler(events byte, conn *iscsiConnection) {
|
|||||||
s.txHandler(conn)
|
s.txHandler(conn)
|
||||||
}
|
}
|
||||||
if conn.state == CONN_STATE_CLOSE {
|
if conn.state == CONN_STATE_CLOSE {
|
||||||
glog.Warningf("iscsi connection[%d] closed", conn.cid)
|
glog.Warningf("iscsi connection[%d] closed", conn.CID)
|
||||||
conn.close()
|
conn.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -324,6 +325,7 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
|||||||
{"DataSequenceInOrder", "Yes"},
|
{"DataSequenceInOrder", "Yes"},
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
|
conn.CID = cmd.ConnID
|
||||||
pairs := util.ParseKVText(cmd.RawData)
|
pairs := util.ParseKVText(cmd.RawData)
|
||||||
if initiatorName, ok := pairs["InitiatorName"]; ok {
|
if initiatorName, ok := pairs["InitiatorName"]; ok {
|
||||||
conn.initiator = initiatorName
|
conn.initiator = initiatorName
|
||||||
@@ -376,11 +378,14 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
|||||||
case SESSION_NORMAL:
|
case SESSION_NORMAL:
|
||||||
if conn.session == nil {
|
if conn.session == nil {
|
||||||
// create a new session
|
// create a new session
|
||||||
sess, err := s.NewISCSISession(conn)
|
sess, err := s.NewISCSISession(conn, cmd.ISID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Error(err)
|
glog.Error(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(sess)}
|
||||||
|
scsi.AddITNexus(&sess.Target.SCSITarget, itnexus)
|
||||||
|
sess.ITNexusID = itnexus.ID
|
||||||
conn.session = sess
|
conn.session = sess
|
||||||
}
|
}
|
||||||
case SESSION_DISCOVERY:
|
case SESSION_DISCOVERY:
|
||||||
@@ -804,7 +809,7 @@ func (s *ISCSITargetService) iscsiExecTask(task *iscsiTask) error {
|
|||||||
task.scmd.Direction = api.SCSIDataWrite
|
task.scmd.Direction = api.SCSIDataWrite
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
task.scmd.CommandITNID = task.conn.session.Tsih
|
task.scmd.ITNexusID = task.conn.session.ITNexusID
|
||||||
task.scmd.SCB = bytes.NewBuffer(cmd.CDB)
|
task.scmd.SCB = bytes.NewBuffer(cmd.CDB)
|
||||||
task.scmd.SCBLength = len(cmd.CDB)
|
task.scmd.SCBLength = len(cmd.CDB)
|
||||||
task.scmd.Lun = cmd.LUN
|
task.scmd.Lun = cmd.LUN
|
||||||
|
|||||||
@@ -78,8 +78,8 @@ type iSCSITPGT struct {
|
|||||||
type ISCSITarget struct {
|
type ISCSITarget struct {
|
||||||
api.SCSITarget
|
api.SCSITarget
|
||||||
api.SCSITargetDriverCommon
|
api.SCSITargetDriverCommon
|
||||||
TPGTs map[uint16]*iSCSITPGT
|
TPGTs map[uint16]*iSCSITPGT /* Key is a TPGT number */
|
||||||
Sessions []*ISCSISession
|
Sessions map[uint16]*ISCSISession /* Key is an TSIH */
|
||||||
SessionParam []ISCSISessionParam
|
SessionParam []ISCSISessionParam
|
||||||
Alias string
|
Alias string
|
||||||
MaxSessions int
|
MaxSessions int
|
||||||
@@ -87,6 +87,23 @@ type ISCSITarget struct {
|
|||||||
Rdma int
|
Rdma int
|
||||||
NopInterval int
|
NopInterval int
|
||||||
NopCount int
|
NopCount int
|
||||||
|
TSIHCounter uint16
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* RFC 3720 iSCSI SSID = ISID , TPGT
|
||||||
|
* ISID is an 6 bytes number, TPGT is an 2 bytes number
|
||||||
|
* We combime ISID and TPGT together to be SSID
|
||||||
|
*/
|
||||||
|
func MakeSSID(ISID uint64, TPGT uint16) uint64 {
|
||||||
|
SSID := ISID<<16 | uint64(TPGT)
|
||||||
|
return SSID
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseSSID(SSID uint64) (uint64, uint16) {
|
||||||
|
TPGT := uint16(uint64(0xFFFF) & SSID)
|
||||||
|
ISID := SSID >> 16
|
||||||
|
return ISID, TPGT
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
||||||
@@ -103,8 +120,10 @@ func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
|||||||
|
|
||||||
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
||||||
return &ISCSITarget{
|
return &ISCSITarget{
|
||||||
SCSITarget: *target,
|
SCSITarget: *target,
|
||||||
TPGTs: make(map[uint16]*iSCSITPGT),
|
TPGTs: make(map[uint16]*iSCSITPGT),
|
||||||
|
Sessions: make(map[uint16]*ISCSISession),
|
||||||
|
TSIHCounter: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,6 +20,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -149,8 +151,9 @@ type ISCSISession struct {
|
|||||||
Initiator string
|
Initiator string
|
||||||
InitiatorAlias string
|
InitiatorAlias string
|
||||||
Target *ISCSITarget
|
Target *ISCSITarget
|
||||||
Isid uint64
|
ISID uint64
|
||||||
Tsih uint64
|
TSIH uint64
|
||||||
|
ITNexusID uuid.UUID
|
||||||
|
|
||||||
ExpCmdSN uint32
|
ExpCmdSN uint32
|
||||||
// only one connection per session
|
// only one connection per session
|
||||||
@@ -215,7 +218,7 @@ type iscsiPdu struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New creates a new session.
|
// New creates a new session.
|
||||||
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
|
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection, isid uint64) (*ISCSISession, error) {
|
||||||
var (
|
var (
|
||||||
target *ISCSITarget
|
target *ISCSITarget
|
||||||
tsih uint64
|
tsih uint64
|
||||||
@@ -235,7 +238,7 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
|||||||
rand.Seed(int64(time.Now().UTC().Nanosecond()))
|
rand.Seed(int64(time.Now().UTC().Nanosecond()))
|
||||||
tsih = uint64(rand.Uint32())
|
tsih = uint64(rand.Uint32())
|
||||||
for _, s := range target.Sessions {
|
for _, s := range target.Sessions {
|
||||||
if s.Tsih == tsih {
|
if s.TSIH == tsih {
|
||||||
tsih = 0
|
tsih = 0
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -246,7 +249,8 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
|||||||
}
|
}
|
||||||
|
|
||||||
sess := &ISCSISession{
|
sess := &ISCSISession{
|
||||||
Tsih: tsih,
|
TSIH: tsih,
|
||||||
|
ISID: isid,
|
||||||
Initiator: conn.initiator,
|
Initiator: conn.initiator,
|
||||||
InitiatorAlias: conn.initiatorAlias,
|
InitiatorAlias: conn.initiatorAlias,
|
||||||
Target: target,
|
Target: target,
|
||||||
@@ -259,3 +263,14 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
|||||||
conn.session = sess
|
conn.session = sess
|
||||||
return sess, nil
|
return sess, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* iSCSI I_T nexus identifer = (iSCSI Initiator Name + 'i' + ISID, iSCSI Target Name + 't' + Portal Group Tag)
|
||||||
|
*/
|
||||||
|
func GeniSCSIITNexusID(sess *ISCSISession) string {
|
||||||
|
strID := fmt.Sprintf("%si0x%12x,%st%d",
|
||||||
|
sess.Initiator, sess.ISID,
|
||||||
|
sess.Target.SCSITarget.Name,
|
||||||
|
sess.Connections[0].tpgt)
|
||||||
|
return strID
|
||||||
|
}
|
||||||
|
|||||||
@@ -20,36 +20,33 @@ import (
|
|||||||
"github.com/gostor/gotgt/pkg/util"
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SCSIPRServiceAction byte
|
const (
|
||||||
type SCSIPRType byte
|
/* 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
|
||||||
|
|
||||||
var (
|
/* PERSISTENT_RESERVE_OUT service action codes */
|
||||||
// PERSISTENT_RESERVE_IN service action codes
|
PR_OUT_REGISTER byte = 0x00
|
||||||
PR_IN_READ_KEYS SCSIPRServiceAction = 0x00
|
PR_OUT_RESERVE byte = 0x01
|
||||||
PR_IN_READ_RESERVATION SCSIPRServiceAction = 0x01
|
PR_OUT_RELEASE byte = 0x02
|
||||||
PR_IN_REPORT_CAPABILITIES SCSIPRServiceAction = 0x02
|
PR_OUT_CLEAR byte = 0x03
|
||||||
PR_IN_READ_FULL_STATUS SCSIPRServiceAction = 0x03
|
PR_OUT_PREEMPT byte = 0x04
|
||||||
|
PR_OUT_PREEMPT_AND_ABORT byte = 0x05
|
||||||
// PERSISTENT_RESERVE_OUT service action codes
|
PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY byte = 0x06
|
||||||
PR_OUT_REGISTER SCSIPRServiceAction = 0x00
|
PR_OUT_REGISTER_AND_MOVE byte = 0x07
|
||||||
PR_OUT_RESERVE SCSIPRServiceAction = 0x01
|
|
||||||
PR_OUT_RELEASE SCSIPRServiceAction = 0x02
|
|
||||||
PR_OUT_CLEAR SCSIPRServiceAction = 0x03
|
|
||||||
PR_OUT_PREEMPT SCSIPRServiceAction = 0x04
|
|
||||||
PR_OUT_PREEMPT_AND_ABORT SCSIPRServiceAction = 0x05
|
|
||||||
PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY SCSIPRServiceAction = 0x06
|
|
||||||
PR_OUT_REGISTER_AND_MOVE SCSIPRServiceAction = 0x07
|
|
||||||
|
|
||||||
// Persistent Reservation scope
|
// Persistent Reservation scope
|
||||||
PR_LU_SCOPE byte = 0x00
|
PR_LU_SCOPE byte = 0x00
|
||||||
|
|
||||||
// Persistent Reservation Type Mask format
|
/* Persistent Reservation Type Mask format */
|
||||||
PR_TYPE_WRITE_EXCLUSIVE SCSIPRType = 0x01
|
PR_TYPE_WRITE_EXCLUSIVE byte = 0x01
|
||||||
PR_TYPE_EXCLUSIVE_ACCESS SCSIPRType = 0x03
|
PR_TYPE_EXCLUSIVE_ACCESS byte = 0x03
|
||||||
PR_TYPE_WRITE_EXCLUSIVE_REGONLY SCSIPRType = 0x05
|
PR_TYPE_WRITE_EXCLUSIVE_REGONLY byte = 0x05
|
||||||
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY SCSIPRType = 0x06
|
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY byte = 0x06
|
||||||
PR_TYPE_WRITE_EXCLUSIVE_ALLREG SCSIPRType = 0x07
|
PR_TYPE_WRITE_EXCLUSIVE_ALLREG byte = 0x07
|
||||||
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG SCSIPRType = 0x08
|
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG byte = 0x08
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|||||||
@@ -11,8 +11,7 @@ Unless required by applicable law or agreed to in writing, software
|
|||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License. */
|
||||||
*/
|
|
||||||
|
|
||||||
package scsi
|
package scsi
|
||||||
|
|
||||||
@@ -80,6 +79,10 @@ func NewLUN0() *api.SCSILu {
|
|||||||
return lu
|
return lu
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetReservation(dev *api.SCSILu, nexusID uint64) *api.SCSIReservation {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
|
func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
op := int(cmd.SCB.Bytes()[0])
|
op := int(cmd.SCB.Bytes()[0])
|
||||||
fn := cmd.Device.DeviceProtocol.PerformCommand(op)
|
fn := cmd.Device.DeviceProtocol.PerformCommand(op)
|
||||||
|
|||||||
@@ -45,6 +45,16 @@ func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} {
|
|||||||
return sbc.SCSIDeviceOps[opcode]
|
return sbc.SCSIDeviceOps[opcode]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sbc SBCSCSIDeviceProtocol) PerformServiceAction(opcode int, action uint8) interface{} {
|
||||||
|
var sa *SCSIServiceAction
|
||||||
|
for _, sa = range sbc.SCSIDeviceOps[opcode].ServiceAction {
|
||||||
|
if sa.ServiceAction == action {
|
||||||
|
return sa
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
|
func (sbc SBCSCSIDeviceProtocol) InitLu(lu *api.SCSILu) error {
|
||||||
// init LU's phy attribute
|
// init LU's phy attribute
|
||||||
lu.Attrs.DeviceType = sbc.DeviceType
|
lu.Attrs.DeviceType = sbc.DeviceType
|
||||||
@@ -158,8 +168,23 @@ func NewSBCDevice(deviceType api.SCSIDeviceType) api.SCSIDeviceProtocol {
|
|||||||
|
|
||||||
sbc.SCSIDeviceOps[api.MODE_SELECT_10] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
|
sbc.SCSIDeviceOps[api.MODE_SELECT_10] = NewSCSIDeviceOperation(SBCModeSelect, nil, PR_WE_FA|PR_EA_FA|PR_EA_FN|PR_WE_FN)
|
||||||
sbc.SCSIDeviceOps[api.MODE_SENSE_10] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_WE_FN|PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[api.MODE_SENSE_10] = NewSCSIDeviceOperation(SBCModeSense, nil, PR_WE_FA|PR_WE_FN|PR_EA_FA|PR_EA_FN)
|
||||||
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)
|
|
||||||
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)
|
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_IN] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{
|
||||||
|
{ServiceAction: PR_IN_READ_KEYS, CommandPerformFunc: SPCPRReadKeys},
|
||||||
|
{ServiceAction: PR_IN_READ_RESERVATION, CommandPerformFunc: SPCPRReadReservation},
|
||||||
|
{ServiceAction: PR_IN_REPORT_CAPABILITIES, CommandPerformFunc: SPCPRReportCapabilities},
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
sbc.SCSIDeviceOps[api.PERSISTENT_RESERVE_OUT] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{
|
||||||
|
{ServiceAction: PR_OUT_REGISTER, CommandPerformFunc: SPCPRRegister},
|
||||||
|
{ServiceAction: PR_OUT_RESERVE, CommandPerformFunc: SPCPRReserve},
|
||||||
|
{ServiceAction: PR_OUT_RELEASE, CommandPerformFunc: SPCPRRelease},
|
||||||
|
{ServiceAction: PR_OUT_CLEAR, CommandPerformFunc: SPCPRClear},
|
||||||
|
{ServiceAction: PR_OUT_PREEMPT, CommandPerformFunc: SPCPRPreempt},
|
||||||
|
// {ServiceAction: PR_OUT_PREEMPT_AND_ABORT, CommandPerformFunc: SPCPRPreempt},
|
||||||
|
{ServiceAction: PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY, CommandPerformFunc: SPCPRRegister},
|
||||||
|
{ServiceAction: PR_OUT_REGISTER_AND_MOVE, CommandPerformFunc: SPCPRRegisterAndMove},
|
||||||
|
}, 0)
|
||||||
|
|
||||||
sbc.SCSIDeviceOps[api.READ_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[api.READ_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||||
sbc.SCSIDeviceOps[api.WRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
|
sbc.SCSIDeviceOps[api.WRITE_16] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN|PR_WE_FA|PR_WE_FN)
|
||||||
@@ -173,8 +198,10 @@ func NewSBCDevice(deviceType api.SCSIDeviceType) api.SCSIDeviceProtocol {
|
|||||||
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SBCServiceAction, nil, 0)
|
sbc.SCSIDeviceOps[api.SERVICE_ACTION_IN] = NewSCSIDeviceOperation(SBCServiceAction, nil, 0)
|
||||||
|
|
||||||
sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
|
sbc.SCSIDeviceOps[api.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
|
||||||
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{
|
||||||
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
{ServiceAction: 0x0C, CommandPerformFunc: SPCReportSupportedOperationCodes},
|
||||||
|
}, 0)
|
||||||
|
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCIllegalOp, nil, 0)
|
||||||
sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[api.READ_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||||
sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
|
sbc.SCSIDeviceOps[api.WRITE_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_WE_FA|PR_EA_FA|PR_WE_FA|PR_WE_FN)
|
||||||
sbc.SCSIDeviceOps[api.WRITE_VERIFY_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
sbc.SCSIDeviceOps[api.WRITE_VERIFY_12] = NewSCSIDeviceOperation(SBCReadWrite, nil, PR_EA_FA|PR_EA_FN)
|
||||||
@@ -431,7 +458,7 @@ func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
|
|||||||
|
|
||||||
func SBCRelease(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SBCRelease(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
||||||
if err := deviceRelease(cmd.Target.TID, cmd.CommandITNID, lun, false); err != nil {
|
if err := deviceRelease(cmd.Target.TID, cmd.ITNexusID, lun, false); err != nil {
|
||||||
return api.SAMStatReservationConflict
|
return api.SAMStatReservationConflict
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SCSITargetService struct {
|
type SCSITargetService struct {
|
||||||
@@ -70,7 +71,7 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro
|
|||||||
}
|
}
|
||||||
scmd.Target = target
|
scmd.Target = target
|
||||||
for _, it := range target.ITNexus {
|
for _, it := range target.ITNexus {
|
||||||
if it.ID == scmd.CommandITNID {
|
if uuid.Equal(it.ID, scmd.ITNexusID) {
|
||||||
itn = it
|
itn = it
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@@ -99,13 +100,13 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro
|
|||||||
}
|
}
|
||||||
|
|
||||||
type SCSIServiceAction struct {
|
type SCSIServiceAction struct {
|
||||||
ServiceAction uint32
|
ServiceAction uint8
|
||||||
CommandPerformFunc api.CommandFunc
|
CommandPerformFunc api.CommandFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
type SCSIDeviceOperation struct {
|
type SCSIDeviceOperation struct {
|
||||||
CommandPerformFunc api.CommandFunc
|
CommandPerformFunc api.CommandFunc
|
||||||
ServiceAction *SCSIServiceAction
|
ServiceAction []*SCSIServiceAction
|
||||||
PRConflictBits uint8
|
PRConflictBits uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -114,7 +115,7 @@ type BaseSCSIDeviceProtocol struct {
|
|||||||
SCSIDeviceOps []SCSIDeviceOperation
|
SCSIDeviceOps []SCSIDeviceOperation
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSCSIDeviceOperation(fn api.CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
func NewSCSIDeviceOperation(fn api.CommandFunc, sa []*SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
||||||
return SCSIDeviceOperation{
|
return SCSIDeviceOperation{
|
||||||
CommandPerformFunc: fn,
|
CommandPerformFunc: fn,
|
||||||
ServiceAction: sa,
|
ServiceAction: sa,
|
||||||
|
|||||||
324
pkg/scsi/scsi_pr.go
Normal file
324
pkg/scsi/scsi_pr.go
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
package scsi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SCSIReservationOperator interface {
|
||||||
|
GetCurrentReservation(tgtName string, devUUID uint64) *api.SCSIReservation
|
||||||
|
SetCurrentReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool
|
||||||
|
IsCurrentReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool
|
||||||
|
|
||||||
|
GetPRGeneration(tgtName string, devUUID uint64) (uint32, bool)
|
||||||
|
IncPRGeneration(tgtName string, devUUID uint64) bool
|
||||||
|
|
||||||
|
AddReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool
|
||||||
|
GetReservation(tgtName string, devUUID uint64, ITNexusID uuid.UUID) *api.SCSIReservation
|
||||||
|
GetReservationList(tgtName string, devUUID uint64) []*api.SCSIReservation
|
||||||
|
DeleteAndRemoveReservation(tgtName string, devUUID uint64, res *api.SCSIReservation)
|
||||||
|
RemoveReservation(tgtName string, devUUID uint64, res *api.SCSIReservation)
|
||||||
|
RemoveAllReservation(tgtName string, devUUID uint64)
|
||||||
|
|
||||||
|
IsKeyExists(tgtName string, devUUID uint64, key uint64) bool
|
||||||
|
|
||||||
|
Save(tgtName string, devUUID uint64) bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var onePROperator SCSIReservationOperator
|
||||||
|
|
||||||
|
func GetSCSIReservationOperator() SCSIReservationOperator {
|
||||||
|
if onePROperator == nil {
|
||||||
|
onePROperator = &SCSISimpleReservationOperator{
|
||||||
|
targetReservations: make(map[string]SCSILUReservationMap),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return onePROperator
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCSILUReservation struct {
|
||||||
|
TargetName string
|
||||||
|
DeviceUUID uint64
|
||||||
|
PRGeneration uint32
|
||||||
|
Reservations []*api.SCSIReservation
|
||||||
|
CurrentReservation *api.SCSIReservation
|
||||||
|
}
|
||||||
|
|
||||||
|
type SCSILUReservationMap map[uint64]*SCSILUReservation /* device uuid as the key */
|
||||||
|
|
||||||
|
type SCSISimpleReservationOperator struct {
|
||||||
|
SCSIReservationOperator
|
||||||
|
targetReservations map[string]SCSILUReservationMap /* target name as the key*/
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) InitLUReservation(tgtName string, devUUID uint64) {
|
||||||
|
var (
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
op.targetReservations[tgtName] = make(SCSILUReservationMap)
|
||||||
|
targetRes = op.targetReservations[tgtName]
|
||||||
|
}
|
||||||
|
if _, ok = targetRes[devUUID]; !ok {
|
||||||
|
targetRes[devUUID] = &SCSILUReservation{TargetName: tgtName,
|
||||||
|
DeviceUUID: devUUID,
|
||||||
|
PRGeneration: 0,
|
||||||
|
Reservations: []*api.SCSIReservation{},
|
||||||
|
CurrentReservation: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) GetReservation(tgtName string, devUUID uint64, ITNexusID uuid.UUID) *api.SCSIReservation {
|
||||||
|
var (
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
SCSIRes *api.SCSIReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, SCSIRes = range LURes.Reservations {
|
||||||
|
if uuid.Equal(SCSIRes.ITNexusID, ITNexusID) {
|
||||||
|
return SCSIRes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) GetPRGeneration(tgtName string, devUUID uint64) (uint32, bool) {
|
||||||
|
var (
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return LURes.PRGeneration, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) IncPRGeneration(tgtName string, devUUID uint64) bool {
|
||||||
|
var (
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
LURes.PRGeneration++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) GetCurrentReservation(tgtName string, devUUID uint64) *api.SCSIReservation {
|
||||||
|
var (
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return LURes.CurrentReservation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) SetCurrentReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool {
|
||||||
|
var (
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
LURes.CurrentReservation = res
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) GetReservationList(tgtName string, devUUID uint64) []*api.SCSIReservation {
|
||||||
|
var (
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return LURes.Reservations
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) DeleteAndRemoveReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) {
|
||||||
|
var (
|
||||||
|
i int = -1
|
||||||
|
ok bool
|
||||||
|
tmpRes *api.SCSIReservation
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resArray := LURes.Reservations
|
||||||
|
curRes := LURes.CurrentReservation
|
||||||
|
|
||||||
|
for i, tmpRes = range resArray {
|
||||||
|
if tmpRes == res {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= 0 {
|
||||||
|
resArray[i] = resArray[len(resArray)-1]
|
||||||
|
resArray[len(resArray)-1] = nil
|
||||||
|
resArray = resArray[:len(resArray)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
if curRes == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !op.IsCurrentReservation(tgtName, devUUID, res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (curRes.Type != PR_TYPE_WRITE_EXCLUSIVE_ALLREG &&
|
||||||
|
curRes.Type != PR_TYPE_EXCLUSIVE_ACCESS_ALLREG) ||
|
||||||
|
len(resArray) == 0 {
|
||||||
|
curRes.Scope = 0
|
||||||
|
curRes.Type = 0
|
||||||
|
LURes.CurrentReservation = nil
|
||||||
|
for i, tmpRes = range resArray {
|
||||||
|
if tmpRes == res {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//TODO send sense code
|
||||||
|
}
|
||||||
|
LURes.PRGeneration++
|
||||||
|
} else {
|
||||||
|
for i, tmpRes = range resArray {
|
||||||
|
if tmpRes != res {
|
||||||
|
//kep scope and type
|
||||||
|
LURes.CurrentReservation = tmpRes
|
||||||
|
tmpRes.Scope = curRes.Scope
|
||||||
|
tmpRes.Type = curRes.Type
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LURes.PRGeneration++
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) RemoveReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) {
|
||||||
|
var (
|
||||||
|
i int = -1
|
||||||
|
tmpRes *api.SCSIReservation
|
||||||
|
)
|
||||||
|
|
||||||
|
resArray := op.GetReservationList(tgtName, devUUID)
|
||||||
|
|
||||||
|
for i, tmpRes = range resArray {
|
||||||
|
if tmpRes == res {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if i >= 0 {
|
||||||
|
resArray[i] = resArray[len(resArray)-1]
|
||||||
|
resArray[len(resArray)-1] = nil
|
||||||
|
resArray = resArray[:len(resArray)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) AddReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool {
|
||||||
|
var (
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
LURes.Reservations = append(LURes.Reservations, res)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) IsKeyExists(tgtName string, devUUID uint64, key uint64) bool {
|
||||||
|
|
||||||
|
resList := op.GetReservationList(tgtName, devUUID)
|
||||||
|
for _, tmpRes := range resList {
|
||||||
|
if tmpRes.Key == key {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) IsCurrentReservation(tgtName string, devUUID uint64, res *api.SCSIReservation) bool {
|
||||||
|
curRes := op.GetCurrentReservation(tgtName, devUUID)
|
||||||
|
if curRes == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if curRes.Type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG ||
|
||||||
|
curRes.Type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
if curRes == res {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) RemoveAllReservation(tgtName string, devUUID uint64) {
|
||||||
|
var (
|
||||||
|
LURes *SCSILUReservation
|
||||||
|
targetRes SCSILUReservationMap
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if targetRes, ok = op.targetReservations[tgtName]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if LURes, ok = targetRes[devUUID]; !ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
LURes.Reservations = []*api.SCSIReservation{}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (op *SCSISimpleReservationOperator) Save(tgtName string, devUUID uint64) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
@@ -65,6 +65,8 @@ func GetTargetLUNMap(tgtName string) api.LUNMap {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InitSCSILUMap(config *config.Config) error {
|
func InitSCSILUMap(config *config.Config) error {
|
||||||
|
var simpleOp *SCSISimpleReservationOperator
|
||||||
|
var ok bool
|
||||||
globalSCSILUMap.mutex.Lock()
|
globalSCSILUMap.mutex.Lock()
|
||||||
defer globalSCSILUMap.mutex.Unlock()
|
defer globalSCSILUMap.mutex.Unlock()
|
||||||
|
|
||||||
@@ -83,6 +85,11 @@ func InitSCSILUMap(config *config.Config) error {
|
|||||||
return errors.New("LU Number must be a number")
|
return errors.New("LU Number must be a number")
|
||||||
}
|
}
|
||||||
mappingLUN(deviceID, lun, tgtName)
|
mappingLUN(deviceID, lun, tgtName)
|
||||||
|
//Init SCSISimpleReservationOperator
|
||||||
|
op := GetSCSIReservationOperator()
|
||||||
|
if simpleOp, ok = op.(*SCSISimpleReservationOperator); ok {
|
||||||
|
simpleOp.InitLUReservation(tgtName, deviceID)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
548
pkg/scsi/spc.go
548
pkg/scsi/spc.go
@@ -25,6 +25,7 @@ import (
|
|||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
"github.com/gostor/gotgt/pkg/util"
|
"github.com/gostor/gotgt/pkg/util"
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func SPCIllegalOp(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCIllegalOp(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
@@ -568,9 +569,7 @@ func reportOpcodeOne(cmd *api.SCSICommand, rctd int, opcode byte, rsa uint16, se
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is useful for the various commands using the SERVICE ACTION format.
|
func SPCReportSupportedOperationCodes(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
|
||||||
// TODO
|
|
||||||
scb := cmd.SCB.Bytes()
|
scb := cmd.SCB.Bytes()
|
||||||
reporting_options := scb[2] & 0x07
|
reporting_options := scb[2] & 0x07
|
||||||
opcode := scb[3]
|
opcode := scb[3]
|
||||||
@@ -609,15 +608,60 @@ sense:
|
|||||||
return api.SAMStatCheckCondition
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is useful for the various commands using the SERVICE ACTION format.
|
||||||
|
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
|
||||||
|
scb := cmd.SCB.Bytes()
|
||||||
|
opcode := int(scb[0])
|
||||||
|
action := uint8(scb[1] & 0x1F)
|
||||||
|
serviceAction := cmd.Device.DeviceProtocol.PerformServiceAction(opcode, action)
|
||||||
|
if serviceAction == nil {
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
|
} else {
|
||||||
|
fnop := serviceAction.(*SCSIServiceAction)
|
||||||
|
return fnop.CommandPerformFunc(host, cmd)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRReadKeys(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
allocationLength := util.GetUnalignedUint32(cmd.SCB.Bytes()[7:9])
|
var (
|
||||||
|
buf = &bytes.Buffer{}
|
||||||
|
data []byte = []byte{}
|
||||||
|
addBuf = &bytes.Buffer{}
|
||||||
|
allocationLength uint16
|
||||||
|
additionLength uint32
|
||||||
|
)
|
||||||
|
tgtName := cmd.Target.Name
|
||||||
|
devUUID := cmd.Device.UUID
|
||||||
|
scsiResOp := GetSCSIReservationOperator()
|
||||||
|
PRGeneration, _ := scsiResOp.GetPRGeneration(tgtName, devUUID)
|
||||||
|
resList := scsiResOp.GetReservationList(tgtName, devUUID)
|
||||||
|
length, _ := SCSICDBBufXLength(cmd.SCB.Bytes())
|
||||||
|
|
||||||
|
allocationLength = uint16(length)
|
||||||
if allocationLength < 8 {
|
if allocationLength < 8 {
|
||||||
goto sense
|
goto sense
|
||||||
}
|
}
|
||||||
if cmd.InSDBBuffer.Length < allocationLength {
|
|
||||||
goto sense
|
for _, res := range resList {
|
||||||
|
addBuf.Write(util.MarshalUint64(res.Key))
|
||||||
}
|
}
|
||||||
// TODO
|
additionLength = uint32(len(addBuf.Bytes()))
|
||||||
|
|
||||||
|
buf.Write(util.MarshalUint32(PRGeneration))
|
||||||
|
buf.Write(util.MarshalUint32(additionLength))
|
||||||
|
buf.Write(addBuf.Bytes())
|
||||||
|
data = buf.Bytes()
|
||||||
|
if allocationLength < uint16(additionLength) {
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data[0:allocationLength])
|
||||||
|
} else {
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.InSDBBuffer.Resid = int32(additionLength)
|
||||||
|
return api.SAMStatGood
|
||||||
sense:
|
sense:
|
||||||
cmd.InSDBBuffer.Resid = 0
|
cmd.InSDBBuffer.Resid = 0
|
||||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
@@ -625,7 +669,59 @@ sense:
|
|||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRReadReservation(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRReadReservation(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
var (
|
||||||
|
buf = &bytes.Buffer{}
|
||||||
|
data []byte = []byte{}
|
||||||
|
addBuf = &bytes.Buffer{}
|
||||||
|
allocationLength uint16
|
||||||
|
additionLength uint32
|
||||||
|
)
|
||||||
|
tgtName := cmd.Target.Name
|
||||||
|
devUUID := cmd.Device.UUID
|
||||||
|
scsiResOp := GetSCSIReservationOperator()
|
||||||
|
PRGeneration, _ := scsiResOp.GetPRGeneration(tgtName, devUUID)
|
||||||
|
curRes := scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||||
|
|
||||||
|
length, _ := SCSICDBBufXLength(cmd.SCB.Bytes())
|
||||||
|
allocationLength = uint16(length)
|
||||||
|
if allocationLength < 8 {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
if curRes == nil {
|
||||||
|
additionLength = 0
|
||||||
|
} else {
|
||||||
|
addBuf.Write(util.MarshalUint64(curRes.Key))
|
||||||
|
//Obsolete
|
||||||
|
addBuf.WriteByte(0x00)
|
||||||
|
addBuf.WriteByte(0x00)
|
||||||
|
addBuf.WriteByte(0x00)
|
||||||
|
addBuf.WriteByte(0x00)
|
||||||
|
//Reserved
|
||||||
|
addBuf.WriteByte(0x00)
|
||||||
|
//SCOPE and TYPE
|
||||||
|
scope_type := (curRes.Scope << 4) | curRes.Type
|
||||||
|
addBuf.WriteByte(scope_type)
|
||||||
|
additionLength = uint32(0x10)
|
||||||
|
}
|
||||||
|
|
||||||
|
buf.Write(util.MarshalUint32(PRGeneration))
|
||||||
|
buf.Write(util.MarshalUint32(additionLength))
|
||||||
|
buf.Write(addBuf.Bytes())
|
||||||
|
data = buf.Bytes()
|
||||||
|
if allocationLength < uint16(additionLength) {
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data[0:allocationLength])
|
||||||
|
} else {
|
||||||
|
cmd.InSDBBuffer.Buffer = bytes.NewBuffer(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd.InSDBBuffer.Resid = int32(additionLength)
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRReportCapabilities(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRReportCapabilities(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
@@ -672,28 +768,466 @@ sense:
|
|||||||
return api.SAMStatCheckCondition
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reservationCheck(host int, cmd *api.SCSICommand) bool {
|
||||||
|
var (
|
||||||
|
paramLen uint32
|
||||||
|
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||||
|
)
|
||||||
|
length, _ := SCSICDBBufXLength(cmd.SCB.Bytes())
|
||||||
|
paramLen = uint32(length)
|
||||||
|
if paramLen != 24 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
spec_i_pt := uint8(buf[20] & 0x08)
|
||||||
|
all_tg_pt := uint8(buf[20] & 0x04)
|
||||||
|
aptpl := uint8(buf[20] & 0x01)
|
||||||
|
/* Currently, We don't support these flags */
|
||||||
|
if (spec_i_pt | all_tg_pt | aptpl) > 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
func SPCPRRegister(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRRegister(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
var (
|
||||||
|
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||||
|
scb []byte = cmd.SCB.Bytes()
|
||||||
|
ignoreKey bool = false
|
||||||
|
ok bool = false
|
||||||
|
resKey uint64
|
||||||
|
sAResKey uint64
|
||||||
|
)
|
||||||
|
|
||||||
|
tgtName := cmd.Target.Name
|
||||||
|
devUUID := cmd.Device.UUID
|
||||||
|
scsiResOp := GetSCSIReservationOperator()
|
||||||
|
res := scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||||
|
|
||||||
|
if scb[1] == PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY {
|
||||||
|
ignoreKey = true
|
||||||
|
}
|
||||||
|
ok = reservationCheck(host, cmd)
|
||||||
|
if !ok {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||||
|
sAResKey = util.GetUnalignedUint64(buf[8:16])
|
||||||
|
|
||||||
|
if res != nil {
|
||||||
|
if ignoreKey || resKey == res.Key {
|
||||||
|
if sAResKey != 0 {
|
||||||
|
res.Key = sAResKey
|
||||||
|
} else {
|
||||||
|
scsiResOp.DeleteAndRemoveReservation(tgtName, devUUID, res)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ignoreKey || resKey == 0 {
|
||||||
|
if sAResKey != 0 {
|
||||||
|
newRes := &api.SCSIReservation{
|
||||||
|
ID: uuid.NewV1(),
|
||||||
|
Key: sAResKey,
|
||||||
|
ITNexusID: cmd.ITNexusID,
|
||||||
|
}
|
||||||
|
scsiResOp.AddReservation(tgtName, devUUID, newRes)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scsiResOp.IncPRGeneration(tgtName, devUUID)
|
||||||
|
scsiResOp.Save(tgtName, devUUID)
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRReserve(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRReserve(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
var (
|
||||||
|
scb []byte = cmd.SCB.Bytes()
|
||||||
|
curRes *api.SCSIReservation
|
||||||
|
res *api.SCSIReservation
|
||||||
|
ok bool = false
|
||||||
|
resScope uint8
|
||||||
|
resType uint8
|
||||||
|
)
|
||||||
|
|
||||||
|
tgtName := cmd.Target.Name
|
||||||
|
devUUID := cmd.Device.UUID
|
||||||
|
scsiResOp := GetSCSIReservationOperator()
|
||||||
|
|
||||||
|
ok = reservationCheck(host, cmd)
|
||||||
|
if !ok {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
resScope = scb[2] & 0xf0 >> 4
|
||||||
|
resType = scb[2] & 0x0f
|
||||||
|
|
||||||
|
switch resType {
|
||||||
|
case PR_TYPE_WRITE_EXCLUSIVE_REGONLY,
|
||||||
|
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY,
|
||||||
|
PR_TYPE_WRITE_EXCLUSIVE_ALLREG,
|
||||||
|
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
if resScope != PR_LU_SCOPE {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||||
|
if res == nil {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||||
|
if curRes != nil {
|
||||||
|
if !scsiResOp.IsCurrentReservation(tgtName, devUUID, res) {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
if curRes.Type != resType ||
|
||||||
|
curRes.Scope != resScope {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res.Scope = resScope
|
||||||
|
res.Type = resType
|
||||||
|
scsiResOp.SetCurrentReservation(tgtName, devUUID, res)
|
||||||
|
scsiResOp.Save(tgtName, devUUID)
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRRelease(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRRelease(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
var (
|
||||||
|
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||||
|
scb []byte = cmd.SCB.Bytes()
|
||||||
|
curRes *api.SCSIReservation
|
||||||
|
res *api.SCSIReservation
|
||||||
|
resList []*api.SCSIReservation
|
||||||
|
ok bool = false
|
||||||
|
resKey uint64
|
||||||
|
resScope uint8
|
||||||
|
resType uint8
|
||||||
|
)
|
||||||
|
|
||||||
|
tgtName := cmd.Target.Name
|
||||||
|
devUUID := cmd.Device.UUID
|
||||||
|
scsiResOp := GetSCSIReservationOperator()
|
||||||
|
|
||||||
|
ok = reservationCheck(host, cmd)
|
||||||
|
if !ok {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
resScope = scb[2] & 0xf0 >> 4
|
||||||
|
resType = scb[2] & 0x0f
|
||||||
|
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||||
|
|
||||||
|
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||||
|
if res == nil {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||||
|
if curRes == nil {
|
||||||
|
return api.SAMStatGood
|
||||||
|
}
|
||||||
|
|
||||||
|
if !scsiResOp.IsCurrentReservation(tgtName, devUUID, res) {
|
||||||
|
return api.SAMStatGood
|
||||||
|
}
|
||||||
|
|
||||||
|
if resKey != res.Key {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
if curRes.Scope != resScope || curRes.Type != resType {
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_RELEASE_OF_PERSISTENT_RESERVATION)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
|
}
|
||||||
|
|
||||||
|
scsiResOp.SetCurrentReservation(tgtName, devUUID, nil)
|
||||||
|
res.Scope = 0
|
||||||
|
res.Type = 0
|
||||||
|
|
||||||
|
switch resType {
|
||||||
|
case PR_TYPE_WRITE_EXCLUSIVE,
|
||||||
|
PR_TYPE_EXCLUSIVE_ACCESS,
|
||||||
|
PR_TYPE_WRITE_EXCLUSIVE_REGONLY,
|
||||||
|
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY,
|
||||||
|
PR_TYPE_WRITE_EXCLUSIVE_ALLREG,
|
||||||
|
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG:
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
resList = scsiResOp.GetReservationList(tgtName, devUUID)
|
||||||
|
for _, tmpRes := range resList {
|
||||||
|
if tmpRes.ID == res.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
//TODO send sense code
|
||||||
|
}
|
||||||
|
scsiResOp.Save(tgtName, devUUID)
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRClear(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRClear(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
var (
|
||||||
|
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||||
|
curRes *api.SCSIReservation
|
||||||
|
res *api.SCSIReservation
|
||||||
|
ok bool = false
|
||||||
|
resKey uint64
|
||||||
|
)
|
||||||
|
|
||||||
|
tgtName := cmd.Target.Name
|
||||||
|
devUUID := cmd.Device.UUID
|
||||||
|
scsiResOp := GetSCSIReservationOperator()
|
||||||
|
resList := scsiResOp.GetReservationList(tgtName, devUUID)
|
||||||
|
|
||||||
|
ok = reservationCheck(host, cmd)
|
||||||
|
if !ok {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||||
|
|
||||||
|
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Key != resKey {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||||
|
|
||||||
|
if curRes != nil {
|
||||||
|
curRes.Scope = 0
|
||||||
|
curRes.Type = 0
|
||||||
|
scsiResOp.SetCurrentReservation(tgtName, devUUID, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tmpRes := range resList {
|
||||||
|
if tmpRes != res {
|
||||||
|
//TODO send sense code
|
||||||
|
}
|
||||||
|
scsiResOp.DeleteAndRemoveReservation(tgtName, devUUID, tmpRes)
|
||||||
|
}
|
||||||
|
scsiResOp.IncPRGeneration(tgtName, devUUID)
|
||||||
|
scsiResOp.Save(tgtName, devUUID)
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRPreempt(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRPreempt(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
var (
|
||||||
|
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||||
|
scb []byte = cmd.SCB.Bytes()
|
||||||
|
ok bool = false
|
||||||
|
resKey uint64
|
||||||
|
sAResKey uint64
|
||||||
|
res *api.SCSIReservation
|
||||||
|
curRes *api.SCSIReservation
|
||||||
|
resReleased bool
|
||||||
|
removeAllRes bool
|
||||||
|
resScope uint8
|
||||||
|
resType uint8
|
||||||
|
)
|
||||||
|
|
||||||
|
tgtName := cmd.Target.Name
|
||||||
|
devUUID := cmd.Device.UUID
|
||||||
|
scsiResOp := GetSCSIReservationOperator()
|
||||||
|
resList := scsiResOp.GetReservationList(tgtName, devUUID)
|
||||||
|
|
||||||
|
ok = reservationCheck(host, cmd)
|
||||||
|
if !ok {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
resScope = scb[2] & 0xf0 >> 4
|
||||||
|
resType = scb[2] & 0x0f
|
||||||
|
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||||
|
sAResKey = util.GetUnalignedUint64(buf[8:16])
|
||||||
|
|
||||||
|
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||||
|
|
||||||
|
if res == nil {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Key != resKey {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
if sAResKey != 0 {
|
||||||
|
ok = scsiResOp.IsKeyExists(tgtName, devUUID, sAResKey)
|
||||||
|
if ok {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||||
|
if curRes != nil {
|
||||||
|
if curRes.Type == PR_TYPE_WRITE_EXCLUSIVE_ALLREG ||
|
||||||
|
curRes.Type == PR_TYPE_EXCLUSIVE_ACCESS_ALLREG {
|
||||||
|
if sAResKey == 0 {
|
||||||
|
if resType != curRes.Type ||
|
||||||
|
resScope != curRes.Scope {
|
||||||
|
resReleased = true
|
||||||
|
}
|
||||||
|
res.Type = resType
|
||||||
|
res.Scope = resScope
|
||||||
|
scsiResOp.SetCurrentReservation(tgtName, devUUID, res)
|
||||||
|
removeAllRes = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if curRes.Key == resKey {
|
||||||
|
if resType != curRes.Type ||
|
||||||
|
resScope != curRes.Scope {
|
||||||
|
resReleased = true
|
||||||
|
}
|
||||||
|
res.Type = resType
|
||||||
|
res.Scope = resScope
|
||||||
|
scsiResOp.SetCurrentReservation(tgtName, devUUID, res)
|
||||||
|
} else {
|
||||||
|
if sAResKey == 0 {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tmpRes := range resList {
|
||||||
|
if tmpRes == res {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Key == resKey || removeAllRes {
|
||||||
|
//TODO send sense code
|
||||||
|
scsiResOp.RemoveReservation(tgtName, devUUID, res)
|
||||||
|
} else {
|
||||||
|
if resReleased {
|
||||||
|
//TODO send sense code
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
scsiResOp.IncPRGeneration(tgtName, devUUID)
|
||||||
|
scsiResOp.Save(tgtName, devUUID)
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
func SPCPRRegisterAndMove(host int, cmd *api.SCSICommand) api.SAMStat {
|
func SPCPRRegisterAndMove(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||||
|
var (
|
||||||
|
buf []byte = cmd.OutSDBBuffer.Buffer.Bytes()
|
||||||
|
scb []byte = cmd.SCB.Bytes()
|
||||||
|
resKey uint64
|
||||||
|
sAResKey uint64
|
||||||
|
curRes, dstReg, res *api.SCSIReservation
|
||||||
|
paramListLen uint32
|
||||||
|
unreg uint8
|
||||||
|
aptpl uint8
|
||||||
|
tpidDataLen uint32
|
||||||
|
//idLen uint32
|
||||||
|
)
|
||||||
|
|
||||||
|
tgtName := cmd.Target.Name
|
||||||
|
devUUID := cmd.Device.UUID
|
||||||
|
scsiResOp := GetSCSIReservationOperator()
|
||||||
|
resList := scsiResOp.GetReservationList(tgtName, devUUID)
|
||||||
|
|
||||||
|
paramListLen = util.GetUnalignedUint32(scb[5:9])
|
||||||
|
if paramListLen < 24 {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
aptpl = buf[17] & 0x01
|
||||||
|
if aptpl != 0 { /* no reported in capabilities */
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
unreg = buf[17] & 0x02
|
||||||
|
|
||||||
|
resKey = util.GetUnalignedUint64(buf[0:8])
|
||||||
|
sAResKey = util.GetUnalignedUint64(buf[8:16])
|
||||||
|
|
||||||
|
tpidDataLen = util.GetUnalignedUint32(buf[20:25])
|
||||||
|
if tpidDataLen < 24 || (tpidDataLen%4) != 0 {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
if (paramListLen - 24) < tpidDataLen {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
|
||||||
|
res = scsiResOp.GetReservation(tgtName, devUUID, cmd.ITNexusID)
|
||||||
|
curRes = scsiResOp.GetCurrentReservation(tgtName, devUUID)
|
||||||
|
if res == nil {
|
||||||
|
if curRes != nil {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
} else {
|
||||||
|
goto sense
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if scsiResOp.IsCurrentReservation(tgtName, devUUID, res) {
|
||||||
|
return api.SAMStatGood
|
||||||
|
}
|
||||||
|
|
||||||
|
if res.Key != resKey {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
if sAResKey == 0 {
|
||||||
|
return api.SAMStatReservationConflict
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, dstReg = range resList {
|
||||||
|
if dstReg.Key == sAResKey {
|
||||||
|
goto found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
goto sense
|
||||||
|
found:
|
||||||
|
//TODO check transportid
|
||||||
|
scsiResOp.SetCurrentReservation(tgtName, devUUID, dstReg)
|
||||||
|
if unreg != 0 {
|
||||||
|
scsiResOp.RemoveReservation(tgtName, devUUID, res)
|
||||||
|
}
|
||||||
|
scsiResOp.IncPRGeneration(tgtName, devUUID)
|
||||||
|
scsiResOp.Save(tgtName, devUUID)
|
||||||
return api.SAMStatGood
|
return api.SAMStatGood
|
||||||
|
sense:
|
||||||
|
cmd.InSDBBuffer.Resid = 0
|
||||||
|
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||||
|
return api.SAMStatCheckCondition
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import (
|
|||||||
|
|
||||||
"github.com/golang/glog"
|
"github.com/golang/glog"
|
||||||
"github.com/gostor/gotgt/pkg/api"
|
"github.com/gostor/gotgt/pkg/api"
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*api.SCSITarget, error) {
|
func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*api.SCSITarget, error) {
|
||||||
@@ -34,6 +35,7 @@ func (s *SCSITargetService) NewSCSITarget(tid int, driverName, name string) (*ap
|
|||||||
Name: name,
|
Name: name,
|
||||||
TID: tid,
|
TID: tid,
|
||||||
TargetPortGroups: []*api.TargetPortGroup{},
|
TargetPortGroups: []*api.TargetPortGroup{},
|
||||||
|
ITNexus: make(map[uuid.UUID]*api.ITNexus),
|
||||||
}
|
}
|
||||||
tpg := &api.TargetPortGroup{0, []*api.SCSITargetPort{}}
|
tpg := &api.TargetPortGroup{0, []*api.SCSITargetPort{}}
|
||||||
s.Targets = append(s.Targets, target)
|
s.Targets = append(s.Targets, target)
|
||||||
@@ -65,6 +67,25 @@ func FindTargetPort(target *api.SCSITarget, relPortID uint16) *api.SCSITargetPor
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddITNexus(target *api.SCSITarget, itnexus *api.ITNexus) bool {
|
||||||
|
var ret bool = true
|
||||||
|
target.ITNexusMutex.Lock()
|
||||||
|
defer target.ITNexusMutex.Unlock()
|
||||||
|
if _, ok := target.ITNexus[itnexus.ID]; !ok {
|
||||||
|
target.ITNexus[itnexus.ID] = itnexus
|
||||||
|
ret = true
|
||||||
|
} else {
|
||||||
|
ret = false
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveITNexus(target *api.SCSITarget, itnexus *api.ITNexus) {
|
||||||
|
target.ITNexusMutex.Lock()
|
||||||
|
defer target.ITNexusMutex.Unlock()
|
||||||
|
delete(target.ITNexus, itnexus.ID)
|
||||||
|
}
|
||||||
|
|
||||||
func deviceReserve(cmd *api.SCSICommand) error {
|
func deviceReserve(cmd *api.SCSICommand) error {
|
||||||
var lu *api.SCSILu
|
var lu *api.SCSILu
|
||||||
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
||||||
@@ -80,15 +101,15 @@ func deviceReserve(cmd *api.SCSICommand) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if lu.ReserveID != 0 && lu.ReserveID != cmd.CommandITNID {
|
if !uuid.Equal(lu.ReserveID, uuid.Nil) && uuid.Equal(lu.ReserveID, cmd.ITNexusID) {
|
||||||
glog.Errorf("already reserved %d, %d", lu.ReserveID, cmd.CommandITNID)
|
glog.Errorf("already reserved %d, %d", lu.ReserveID, cmd.ITNexusID)
|
||||||
return fmt.Errorf("already reserved")
|
return fmt.Errorf("already reserved")
|
||||||
}
|
}
|
||||||
lu.ReserveID = cmd.CommandITNID
|
lu.ReserveID = cmd.ITNexusID
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func deviceRelease(tid int, itn, lun uint64, force bool) error {
|
func deviceRelease(tid int, itn uuid.UUID, lun uint64, force bool) error {
|
||||||
// TODO
|
// TODO
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user