This repository has been archived by the owner on Jul 28, 2021. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 41
/
scsi.go
157 lines (138 loc) · 3.93 KB
/
scsi.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
// +build linux
package scsi
import (
"context"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"time"
"github.com/Microsoft/opengcs/internal/log"
"github.com/Microsoft/opengcs/internal/oc"
"github.com/pkg/errors"
"go.opencensus.io/trace"
"golang.org/x/sys/unix"
)
// Test dependencies
var (
osMkdirAll = os.MkdirAll
osRemoveAll = os.RemoveAll
unixMount = unix.Mount
// controllerLunToName is stubbed to make testing `Mount` easier.
controllerLunToName = ControllerLunToName
)
// Mount creates a mount from the SCSI device on `controller` index `lun` to
// `target`
//
// `target` will be created. On mount failure the created `target` will be
// automatically cleaned up.
func Mount(ctx context.Context, controller, lun uint8, target string, readonly bool) (err error) {
ctx, span := trace.StartSpan(ctx, "scsi::Mount")
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
span.AddAttributes(
trace.Int64Attribute("controller", int64(controller)),
trace.Int64Attribute("lun", int64(lun)))
if err := osMkdirAll(target, 0700); err != nil {
return err
}
defer func() {
if err != nil {
osRemoveAll(target)
}
}()
source, err := controllerLunToName(ctx, controller, lun)
if err != nil {
return err
}
var flags uintptr
data := ""
if readonly {
flags |= unix.MS_RDONLY
data = "noload"
}
for {
if err := unixMount(source, target, "ext4", flags, data); err != nil {
// The `source` found by controllerLunToName can take some time
// before its actually available under `/dev/sd*`. Retry while we
// wait for `source` to show up.
if err == unix.ENOENT {
select {
case <-ctx.Done():
return ctx.Err()
default:
time.Sleep(10 * time.Millisecond)
continue
}
}
return err
}
break
}
return nil
}
// ControllerLunToName finds the `/dev/sd*` path to the SCSI device on
// `controller` index `lun`.
func ControllerLunToName(ctx context.Context, controller, lun uint8) (_ string, err error) {
ctx, span := trace.StartSpan(ctx, "scsi::ControllerLunToName")
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
span.AddAttributes(
trace.Int64Attribute("controller", int64(controller)),
trace.Int64Attribute("lun", int64(lun)))
scsiID := fmt.Sprintf("0:0:%d:%d", controller, lun)
// Devices matching the given SCSI code should each have a subdirectory
// under /sys/bus/scsi/devices/<scsiID>/block.
blockPath := filepath.Join("/sys/bus/scsi/devices", scsiID, "block")
var deviceNames []os.FileInfo
for {
deviceNames, err = ioutil.ReadDir(blockPath)
if err != nil && !os.IsNotExist(err) {
return "", err
}
if len(deviceNames) == 0 {
select {
case <-ctx.Done():
return "", ctx.Err()
default:
time.Sleep(time.Millisecond * 10)
continue
}
}
break
}
if len(deviceNames) == 0 {
return "", errors.Errorf("no matching device names found for SCSI ID \"%s\"", scsiID)
}
if len(deviceNames) > 1 {
return "", errors.Errorf("more than one block device could match SCSI ID \"%s\"", scsiID)
}
devicePath := filepath.Join("/dev", deviceNames[0].Name())
log.G(ctx).WithField("devicePath", devicePath).Debug("found device path")
return devicePath, nil
}
// UnplugDevice finds the SCSI device on `controller` index `lun` and issues a
// guest initiated unplug.
//
// If the device is not attached returns no error.
func UnplugDevice(ctx context.Context, controller, lun uint8) (err error) {
_, span := trace.StartSpan(ctx, "scsi::UnplugDevice")
defer span.End()
defer func() { oc.SetSpanStatus(span, err) }()
span.AddAttributes(
trace.Int64Attribute("controller", int64(controller)),
trace.Int64Attribute("lun", int64(lun)))
scsiID := fmt.Sprintf("0:0:%d:%d", controller, lun)
f, err := os.OpenFile(filepath.Join("/sys/bus/scsi/devices", scsiID, "delete"), os.O_WRONLY, 0644)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
defer f.Close()
if _, err := f.Write([]byte("1\n")); err != nil {
return err
}
return nil
}