Merge pull request #28 from orzhang/PR_impl

PR implementation
This commit is contained in:
Le Zhang
2016-11-24 09:42:53 -06:00
committed by GitHub
19 changed files with 2382 additions and 75 deletions

View 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

View 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.

View File

@@ -0,0 +1,65 @@
# UUID package for Go language
[![Build Status](https://travis-ci.org/satori/go.uuid.png?branch=master)](https://travis-ci.org/satori/go.uuid)
[![Coverage Status](https://coveralls.io/repos/github/satori/go.uuid/badge.svg?branch=master)](https://coveralls.io/github/satori/go.uuid)
[![GoDoc](http://godoc.org/github.com/satori/go.uuid?status.png)](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.

View 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
View 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
}

View 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)
}
}

View File

@@ -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

View File

@@ -54,7 +54,7 @@ type iscsiConnection struct {
sessionType int
sessionParam []ISCSISessionParam
tid int
cid uint16
CID uint16
rxIOState int
txIOState int
refcount int

View File

@@ -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

View File

@@ -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,
}
}

View File

@@ -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
}

View File

@@ -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 (

View File

@@ -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)

View File

@@ -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
}

View File

@@ -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
View 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
}

View File

@@ -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

View File

@@ -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
}
/*

View File

@@ -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
}