PR implementation
This commit is contained in:
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 (
|
||||
"bytes"
|
||||
"errors"
|
||||
"sync"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type SCSICommandType byte
|
||||
@@ -158,7 +161,7 @@ type SCSICommand struct {
|
||||
OutSDBBuffer SCSIDataBuffer
|
||||
RelTargetPortID uint16
|
||||
// Command ITN ID
|
||||
CommandITNID uint64
|
||||
ITNexusID uuid.UUID
|
||||
Offset uint64
|
||||
TL uint32
|
||||
SCB *bytes.Buffer
|
||||
@@ -174,12 +177,8 @@ type SCSICommand struct {
|
||||
}
|
||||
|
||||
type ITNexus struct {
|
||||
ID uint64 `json:"id"`
|
||||
Ctime uint64 `json:"ctime"`
|
||||
Commands []SCSICommand `json:"-"`
|
||||
Target *SCSITarget `json:"-"`
|
||||
Host int `json:"host"`
|
||||
Info string `json:"info"`
|
||||
ID uuid.UUID `json:"id"` /*UUIDv1*/
|
||||
Tag string `json:"Tag"` /*For protocal spec identifer*/
|
||||
}
|
||||
|
||||
type ITNexusLuInfo struct {
|
||||
@@ -199,15 +198,18 @@ type TargetPortGroup struct {
|
||||
}
|
||||
|
||||
type SCSITarget struct {
|
||||
Name string `json:"name"`
|
||||
TID int `json:"tid"`
|
||||
LID int `json:"lid"`
|
||||
State SCSITargetState `json:"state"`
|
||||
Devices LUNMap `json:"-"`
|
||||
LUN0 *SCSILu `json:"-"`
|
||||
ITNexus []*ITNexus `json:"itnexus"`
|
||||
Name string `json:"name"`
|
||||
TID int `json:"tid"`
|
||||
LID int `json:"lid"`
|
||||
State SCSITargetState `json:"state"`
|
||||
Devices LUNMap `json:"-"`
|
||||
LUN0 *SCSILu `json:"-"`
|
||||
|
||||
TargetPortGroups []*TargetPortGroup `json:"tpg"`
|
||||
SCSITargetDriver interface{} `json:"-"`
|
||||
|
||||
ITNexusMutex sync.Mutex
|
||||
ITNexus map[uuid.UUID]*ITNexus `json:"itnexus"`
|
||||
}
|
||||
|
||||
type SCSITargetDriverState int
|
||||
@@ -336,6 +338,7 @@ type BackingStore interface {
|
||||
|
||||
type SCSIDeviceProtocol interface {
|
||||
PerformCommand(opcode int) interface{}
|
||||
PerformServiceAction(opcode int, action uint8) interface{}
|
||||
InitLu(lu *SCSILu) error
|
||||
ConfigLu(lu *SCSILu) error
|
||||
OnlineLu(lu *SCSILu) error
|
||||
@@ -348,6 +351,14 @@ type ModePage struct {
|
||||
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 {
|
||||
Address uint64
|
||||
Size uint64
|
||||
@@ -355,7 +366,7 @@ type SCSILu struct {
|
||||
Path string
|
||||
BsoFlags int
|
||||
BlockShift uint
|
||||
ReserveID uint64
|
||||
ReserveID uuid.UUID
|
||||
Attrs SCSILuPhyAttribute
|
||||
ModePages []ModePage
|
||||
Storage BackingStore
|
||||
|
||||
@@ -54,7 +54,7 @@ type iscsiConnection struct {
|
||||
sessionType int
|
||||
sessionParam []ISCSISessionParam
|
||||
tid int
|
||||
cid uint16
|
||||
CID uint16
|
||||
rxIOState int
|
||||
txIOState int
|
||||
refcount int
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"github.com/gostor/gotgt/pkg/port"
|
||||
"github.com/gostor/gotgt/pkg/scsi"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type ISCSITargetService struct {
|
||||
@@ -165,7 +166,7 @@ func (s *ISCSITargetService) handler(events byte, conn *iscsiConnection) {
|
||||
s.txHandler(conn)
|
||||
}
|
||||
if conn.state == CONN_STATE_CLOSE {
|
||||
glog.Warningf("iscsi connection[%d] closed", conn.cid)
|
||||
glog.Warningf("iscsi connection[%d] closed", conn.CID)
|
||||
conn.close()
|
||||
}
|
||||
}
|
||||
@@ -324,6 +325,7 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
{"DataSequenceInOrder", "Yes"},
|
||||
}),
|
||||
}
|
||||
conn.CID = cmd.ConnID
|
||||
pairs := util.ParseKVText(cmd.RawData)
|
||||
if initiatorName, ok := pairs["InitiatorName"]; ok {
|
||||
conn.initiator = initiatorName
|
||||
@@ -376,11 +378,14 @@ func (s *ISCSITargetService) iscsiExecLogin(conn *iscsiConnection) error {
|
||||
case SESSION_NORMAL:
|
||||
if conn.session == nil {
|
||||
// create a new session
|
||||
sess, err := s.NewISCSISession(conn)
|
||||
sess, err := s.NewISCSISession(conn, cmd.ISID)
|
||||
if err != nil {
|
||||
glog.Error(err)
|
||||
return err
|
||||
}
|
||||
itnexus := &api.ITNexus{uuid.NewV1(), GeniSCSIITNexusID(sess)}
|
||||
scsi.AddITNexus(&sess.Target.SCSITarget, itnexus)
|
||||
sess.ITNexusID = itnexus.ID
|
||||
conn.session = sess
|
||||
}
|
||||
case SESSION_DISCOVERY:
|
||||
@@ -804,7 +809,7 @@ func (s *ISCSITargetService) iscsiExecTask(task *iscsiTask) error {
|
||||
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.SCBLength = len(cmd.CDB)
|
||||
task.scmd.Lun = cmd.LUN
|
||||
|
||||
@@ -78,8 +78,8 @@ type iSCSITPGT struct {
|
||||
type ISCSITarget struct {
|
||||
api.SCSITarget
|
||||
api.SCSITargetDriverCommon
|
||||
TPGTs map[uint16]*iSCSITPGT
|
||||
Sessions []*ISCSISession
|
||||
TPGTs map[uint16]*iSCSITPGT /* Key is a TPGT number */
|
||||
Sessions map[uint16]*ISCSISession /* Key is an TSIH */
|
||||
SessionParam []ISCSISessionParam
|
||||
Alias string
|
||||
MaxSessions int
|
||||
@@ -87,6 +87,23 @@ type ISCSITarget struct {
|
||||
Rdma int
|
||||
NopInterval 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) {
|
||||
@@ -103,8 +120,10 @@ func (tgt *ISCSITarget) FindTPG(portal string) (uint16, error) {
|
||||
|
||||
func newISCSITarget(target *api.SCSITarget) *ISCSITarget {
|
||||
return &ISCSITarget{
|
||||
SCSITarget: *target,
|
||||
TPGTs: make(map[uint16]*iSCSITPGT),
|
||||
SCSITarget: *target,
|
||||
TPGTs: make(map[uint16]*iSCSITPGT),
|
||||
Sessions: make(map[uint16]*ISCSISession),
|
||||
TSIHCounter: 1,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"time"
|
||||
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -149,8 +151,9 @@ type ISCSISession struct {
|
||||
Initiator string
|
||||
InitiatorAlias string
|
||||
Target *ISCSITarget
|
||||
Isid uint64
|
||||
Tsih uint64
|
||||
ISID uint64
|
||||
TSIH uint64
|
||||
ITNexusID uuid.UUID
|
||||
|
||||
ExpCmdSN uint32
|
||||
// only one connection per session
|
||||
@@ -215,7 +218,7 @@ type iscsiPdu struct {
|
||||
}
|
||||
|
||||
// New creates a new session.
|
||||
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISession, error) {
|
||||
func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection, isid uint64) (*ISCSISession, error) {
|
||||
var (
|
||||
target *ISCSITarget
|
||||
tsih uint64
|
||||
@@ -235,7 +238,7 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
||||
rand.Seed(int64(time.Now().UTC().Nanosecond()))
|
||||
tsih = uint64(rand.Uint32())
|
||||
for _, s := range target.Sessions {
|
||||
if s.Tsih == tsih {
|
||||
if s.TSIH == tsih {
|
||||
tsih = 0
|
||||
break
|
||||
}
|
||||
@@ -246,7 +249,8 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
||||
}
|
||||
|
||||
sess := &ISCSISession{
|
||||
Tsih: tsih,
|
||||
TSIH: tsih,
|
||||
ISID: isid,
|
||||
Initiator: conn.initiator,
|
||||
InitiatorAlias: conn.initiatorAlias,
|
||||
Target: target,
|
||||
@@ -259,3 +263,14 @@ func (s *ISCSITargetService) NewISCSISession(conn *iscsiConnection) (*ISCSISessi
|
||||
conn.session = sess
|
||||
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"
|
||||
)
|
||||
|
||||
type SCSIPRServiceAction byte
|
||||
type SCSIPRType byte
|
||||
const (
|
||||
/* 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_IN service action codes
|
||||
PR_IN_READ_KEYS SCSIPRServiceAction = 0x00
|
||||
PR_IN_READ_RESERVATION SCSIPRServiceAction = 0x01
|
||||
PR_IN_REPORT_CAPABILITIES SCSIPRServiceAction = 0x02
|
||||
PR_IN_READ_FULL_STATUS SCSIPRServiceAction = 0x03
|
||||
|
||||
// PERSISTENT_RESERVE_OUT service action codes
|
||||
PR_OUT_REGISTER SCSIPRServiceAction = 0x00
|
||||
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_RESERVE_OUT service action codes */
|
||||
PR_OUT_REGISTER byte = 0x00
|
||||
PR_OUT_RESERVE byte = 0x01
|
||||
PR_OUT_RELEASE byte = 0x02
|
||||
PR_OUT_CLEAR byte = 0x03
|
||||
PR_OUT_PREEMPT byte = 0x04
|
||||
PR_OUT_PREEMPT_AND_ABORT byte = 0x05
|
||||
PR_OUT_REGISTER_AND_IGNORE_EXISTING_KEY byte = 0x06
|
||||
PR_OUT_REGISTER_AND_MOVE byte = 0x07
|
||||
|
||||
// Persistent Reservation scope
|
||||
PR_LU_SCOPE byte = 0x00
|
||||
|
||||
// Persistent Reservation Type Mask format
|
||||
PR_TYPE_WRITE_EXCLUSIVE SCSIPRType = 0x01
|
||||
PR_TYPE_EXCLUSIVE_ACCESS SCSIPRType = 0x03
|
||||
PR_TYPE_WRITE_EXCLUSIVE_REGONLY SCSIPRType = 0x05
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY SCSIPRType = 0x06
|
||||
PR_TYPE_WRITE_EXCLUSIVE_ALLREG SCSIPRType = 0x07
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG SCSIPRType = 0x08
|
||||
/* Persistent Reservation Type Mask format */
|
||||
PR_TYPE_WRITE_EXCLUSIVE byte = 0x01
|
||||
PR_TYPE_EXCLUSIVE_ACCESS byte = 0x03
|
||||
PR_TYPE_WRITE_EXCLUSIVE_REGONLY byte = 0x05
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_REGONLY byte = 0x06
|
||||
PR_TYPE_WRITE_EXCLUSIVE_ALLREG byte = 0x07
|
||||
PR_TYPE_EXCLUSIVE_ACCESS_ALLREG byte = 0x08
|
||||
)
|
||||
|
||||
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,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
limitations under the License. */
|
||||
|
||||
package scsi
|
||||
|
||||
@@ -80,6 +79,10 @@ func NewLUN0() *api.SCSILu {
|
||||
return lu
|
||||
}
|
||||
|
||||
func GetReservation(dev *api.SCSILu, nexusID uint64) *api.SCSIReservation {
|
||||
return nil
|
||||
}
|
||||
|
||||
func luPerformCommand(tid int, cmd *api.SCSICommand) api.SAMStat {
|
||||
op := int(cmd.SCB.Bytes()[0])
|
||||
fn := cmd.Device.DeviceProtocol.PerformCommand(op)
|
||||
|
||||
@@ -45,6 +45,16 @@ func (sbc SBCSCSIDeviceProtocol) PerformCommand(opcode int) interface{} {
|
||||
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 {
|
||||
// init LU's phy attribute
|
||||
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_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.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.REPORT_LUNS] = NewSCSIDeviceOperation(SPCReportLuns, nil, 0)
|
||||
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
||||
sbc.SCSIDeviceOps[api.EXCHANGE_MEDIUM] = NewSCSIDeviceOperation(SPCServiceAction, nil, 0)
|
||||
sbc.SCSIDeviceOps[api.MAINT_PROTOCOL_IN] = NewSCSIDeviceOperation(SPCServiceAction, []*SCSIServiceAction{
|
||||
{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.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)
|
||||
@@ -431,7 +458,7 @@ func SBCReserve(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
|
||||
func SBCRelease(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
type SCSITargetService struct {
|
||||
@@ -70,7 +71,7 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro
|
||||
}
|
||||
scmd.Target = target
|
||||
for _, it := range target.ITNexus {
|
||||
if it.ID == scmd.CommandITNID {
|
||||
if uuid.Equal(it.ID, scmd.ITNexusID) {
|
||||
itn = it
|
||||
break
|
||||
}
|
||||
@@ -99,13 +100,13 @@ func (s *SCSITargetService) AddCommandQueue(tid int, scmd *api.SCSICommand) erro
|
||||
}
|
||||
|
||||
type SCSIServiceAction struct {
|
||||
ServiceAction uint32
|
||||
ServiceAction uint8
|
||||
CommandPerformFunc api.CommandFunc
|
||||
}
|
||||
|
||||
type SCSIDeviceOperation struct {
|
||||
CommandPerformFunc api.CommandFunc
|
||||
ServiceAction *SCSIServiceAction
|
||||
ServiceAction []*SCSIServiceAction
|
||||
PRConflictBits uint8
|
||||
}
|
||||
|
||||
@@ -114,7 +115,7 @@ type BaseSCSIDeviceProtocol struct {
|
||||
SCSIDeviceOps []SCSIDeviceOperation
|
||||
}
|
||||
|
||||
func NewSCSIDeviceOperation(fn api.CommandFunc, sa *SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
||||
func NewSCSIDeviceOperation(fn api.CommandFunc, sa []*SCSIServiceAction, pr uint8) SCSIDeviceOperation {
|
||||
return SCSIDeviceOperation{
|
||||
CommandPerformFunc: fn,
|
||||
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 {
|
||||
var simpleOp *SCSISimpleReservationOperator
|
||||
var ok bool
|
||||
globalSCSILUMap.mutex.Lock()
|
||||
defer globalSCSILUMap.mutex.Unlock()
|
||||
|
||||
@@ -83,6 +85,11 @@ func InitSCSILUMap(config *config.Config) error {
|
||||
return errors.New("LU Number must be a number")
|
||||
}
|
||||
mappingLUN(deviceID, lun, tgtName)
|
||||
//Init SCSISimpleReservationOperator
|
||||
op := GetSCSIReservationOperator()
|
||||
if simpleOp, ok = op.(*SCSISimpleReservationOperator); ok {
|
||||
simpleOp.InitLUReservation(tgtName, deviceID)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
548
pkg/scsi/spc.go
548
pkg/scsi/spc.go
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/golang/glog"
|
||||
"github.com/gostor/gotgt/pkg/api"
|
||||
"github.com/gostor/gotgt/pkg/util"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// This is useful for the various commands using the SERVICE ACTION format.
|
||||
func SPCServiceAction(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
// TODO
|
||||
func SPCReportSupportedOperationCodes(host int, cmd *api.SCSICommand) api.SAMStat {
|
||||
scb := cmd.SCB.Bytes()
|
||||
reporting_options := scb[2] & 0x07
|
||||
opcode := scb[3]
|
||||
@@ -609,15 +608,60 @@ sense:
|
||||
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 {
|
||||
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 {
|
||||
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:
|
||||
cmd.InSDBBuffer.Resid = 0
|
||||
BuildSenseData(cmd, ILLEGAL_REQUEST, ASC_INVALID_FIELD_IN_CDB)
|
||||
@@ -625,7 +669,59 @@ sense:
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
@@ -672,28 +768,466 @@ sense:
|
||||
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 {
|
||||
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
|
||||
|
||||
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 {
|
||||
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
|
||||
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 {
|
||||
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
|
||||
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 {
|
||||
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
|
||||
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 {
|
||||
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
|
||||
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 {
|
||||
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
|
||||
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/gostor/gotgt/pkg/api"
|
||||
"github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
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,
|
||||
TID: tid,
|
||||
TargetPortGroups: []*api.TargetPortGroup{},
|
||||
ITNexus: make(map[uuid.UUID]*api.ITNexus),
|
||||
}
|
||||
tpg := &api.TargetPortGroup{0, []*api.SCSITargetPort{}}
|
||||
s.Targets = append(s.Targets, target)
|
||||
@@ -65,6 +67,25 @@ func FindTargetPort(target *api.SCSITarget, relPortID uint16) *api.SCSITargetPor
|
||||
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 {
|
||||
var lu *api.SCSILu
|
||||
lun := *(*uint64)(unsafe.Pointer(&cmd.Lun))
|
||||
@@ -80,15 +101,15 @@ func deviceReserve(cmd *api.SCSICommand) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
if lu.ReserveID != 0 && lu.ReserveID != cmd.CommandITNID {
|
||||
glog.Errorf("already reserved %d, %d", 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.ITNexusID)
|
||||
return fmt.Errorf("already reserved")
|
||||
}
|
||||
lu.ReserveID = cmd.CommandITNID
|
||||
lu.ReserveID = cmd.ITNexusID
|
||||
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
|
||||
return nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user