forked from stmcginnis/gofish
-
Notifications
You must be signed in to change notification settings - Fork 0
/
storagegroup.go
341 lines (304 loc) · 11.7 KB
/
storagegroup.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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
//
// SPDX-License-Identifier: BSD-3-Clause
//
package swordfish
import (
"encoding/json"
"reflect"
"github.com/stmcginnis/gofish/common"
)
// AuthenticationMethod is method used to authenticate.
type AuthenticationMethod string
const (
// NoneAuthenticationMethod No authentication is used.
NoneAuthenticationMethod AuthenticationMethod = "None"
// CHAPAuthenticationMethod iSCSI Challenge Handshake Authentication
// Protocol (CHAP) authentication is used.
CHAPAuthenticationMethod AuthenticationMethod = "CHAP"
// MutualCHAPAuthenticationMethod iSCSI Mutual Challenge Handshake
// Authentication Protocol (CHAP) authentication is used.
MutualCHAPAuthenticationMethod AuthenticationMethod = "MutualCHAP"
// DHCHAPAuthenticationMethod Diffie-Hellman Challenge Handshake
// Authentication Protocol (DHCHAP) is an authentication protocol used in
// Fibre Channel. DHCHAP implies that only properties 'TargetCHAPUser'
// and 'TargetPassword' need to be present.
DHCHAPAuthenticationMethod AuthenticationMethod = "DHCHAP"
)
// CHAPInformation is used for CHAP auth.
type CHAPInformation struct {
// InitiatorCHAPPassword shall be the
// shared secret for CHAP authentication.
InitiatorCHAPPassword string
// InitiatorCHAPUser is If present, this property is the initiator CHAP
// username for authentication. For example, with an iSCSI scenario, use
// the initiator iQN.
InitiatorCHAPUser string
// TargetCHAPUser shall be the CHAP
// Username for 2-way CHAP authentication. For example, with an iSCSI
// scenario, use the target iQN. In a FC with DHCHAP, this value will be
// a FC WWN.
TargetCHAPUser string
// TargetPassword shall be the CHAP Secret
// for 2-way CHAP authentication.
TargetPassword string
}
// StorageGroup is a set of related storage entities (volumes, file systems...)
// The collection should be useful for managing the storage of a set of related
// client applications.
type StorageGroup struct {
common.Entity
// ODataContext is the odata context.
ODataContext string `json:"@odata.context"`
// ODataType is the odata type.
ODataType string `json:"@odata.type"`
// AccessState shall describe the access
// characteristics of this storage group. All associated logical units
// through all aggregated ports shall share this access state.
AccessState AccessState
// AuthenticationMethod is The value of this property must be what kind
// of authentication that the endpoints in this StorageGroup understands.
AuthenticationMethod AuthenticationMethod
// ChapInfo is used by this specific endpoint. For example, if this
// endpoint represents an initiator, and AuthenticationMethod is CHAP or
// MutualCHAP, the Credentials fields CHAPUsername and CHAPSecret must be
// used. If this endpoint represents a target endpoint and
// AuthenticationMethod is MutualCHAP, then MutualCHAPUsername and
// MutualCHAPSecret must be used.
ChapInfo []CHAPInformation
// ClientEndpointGroups is used to make requests to the storage exposed
// by this StorageGroup. If null, the implementation may allow access to
// the storage via any client-side endpoint. If empty, the
// implementation shall not allow access to the storage via any client-
// side endpoint.
ClientEndpointGroups []EndpointGroup
// ClientEndpointGroups@odata.count is
ClientEndpointGroupsCount int `json:"ClientEndpointGroups@odata.count"`
// Description provides a description of this resource.
Description string
// Identifier shall be unique within the managed ecosystem.
Identifier common.Identifier
// mappedVolumes is an array of mapped volumes managed by this storage
// group.
MappedVolumes []MappedVolume
// MembersAreConsistent shall be set to true if all members are in a
// consistent state. The default value for this property is false.
MembersAreConsistent bool
// ReplicaInfo shall describe the replication relationship between this
// storage group and a corresponding source storage group.
ReplicaInfo ReplicaInfo
// ReplicaTargets shall reference the target replicas that
// are sourced by this replica.
// replicaTargets []string
// ReplicaTargetsCount is number of replica targets.
ReplicaTargetsCount int `json:"ReplicaTargets@odata.count"`
// serverEndpointGroups is used to make requests to the storage exposed
// by this storage group. If null, the implementation may allow access
// to the storage via any server-side endpoint. If empty, the
// implementation shall not allow access to the storage via any server-
// side endpoint.
// serverEndpointGroups []string
// ServerEndpointGroupsCount is the number of server endpoints.
ServerEndpointGroupsCount int `json:"ServerEndpointGroups@odata.count"`
// Status is the status of this group.
Status common.Status
// VolumesCount is the number of volumes.
VolumesCount int `json:"Volumes@odata.count"`
// VolumesAreExposed shall be set to true if storage volumes are exposed to
// the paths defined by the client and server endpoints. The default value
// for this property is false.
VolumesAreExposed bool
// ChildStorageGroups is an array of references to StorageGroups are
// incorporated into this StorageGroup
childStorageGroups []string
// ChildStorageGroupsCount is the number of child storage groups.
ChildStorageGroupsCount int
// ClassOfService is the ClassOfService that all storage in this
// StorageGroup conforms to.
classOfService string
// ParentStorageGroups is an array of references to StorageGroups that
// incorporate this StorageGroup
parentStorageGroups []string
// ParentStorageGroupsCount is the number of parent storage groups.
ParentStorageGroupsCount int
// exposeVolumesTarget is the URL to for the ExposeVolumes action.
exposeVolumesTarget string
// hideVolumesTarget is the URL to for the HideVolumes action.
hideVolumesTarget string
// rawData holds the original serialized JSON so we can compare updates.
rawData []byte
}
// UnmarshalJSON unmarshals a StorageGroup object from the raw JSON.
func (storagegroup *StorageGroup) UnmarshalJSON(b []byte) error {
type temp StorageGroup
type links struct {
ChildStorageGroups common.Links
ChildStorageGroupsCount int `json:"ChildStorageGroups@odata.count"`
ClassOfService common.Link
ParentStorageGroups common.Links
ParentStorageGroupsCount int `json:"ParentStorageGroups@odata.count"`
}
type actions struct {
ExposeVolumes struct {
Target string
} `json:"#StorageGroup.ExposeVolumes"`
HideVolumes struct {
Target string
} `json:"#StorageGroup.HideVolumes"`
}
var t struct {
temp
Links links
ServerEndpointGroups common.Links
Actions actions
}
err := json.Unmarshal(b, &t)
if err != nil {
return err
}
// Extract the links to other entities for later
*storagegroup = StorageGroup(t.temp)
storagegroup.childStorageGroups = t.Links.ChildStorageGroups.ToStrings()
storagegroup.ChildStorageGroupsCount = t.Links.ChildStorageGroupsCount
storagegroup.classOfService = t.Links.ClassOfService.String()
storagegroup.parentStorageGroups = t.Links.ParentStorageGroups.ToStrings()
storagegroup.ParentStorageGroupsCount = t.Links.ParentStorageGroupsCount
storagegroup.exposeVolumesTarget = t.Actions.ExposeVolumes.Target
storagegroup.hideVolumesTarget = t.Actions.HideVolumes.Target
// This is a read/write object, so we need to save the raw object data for later
storagegroup.rawData = b
return nil
}
// Update commits updates to this object's properties to the running system.
func (storagegroup *StorageGroup) Update() error {
// Get a representation of the object's original state so we can find what
// to update.
original := new(StorageGroup)
err := original.UnmarshalJSON(storagegroup.rawData)
if err != nil {
return err
}
readWriteFields := []string{
"AccessState",
"AuthenticationMethod",
"ClientEndpointGroups",
"ServerEndpointGroups",
"VolumesAreExposed",
}
originalElement := reflect.ValueOf(original).Elem()
currentElement := reflect.ValueOf(storagegroup).Elem()
return storagegroup.Entity.Update(originalElement, currentElement, readWriteFields)
}
// GetStorageGroup will get a StorageGroup instance from the service.
func GetStorageGroup(c common.Client, uri string) (*StorageGroup, error) {
var storageGroup StorageGroup
return &storageGroup, storageGroup.Get(c, uri, &storageGroup)
}
// ListReferencedStorageGroups gets the collection of StorageGroup from
// a provided reference.
func ListReferencedStorageGroups(c common.Client, link string) ([]*StorageGroup, error) { //nolint:dupl
var result []*StorageGroup
if link == "" {
return result, nil
}
type GetResult struct {
Item *StorageGroup
Link string
Error error
}
ch := make(chan GetResult)
collectionError := common.NewCollectionError()
get := func(link string) {
storagegroup, err := GetStorageGroup(c, link)
ch <- GetResult{Item: storagegroup, Link: link, Error: err}
}
go func() {
err := common.CollectList(get, c, link)
if err != nil {
collectionError.Failures[link] = err
}
close(ch)
}()
for r := range ch {
if r.Error != nil {
collectionError.Failures[r.Link] = r.Error
} else {
result = append(result, r.Item)
}
}
if collectionError.Empty() {
return result, nil
}
return result, collectionError
}
// ChildStorageGroups gets child groups of this group.
func (storagegroup *StorageGroup) ChildStorageGroups() ([]*StorageGroup, error) {
var result []*StorageGroup
collectionError := common.NewCollectionError()
for _, sgLink := range storagegroup.childStorageGroups {
sg, err := GetStorageGroup(storagegroup.Client, sgLink)
if err != nil {
collectionError.Failures[sgLink] = err
} else {
result = append(result, sg)
}
}
if collectionError.Empty() {
return result, nil
}
return result, collectionError
}
// ParentStorageGroups gets parent groups of this group.
func (storagegroup *StorageGroup) ParentStorageGroups() ([]*StorageGroup, error) {
var result []*StorageGroup
collectionError := common.NewCollectionError()
for _, sgLink := range storagegroup.parentStorageGroups {
sg, err := GetStorageGroup(storagegroup.Client, sgLink)
if err != nil {
collectionError.Failures[sgLink] = err
} else {
result = append(result, sg)
}
}
if collectionError.Empty() {
return result, nil
}
return result, collectionError
}
// ClassOfService gets the ClassOfService that all storage in this StorageGroup
// conforms to.
func (storagegroup *StorageGroup) ClassOfService() (*ClassOfService, error) {
if storagegroup.classOfService == "" {
return nil, nil
}
return GetClassOfService(storagegroup.Client, storagegroup.classOfService)
}
// MappedVolume is an exposed volume mapping.
type MappedVolume struct {
// LogicalUnitNumber is the value is a SCSI Logical Unit Number for the Volume.
LogicalUnitNumber int
// Volume shall reference a mapped Volume.
Volume common.Link
}
// ExposeVolumes exposes the storage of this group via the target endpoints
// named in the ServerEndpointGroups to the initiator endpoints named in the
// ClientEndpointGroups. The property VolumesAreExposed shall be set to true
// when this action is completed.
func (storagegroup *StorageGroup) ExposeVolumes() error {
err := storagegroup.Post(storagegroup.exposeVolumesTarget, nil)
if err == nil {
// Only set to exposed if no error. Calling expose when already exposed
// could fail so we don't want to indicate they are not exposed.
storagegroup.VolumesAreExposed = true
}
return err
}
// HideVolumes hides the storage of this group from the initiator endpoints
// named in the ClientEndpointGroups. The property VolumesAreExposed shall be
// set to false when this action is completed.
func (storagegroup *StorageGroup) HideVolumes() error {
err := storagegroup.Post(storagegroup.hideVolumesTarget, nil)
if err == nil {
storagegroup.VolumesAreExposed = false
}
return err
}