Skip to content

Commit 1430b53

Browse files
authored
Merge pull request #12 from utkarshmani1997/discovery
add code to discover the target
2 parents c545557 + 3d612c1 commit 1430b53

File tree

4 files changed

+238
-21
lines changed

4 files changed

+238
-21
lines changed

Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,6 @@ build:
1313
install:
1414
go install ./iscsi/
1515

16+
test:
17+
go test ./iscsi/
18+

iscsi/iscsi.go

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ type Connector struct {
4646
Multipath bool `json:"multipath"`
4747
RetryCount int32 `json:"retry_count"`
4848
CheckInterval int32 `json:"check_interval"`
49+
DoDiscovery bool `json:"do_discovery"`
50+
DoCHAPDiscovery bool `json:"do_chap_discovery"`
4951
}
5052

5153
func init() {
@@ -221,7 +223,7 @@ func getMultipathDisk(path string) (string, error) {
221223

222224
// Connect attempts to connect a volume to this node using the provided Connector info
223225
func Connect(c Connector) (string, error) {
224-
226+
var lastErr error
225227
if c.RetryCount == 0 {
226228
c.RetryCount = 10
227229
}
@@ -249,6 +251,11 @@ func Connect(c Connector) (string, error) {
249251
for _, p := range c.TargetPortals {
250252
debug.Printf("process portal: %s\n", p)
251253
baseArgs := []string{"-m", "node", "-T", c.TargetIqn, "-p", p}
254+
// Rescan sessions to discover newly mapped LUNs. Do not specify the interface when rescanning
255+
// to avoid establishing additional sessions to the same target.
256+
if _, err := iscsiCmd(append(baseArgs, []string{"-R"}...)...); err != nil {
257+
debug.Printf("failed to rescan session, err: %v", err)
258+
}
252259

253260
// create our devicePath that we'll be looking for based on the transport being used
254261
if c.Port != "" {
@@ -270,31 +277,45 @@ func Connect(c Connector) (string, error) {
270277
}
271278
}
272279

273-
// create db entry
274-
args := append(baseArgs, []string{"-I", iFace, "-o", "new"}...)
275-
debug.Printf("create the new record: %s\n", args)
280+
if c.DoDiscovery {
281+
// build discoverydb and discover iscsi target
282+
if err := Discovery(p, iFace, c.DiscoverySecrets, c.DoCHAPDiscovery); err != nil {
283+
debug.Printf("Error in discovery of the target: %s\n", err.Error())
284+
lastErr = err
285+
continue
286+
}
287+
}
288+
276289
// Make sure we don't log the secrets
277-
err := CreateDBEntry(c.TargetIqn, p, iFace, c.DiscoverySecrets, c.SessionSecrets)
290+
err := CreateDBEntry(c.TargetIqn, p, iFace, c.DiscoverySecrets, c.SessionSecrets, c.DoCHAPDiscovery)
278291
if err != nil {
279292
debug.Printf("Error creating db entry: %s\n", err.Error())
280293
continue
281294
}
295+
282296
// perform the login
283297
err = Login(c.TargetIqn, p)
284298
if err != nil {
285-
return "", err
299+
debug.Printf("failed to login, err: %v", err)
300+
lastErr = err
301+
continue
286302
}
287303
retries := int(c.RetryCount / c.CheckInterval)
288304
if exists, err := waitForPathToExist(&devicePath, retries, int(c.CheckInterval), iscsiTransport); exists {
289305
devicePaths = append(devicePaths, devicePath)
290306
continue
291307
} else if err != nil {
292-
return "", err
293-
}
294-
if len(devicePaths) < 1 {
295-
return "", fmt.Errorf("failed to find device path: %s", devicePath)
308+
lastErr = fmt.Errorf("Couldn't attach disk, err: %v", err)
296309
}
310+
}
311+
312+
if len(devicePaths) < 1 {
313+
iscsiCmd([]string{"-m", "iface", "-I", iFace, "-o", "delete"}...)
314+
return "", fmt.Errorf("failed to find device path: %s, last error seen: %v", devicePaths, lastErr)
315+
}
297316

317+
if lastErr != nil {
318+
debug.Printf("Last error occured during iscsi init: \n%v", lastErr)
298319
}
299320

300321
for i, path := range devicePaths {

iscsi/iscsiadm.go

Lines changed: 65 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -88,13 +88,17 @@ func ShowInterface(iface string) (string, error) {
8888
}
8989

9090
// CreateDBEntry sets up a node entry for the specified tgt in the nodes iscsi nodes db
91-
func CreateDBEntry(tgtIQN, portal, iFace string, discoverySecrets, sessionSecrets Secrets) error {
91+
func CreateDBEntry(tgtIQN, portal, iFace string, discoverySecrets, sessionSecrets Secrets, chapDiscovery bool) error {
9292
debug.Println("Begin CreateDBEntry...")
93+
if !chapDiscovery {
94+
return nil
95+
}
9396
baseArgs := []string{"-m", "node", "-T", tgtIQN, "-p", portal}
9497
_, err := iscsiCmd(append(baseArgs, []string{"-I", iFace, "-o", "new"}...)...)
9598
if err != nil {
9699
return err
97100
}
101+
98102
if discoverySecrets.SecretsType == "chap" {
99103
debug.Printf("Setting CHAP Discovery...")
100104
createCHAPEntries(baseArgs, discoverySecrets, true)
@@ -103,25 +107,59 @@ func CreateDBEntry(tgtIQN, portal, iFace string, discoverySecrets, sessionSecret
103107
if sessionSecrets.SecretsType == "chap" {
104108
debug.Printf("Setting CHAP Session...")
105109
createCHAPEntries(baseArgs, sessionSecrets, false)
106-
107110
}
111+
108112
return err
109113

110114
}
111115

116+
func updateISCSIDiscoverydb(tp, iface string, discoverySecrets Secrets) error {
117+
baseArgs := []string{"-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", iface}
118+
_, err := iscsiCmd(append(baseArgs, []string{"-o", "update", "-n", "discovery.sendtargets.auth.authmethod", "-v", "CHAP"}...)...)
119+
if err != nil {
120+
return fmt.Errorf("failed to update discoverydb with CHAP, err: %v", err)
121+
}
122+
123+
return createCHAPEntries(baseArgs, discoverySecrets, true)
124+
}
125+
126+
// Discovery discover the iscsi target
127+
func Discovery(tp, iface string, discoverySecrets Secrets, chapDiscovery bool) error {
128+
debug.Println("Begin Discovery...")
129+
baseArgs := []string{"-m", "discoverydb", "-t", "sendtargets", "-p", tp, "-I", iface}
130+
out, err := iscsiCmd(append(baseArgs, []string{"-o", "new"}...)...)
131+
if err != nil {
132+
return fmt.Errorf("failed to create new entry of target in discoverydb, output: %v, err: %v", string(out), err)
133+
}
134+
135+
if chapDiscovery {
136+
if err := updateISCSIDiscoverydb(tp, iface, discoverySecrets); err != nil {
137+
return err
138+
}
139+
}
140+
141+
_, err = iscsiCmd(append(baseArgs, []string{"--discover"}...)...)
142+
if err != nil {
143+
//delete the discoverydb record
144+
iscsiCmd(append(baseArgs, []string{"-o", "delete"}...)...)
145+
return fmt.Errorf("failed to sendtargets to portal %s, err: %v", tp, err)
146+
}
147+
return nil
148+
}
149+
112150
func createCHAPEntries(baseArgs []string, secrets Secrets, discovery bool) error {
113151
args := []string{}
114152
debug.Printf("Begin createCHAPEntries (discovery=%t)...", discovery)
115153
if discovery {
116154
args = append(baseArgs, []string{"-o", "update",
117-
"-n", "node.discovery.auth.authmethod", "-v", "CHAP",
118-
"-n", "node.discovery.auth.username", "-v", secrets.UserName,
119-
"-n", "node.discovery.auth.password", "-v", secrets.Password}...)
155+
"-n", "discovery.sendtargets.auth.authmethod", "-v", "CHAP",
156+
"-n", "discovery.sendtargets.auth.username", "-v", secrets.UserName,
157+
"-n", "discovery.sendtargets.auth.password", "-v", secrets.Password}...)
120158
if secrets.UserNameIn != "" {
121-
args = append(args, []string{"-n", "node.discovery.auth.username_in", "-v", secrets.UserNameIn}...)
159+
args = append(args, []string{"-n", "discovery.sendtargets.auth.username_in", "-v", secrets.UserNameIn}...)
122160
}
123-
if secrets.UserNameIn != "" {
124-
args = append(args, []string{"-n", "node.discovery.auth.password_in", "-v", secrets.PasswordIn}...)
161+
if secrets.PasswordIn != "" {
162+
args = append(args, []string{"-n", "discovery.sendtargets.auth.password_in", "-v", secrets.PasswordIn}...)
125163
}
126164

127165
} else {
@@ -133,13 +171,17 @@ func createCHAPEntries(baseArgs []string, secrets Secrets, discovery bool) error
133171
if secrets.UserNameIn != "" {
134172
args = append(args, []string{"-n", "node.session.auth.username_in", "-v", secrets.UserNameIn}...)
135173
}
136-
if secrets.UserNameIn != "" {
174+
if secrets.PasswordIn != "" {
137175
args = append(args, []string{"-n", "node.session.auth.password_in", "-v", secrets.PasswordIn}...)
138176
}
139177
}
178+
140179
_, err := iscsiCmd(args...)
141-
return err
180+
if err != nil {
181+
return fmt.Errorf("failed to update discoverydb with CHAP, err: %v", err)
182+
}
142183

184+
return nil
143185
}
144186

145187
// GetSessions retrieves a list of current iscsi sessions on the node
@@ -151,7 +193,13 @@ func GetSessions() (string, error) {
151193

152194
// Login performs an iscsi login for the specified target
153195
func Login(tgtIQN, portal string) error {
154-
_, err := iscsiCmd([]string{"-m", "node", "-T", tgtIQN, "-p", portal, "-l"}...)
196+
baseArgs := []string{"-m", "node", "-T", tgtIQN, "-p", portal}
197+
_, err := iscsiCmd(append(baseArgs, []string{"-l"}...)...)
198+
if err != nil {
199+
//delete the node record from database
200+
iscsiCmd(append(baseArgs, []string{"-o", "delete"}...)...)
201+
return fmt.Errorf("failed to sendtargets to portal %s, err: %v", portal, err)
202+
}
155203
return err
156204
}
157205

@@ -173,5 +221,11 @@ func DeleteDBEntry(tgtIQN string) error {
173221
args := []string{"-m", "node", "-T", tgtIQN, "-o", "delete"}
174222
iscsiCmd(args...)
175223
return nil
224+
}
176225

226+
// DeleteIFace delete the iface
227+
func DeleteIFace(iface string) error {
228+
debug.Println("Begin DeleteIFace...")
229+
iscsiCmd([]string{"-m", "iface", "-I", iface, "-o", "delete"}...)
230+
return nil
177231
}

iscsi/iscsiadm_test.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
package iscsi
2+
3+
import "testing"
4+
5+
func TestDiscovery(t *testing.T) {
6+
execCommand = fakeExecCommand
7+
tests := map[string]struct {
8+
tgtPortal string
9+
iface string
10+
discoverySecret Secrets
11+
chapDiscovery bool
12+
wantErr bool
13+
mockedStdout string
14+
mockedExitStatus int
15+
}{
16+
"DiscoverySuccess": {
17+
tgtPortal: "172.18.0.2:3260",
18+
iface: "default",
19+
chapDiscovery: false,
20+
mockedStdout: "172.18.0.2:3260,1 iqn.2016-09.com.openebs.jiva:store1\n",
21+
mockedExitStatus: 0,
22+
},
23+
24+
"ConnectionFailure": {
25+
tgtPortal: "172.18.0.2:3262",
26+
iface: "default",
27+
chapDiscovery: false,
28+
mockedStdout: `iscsiadm: cannot make connection to 172.18.0.2: Connection refused
29+
iscsiadm: cannot make connection to 172.18.0.2: Connection refused
30+
iscsiadm: connection login retries (reopen_max) 5 exceeded
31+
iscsiadm: Could not perform SendTargets discovery: encountered connection failure\n`,
32+
mockedExitStatus: 4,
33+
wantErr: true,
34+
},
35+
36+
"ChapEntrySuccess": {
37+
tgtPortal: "172.18.0.2:3260",
38+
iface: "default",
39+
chapDiscovery: true,
40+
discoverySecret: Secrets{
41+
UserNameIn: "dummyuser",
42+
PasswordIn: "dummypass",
43+
},
44+
mockedStdout: "172.18.0.2:3260,1 iqn.2016-09.com.openebs.jiva:store1\n",
45+
mockedExitStatus: 0,
46+
},
47+
48+
"ChapEntryFailure": {
49+
tgtPortal: "172.18.0.2:3260",
50+
iface: "default",
51+
discoverySecret: Secrets{
52+
UserNameIn: "dummyuser",
53+
PasswordIn: "dummypass",
54+
},
55+
chapDiscovery: true,
56+
mockedStdout: `iscsiadm: Login failed to authenticate with target
57+
iscsiadm: discovery login to 172.18.0.2 rejected: initiator error (02/01), non-retryable, giving up
58+
iscsiadm: Could not perform SendTargets discovery.\n`,
59+
mockedExitStatus: 4,
60+
wantErr: true,
61+
},
62+
}
63+
64+
for name, tt := range tests {
65+
t.Run(name, func(t *testing.T) {
66+
mockedExitStatus = tt.mockedExitStatus
67+
mockedStdout = tt.mockedStdout
68+
err := Discovery(tt.tgtPortal, tt.iface, tt.discoverySecret, tt.chapDiscovery)
69+
if (err != nil) != tt.wantErr {
70+
t.Errorf("Discovery() error = %v, wantErr %v", err, tt.wantErr)
71+
return
72+
}
73+
})
74+
}
75+
}
76+
77+
func TestCreateDBEntry(t *testing.T) {
78+
execCommand = fakeExecCommand
79+
tests := map[string]struct {
80+
tgtPortal string
81+
tgtIQN string
82+
iface string
83+
discoverySecret Secrets
84+
sessionSecret Secrets
85+
chapDiscovery bool
86+
wantErr bool
87+
mockedStdout string
88+
mockedExitStatus int
89+
}{
90+
"CreateDBEntrySuccess": {
91+
tgtPortal: "192.168.1.107:3260",
92+
tgtIQN: "iqn.2010-10.org.openstack:volume-eb393993-73d0-4e39-9ef4-b5841e244ced",
93+
iface: "default",
94+
chapDiscovery: false,
95+
mockedStdout: nodeDB,
96+
mockedExitStatus: 0,
97+
},
98+
"CreateDBEntryWithChapDiscoverySuccess": {
99+
tgtPortal: "192.168.1.107:3260",
100+
tgtIQN: "iqn.2010-10.org.openstack:volume-eb393993-73d0-4e39-9ef4-b5841e244ced",
101+
iface: "default",
102+
discoverySecret: Secrets{
103+
UserNameIn: "dummyuser",
104+
PasswordIn: "dummypass",
105+
SecretsType: "chap",
106+
},
107+
sessionSecret: Secrets{
108+
UserNameIn: "dummyuser",
109+
PasswordIn: "dummypass",
110+
SecretsType: "chap",
111+
},
112+
chapDiscovery: true,
113+
mockedStdout: nodeDB,
114+
mockedExitStatus: 0,
115+
},
116+
"CreateDBEntryWithChapDiscoveryFailure": {
117+
tgtPortal: "172.18.0.2:3260",
118+
tgtIQN: "iqn.2016-09.com.openebs.jiva:store1",
119+
iface: "default",
120+
chapDiscovery: true,
121+
mockedStdout: "iscsiadm: No records found\n",
122+
mockedExitStatus: 21,
123+
wantErr: true,
124+
},
125+
}
126+
127+
for name, tt := range tests {
128+
t.Run(name, func(t *testing.T) {
129+
mockedExitStatus = tt.mockedExitStatus
130+
mockedStdout = tt.mockedStdout
131+
err := CreateDBEntry(tt.tgtIQN, tt.tgtPortal, tt.iface, tt.discoverySecret, tt.sessionSecret, tt.chapDiscovery)
132+
if (err != nil) != tt.wantErr {
133+
t.Errorf("CreateDBEntry() error = %v, wantErr %v", err, tt.wantErr)
134+
return
135+
}
136+
})
137+
}
138+
139+
}

0 commit comments

Comments
 (0)