1
1
package iscsi
2
2
3
3
import (
4
- "encoding/json"
5
4
"fmt"
5
+ "strconv"
6
+ "strings"
6
7
7
- "github.com/kubernetes-csi/csi-proxy/pkg/utils"
8
+ "github.com/kubernetes-csi/csi-proxy/pkg/cim"
9
+ "github.com/microsoft/wmi/server2019/root/microsoft/windows/storage"
10
+ "k8s.io/klog/v2"
8
11
)
9
12
10
13
// Implements the iSCSI OS API calls. All code here should be very simple
@@ -18,119 +21,178 @@ func New() APIImplementor {
18
21
return APIImplementor {}
19
22
}
20
23
24
+ func parseTargetPortal (instance * storage.MSFT_iSCSITargetPortal ) (string , uint32 , error ) {
25
+ portalAddress , err := instance .GetPropertyTargetPortalAddress ()
26
+ if err != nil {
27
+ return "" , 0 , fmt .Errorf ("failed parsing target portal address %v. err: %w" , instance , err )
28
+ }
29
+
30
+ portalPort , err := instance .GetProperty ("TargetPortalPortNumber" )
31
+ if err != nil {
32
+ return "" , 0 , fmt .Errorf ("failed parsing target portal port number %v. err: %w" , instance , err )
33
+ }
34
+
35
+ return portalAddress , uint32 (portalPort .(int32 )), nil
36
+ }
37
+
21
38
func (APIImplementor ) AddTargetPortal (portal * TargetPortal ) error {
22
- cmdLine := fmt .Sprintf (
23
- `New-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} ` +
24
- `-TargetPortalPortNumber ${Env:iscsi_tp_port}` )
25
- out , err := utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("iscsi_tp_address=%s" , portal .Address ),
26
- fmt .Sprintf ("iscsi_tp_port=%d" , portal .Port ))
39
+ existing , err := cim .QueryISCSITargetPortal (portal .Address , portal .Port , nil )
40
+ if cim .IgnoreNotFound (err ) != nil {
41
+ return err
42
+ }
43
+
44
+ if existing != nil {
45
+ klog .V (2 ).Infof ("target portal at (%s:%d) already exists" , portal .Address , portal .Port )
46
+ return nil
47
+ }
48
+
49
+ _ , err = cim .NewISCSITargetPortal (portal .Address , portal .Port , nil , nil , nil , nil )
27
50
if err != nil {
28
- return fmt .Errorf ("error adding target portal. cmd %s, output: %s, err: %v" , cmdLine , string ( out ) , err )
51
+ return fmt .Errorf ("error adding target portal at (%s:%d). err: %v" , portal . Address , portal . Port , err )
29
52
}
30
53
31
54
return nil
32
55
}
33
56
34
57
func (APIImplementor ) DiscoverTargetPortal (portal * TargetPortal ) ([]string , error ) {
35
- // ConvertTo-Json is not part of the pipeline because powershell converts an
36
- // array with one element to a single element
37
- cmdLine := fmt .Sprintf (
38
- `ConvertTo-Json -InputObject @(Get-IscsiTargetPortal -TargetPortalAddress ` +
39
- `${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port} | ` +
40
- `Get-IscsiTarget | Select-Object -ExpandProperty NodeAddress)` )
41
- out , err := utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("iscsi_tp_address=%s" , portal .Address ),
42
- fmt .Sprintf ("iscsi_tp_port=%d" , portal .Port ))
58
+ instance , err := cim .QueryISCSITargetPortal (portal .Address , portal .Port , nil )
43
59
if err != nil {
44
- return nil , fmt . Errorf ( "error discovering target portal. cmd: %s, output: %s, err: %w" , cmdLine , string ( out ), err )
60
+ return nil , err
45
61
}
46
62
47
- var iqns []string
48
- err = json .Unmarshal (out , & iqns )
63
+ targets , err := cim .ListISCSITargetsByTargetPortalWithFilters (nil , []* storage.MSFT_iSCSITargetPortal {instance })
49
64
if err != nil {
50
- return nil , fmt .Errorf ("failed parsing iqn list. cmd: %s output: %s, err: %w" , cmdLine , string (out ), err )
65
+ return nil , err
66
+ }
67
+
68
+ var iqns []string
69
+ for _ , target := range targets {
70
+ iqn , err := target .GetProperty ("NodeAddress" )
71
+ if err != nil {
72
+ return nil , fmt .Errorf ("failed parsing node address of target %v to target portal at (%s:%d). err: %w" , target , portal .Address , portal .Port , err )
73
+ }
74
+
75
+ iqns = append (iqns , iqn .(string ))
51
76
}
52
77
53
78
return iqns , nil
54
79
}
55
80
56
81
func (APIImplementor ) ListTargetPortals () ([]TargetPortal , error ) {
57
- cmdLine := fmt .Sprintf (
58
- `ConvertTo-Json -InputObject @(Get-IscsiTargetPortal | ` +
59
- `Select-Object TargetPortalAddress, TargetPortalPortNumber)` )
60
-
61
- out , err := utils .RunPowershellCmd (cmdLine )
82
+ instances , err := cim .ListISCSITargetPortals ([]string {"TargetPortalAddress" , "TargetPortalPortNumber" })
62
83
if err != nil {
63
- return nil , fmt . Errorf ( "error listing target portals. cmd %s, output: %s, err: %w" , cmdLine , string ( out ), err )
84
+ return nil , err
64
85
}
65
86
66
87
var portals []TargetPortal
67
- err = json .Unmarshal (out , & portals )
68
- if err != nil {
69
- return nil , fmt .Errorf ("failed parsing target portal list. cmd: %s output: %s, err: %w" , cmdLine , string (out ), err )
88
+ for _ , instance := range instances {
89
+ address , port , err := parseTargetPortal (instance )
90
+ if err != nil {
91
+ return nil , fmt .Errorf ("failed parsing target portal %v. err: %w" , instance , err )
92
+ }
93
+
94
+ portals = append (portals , TargetPortal {
95
+ Address : address ,
96
+ Port : port ,
97
+ })
70
98
}
71
99
72
100
return portals , nil
73
101
}
74
102
75
103
func (APIImplementor ) RemoveTargetPortal (portal * TargetPortal ) error {
76
- cmdLine := fmt . Sprintf (
77
- `Get-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} ` +
78
- `-TargetPortalPortNumber ${Env:iscsi_tp_port} | Remove-IscsiTargetPortal ` +
79
- `-Confirm:$false` )
104
+ instance , err := cim . QueryISCSITargetPortal ( portal . Address , portal . Port , nil )
105
+ if err != nil {
106
+ return err
107
+ }
80
108
81
- out , err := utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("iscsi_tp_address=%s" , portal .Address ),
82
- fmt .Sprintf ("iscsi_tp_port=%d" , portal .Port ))
109
+ address , port , err := parseTargetPortal (instance )
83
110
if err != nil {
84
- return fmt .Errorf ("error removing target portal. cmd %s, output: %s, err: %w" , cmdLine , string (out ), err )
111
+ return fmt .Errorf ("failed to parse target portal %v. error: %v" , instance , err )
112
+ }
113
+
114
+ result , err := instance .InvokeMethodWithReturn ("Remove" ,
115
+ nil ,
116
+ nil ,
117
+ int (port ),
118
+ address ,
119
+ )
120
+ if result != 0 || err != nil {
121
+ return fmt .Errorf ("error removing target portal at (%s:%d). result: %d, err: %w" , address , port , result , err )
85
122
}
86
123
87
124
return nil
88
125
}
89
126
90
127
func (APIImplementor ) ConnectTarget (portal * TargetPortal , iqn string ,
91
128
authType string , chapUser string , chapSecret string ) error {
92
- // Not using InputObject as Connect-IscsiTarget's InputObject does not work.
93
- // This is due to being a static WMI method together with a bug in the
94
- // powershell version of the API.
95
- cmdLine := fmt .Sprintf (
96
- `Connect-IscsiTarget -TargetPortalAddress ${Env:iscsi_tp_address}` +
97
- ` -TargetPortalPortNumber ${Env:iscsi_tp_port} -NodeAddress ${Env:iscsi_target_iqn}` +
98
- ` -AuthenticationType ${Env:iscsi_auth_type}` )
129
+ target , err := cim .QueryISCSITarget (portal .Address , portal .Port , iqn , nil )
130
+ if err != nil {
131
+ return err
132
+ }
99
133
100
- if chapUser != "" {
101
- cmdLine += ` -ChapUsername ${Env:iscsi_chap_user}`
134
+ connected , err := target .GetPropertyIsConnected ()
135
+ if err != nil {
136
+ return err
102
137
}
103
138
104
- if chapSecret != "" {
105
- cmdLine += ` -ChapSecret ${Env:iscsi_chap_secret}`
139
+ if connected {
140
+ klog .V (2 ).Infof ("target %s from target portal at (%s:%d) is connected." , iqn , portal .Address , portal .Port )
141
+ return nil
106
142
}
107
143
108
- out , err := utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("iscsi_tp_address=%s" , portal .Address ),
109
- fmt .Sprintf ("iscsi_tp_port=%d" , portal .Port ),
110
- fmt .Sprintf ("iscsi_target_iqn=%s" , iqn ),
111
- fmt .Sprintf ("iscsi_auth_type=%s" , authType ),
112
- fmt .Sprintf ("iscsi_chap_user=%s" , chapUser ),
113
- fmt .Sprintf ("iscsi_chap_secret=%s" , chapSecret ))
144
+ targetAuthType := strings .ToUpper (strings .ReplaceAll (authType , "_" , "" ))
145
+
146
+ result , _ , err := cim .ConnectISCSITarget (portal .Address , portal .Port , iqn , targetAuthType , & chapUser , & chapSecret )
114
147
if err != nil {
115
- return fmt .Errorf ("error connecting to target portal. cmd %s, output : %s , err: %w" , cmdLine , string ( out ) , err )
148
+ return fmt .Errorf ("error connecting to target portal. result : %d , err: %w" , result , err )
116
149
}
117
150
118
151
return nil
119
152
}
120
153
121
154
func (APIImplementor ) DisconnectTarget (portal * TargetPortal , iqn string ) error {
122
- // Using InputObject instead of pipe to verify input is not empty
123
- cmdLine := fmt .Sprintf (
124
- `Disconnect-IscsiTarget -InputObject (Get-IscsiTargetPortal ` +
125
- `-TargetPortalAddress ${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port} ` +
126
- ` | Get-IscsiTarget | Where-Object { $_.NodeAddress -eq ${Env:iscsi_target_iqn} }) ` +
127
- `-Confirm:$false` )
155
+ target , err := cim .QueryISCSITarget (portal .Address , portal .Port , iqn , nil )
156
+ if err != nil {
157
+ return err
158
+ }
159
+
160
+ connected , err := target .GetPropertyIsConnected ()
161
+ if err != nil {
162
+ return fmt .Errorf ("error query connected of target %s from target portal at (%s:%d). err: %w" , iqn , portal .Address , portal .Port , err )
163
+ }
164
+
165
+ if ! connected {
166
+ klog .V (2 ).Infof ("target %s from target portal at (%s:%d) is not connected." , iqn , portal .Address , portal .Port )
167
+ return nil
168
+ }
128
169
129
- out , err := utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("iscsi_tp_address=%s" , portal .Address ),
130
- fmt .Sprintf ("iscsi_tp_port=%d" , portal .Port ),
131
- fmt .Sprintf ("iscsi_target_iqn=%s" , iqn ))
170
+ // get session
171
+ session , err := cim .QueryISCSISessionByTarget (target , nil )
132
172
if err != nil {
133
- return fmt .Errorf ("error disconnecting from target portal. cmd %s, output: %s, err: %w" , cmdLine , string (out ), err )
173
+ return fmt .Errorf ("error query session of target %s from target portal at (%s:%d). err: %w" , iqn , portal .Address , portal .Port , err )
174
+ }
175
+
176
+ sessionIdentifier , err := session .GetPropertySessionIdentifier ()
177
+ if err != nil {
178
+ return fmt .Errorf ("error query session identifier of target %s from target portal at (%s:%d). err: %w" , iqn , portal .Address , portal .Port , err )
179
+ }
180
+
181
+ persistent , err := session .GetPropertyIsPersistent ()
182
+ if err != nil {
183
+ return fmt .Errorf ("error query session persistency of target %s from target portal at (%s:%d). err: %w" , iqn , portal .Address , portal .Port , err )
184
+ }
185
+
186
+ if persistent {
187
+ result , err := session .InvokeMethodWithReturn ("Unregister" )
188
+ if err != nil {
189
+ return fmt .Errorf ("error unregister session on target %s from target portal at (%s:%d). result: %d, err: %w" , iqn , portal .Address , portal .Port , result , err )
190
+ }
191
+ }
192
+
193
+ result , err := target .InvokeMethodWithReturn ("Disconnect" , sessionIdentifier )
194
+ if err != nil {
195
+ return fmt .Errorf ("error disconnecting target %s from target portal at (%s:%d). result: %d, err: %w" , iqn , portal .Address , portal .Port , result , err )
134
196
}
135
197
136
198
return nil
@@ -139,36 +201,43 @@ func (APIImplementor) DisconnectTarget(portal *TargetPortal, iqn string) error {
139
201
func (APIImplementor ) GetTargetDisks (portal * TargetPortal , iqn string ) ([]string , error ) {
140
202
// Converting DiskNumber to string for compatibility with disk api group
141
203
// Not using pipeline in order to validate that items are non-empty
142
- cmdLine := fmt .Sprintf (
143
- `$ErrorActionPreference = "Stop"; ` +
144
- `$tp = Get-IscsiTargetPortal -TargetPortalAddress ${Env:iscsi_tp_address} -TargetPortalPortNumber ${Env:iscsi_tp_port}; ` +
145
- `$t = $tp | Get-IscsiTarget | Where-Object { $_.NodeAddress -eq ${Env:iscsi_target_iqn} }; ` +
146
- `$c = Get-IscsiConnection -IscsiTarget $t; ` +
147
- `$ids = $c | Get-Disk | Select -ExpandProperty Number | Out-String -Stream; ` +
148
- `ConvertTo-Json -InputObject @($ids)` )
204
+ target , err := cim .QueryISCSITarget (portal .Address , portal .Port , iqn , nil )
205
+ if err != nil {
206
+ return nil , err
207
+ }
149
208
150
- out , err := utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("iscsi_tp_address=%s" , portal .Address ),
151
- fmt .Sprintf ("iscsi_tp_port=%d" , portal .Port ),
152
- fmt .Sprintf ("iscsi_target_iqn=%s" , iqn ))
209
+ connected , err := target .GetPropertyIsConnected ()
153
210
if err != nil {
154
- return nil , fmt .Errorf ("error getting target disks. cmd %s, output: %s, err: %w" , cmdLine , string ( out ) , err )
211
+ return nil , fmt .Errorf ("error query connected of target %s from target portal at (%s:%d). err: %w" , iqn , portal . Address , portal . Port , err )
155
212
}
156
213
157
- var ids []string
158
- err = json .Unmarshal (out , & ids )
214
+ if ! connected {
215
+ klog .V (2 ).Infof ("target %s from target portal at (%s:%d) is not connected." , iqn , portal .Address , portal .Port )
216
+ return nil , nil
217
+ }
218
+
219
+ disks , err := cim .ListDisksByTarget (target , []string {})
220
+
159
221
if err != nil {
160
- return nil , fmt .Errorf ("error parsing iqn target disks. cmd: %s output: %s, err: %w" , cmdLine , string ( out ) , err )
222
+ return nil , fmt .Errorf ("error getting target disks on target %s from target portal at (%s:%d). err: %w" , iqn , portal . Address , portal . Port , err )
161
223
}
162
224
225
+ var ids []string
226
+ for _ , disk := range disks {
227
+ number , err := disk .GetProperty ("Number" )
228
+ if err != nil {
229
+ return nil , fmt .Errorf ("error getting number of disk %v on target %s from target portal at (%s:%d). err: %w" , disk , iqn , portal .Address , portal .Port , err )
230
+ }
231
+
232
+ ids = append (ids , strconv .Itoa (int (number .(int32 ))))
233
+ }
163
234
return ids , nil
164
235
}
165
236
166
237
func (APIImplementor ) SetMutualChapSecret (mutualChapSecret string ) error {
167
- cmdLine := `Set-IscsiChapSecret -ChapSecret ${Env:iscsi_mutual_chap_secret}`
168
- out , err := utils .RunPowershellCmd (cmdLine , fmt .Sprintf ("iscsi_mutual_chap_secret=%s" , mutualChapSecret ))
238
+ result , _ , err := cim .InvokeCimMethod (cim .WMINamespaceStorage , "MSFT_iSCSISession" , "SetCHAPSecret" , map [string ]interface {}{"ChapSecret" : mutualChapSecret })
169
239
if err != nil {
170
- return fmt .Errorf ("error setting mutual chap secret. cmd %s," +
171
- " output: %s, err: %v" , cmdLine , string (out ), err )
240
+ return fmt .Errorf ("error setting mutual chap secret. result: %d, err: %v" , result , err )
172
241
}
173
242
174
243
return nil
0 commit comments