-
Notifications
You must be signed in to change notification settings - Fork 21
/
storagedomain.go
360 lines (317 loc) · 13.4 KB
/
storagedomain.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
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
package ovirtclient
import (
"strings"
"sync"
ovirtsdk4 "github.com/ovirt/go-ovirt"
)
// StorageDomainID is a specialized type for storage domain IDs.
type StorageDomainID string
// StorageDomainClient contains the portion of the goVirt API that deals with storage domains.
type StorageDomainClient interface {
// ListStorageDomains lists all storage domains.
ListStorageDomains(retries ...RetryStrategy) (StorageDomainList, error)
// GetStorageDomain returns a single storage domain, or an error if the storage domain could not be found.
GetStorageDomain(id StorageDomainID, retries ...RetryStrategy) (StorageDomain, error)
// GetDiskFromStorageDomain returns a single disk from a specific storage domain, or an error if no disk can be found.
GetDiskFromStorageDomain(id StorageDomainID, diskID DiskID, retries ...RetryStrategy) (result Disk, err error)
// RemoveDiskFromStorageDomain removes a disk from a specific storage domain, but leaves the disk on other storage
// domains if any. If the disk is not present on any more storage domains, the entire disk will be removed.
RemoveDiskFromStorageDomain(id StorageDomainID, diskID DiskID, retries ...RetryStrategy) error
}
// StorageDomainData is the core of StorageDomain, providing only data access functions.
type StorageDomainData interface {
// ID is the unique identified for the storage system connected to oVirt.
ID() StorageDomainID
// Name is the user-given name for the storage domain.
Name() string
// Available returns the number of available bytes on the storage domain
Available() uint64
// StorageType returns the type of the storage domain
StorageType() StorageDomainType
// Status returns the status of the storage domain. This status may be unknown if the storage domain is external.
// Check ExternalStatus as well.
Status() StorageDomainStatus
// ExternalStatus returns the external status of a storage domain.
ExternalStatus() StorageDomainExternalStatus
}
// StorageDomain represents a storage domain returned from the oVirt Engine API.
type StorageDomain interface {
StorageDomainData
}
// StorageDomainList represents a list of storage domains.
type StorageDomainList []StorageDomain
// Filter applies the passed in filter function to all items in the current list and returns a new list with items that returned true.
func (list StorageDomainList) Filter(apply func(StorageDomain) bool) StorageDomainList {
var filtered StorageDomainList
for _, sd := range list {
if apply(sd) {
filtered = append(filtered, sd)
}
}
return filtered
}
// StorageDomainType represents the type of the storage domain.
type StorageDomainType string
const (
// StorageDomainTypeCinder represents a cinder host storage type.
StorageDomainTypeCinder StorageDomainType = "cinder"
// StorageDomainTypeFCP represents a fcp host storage type.
StorageDomainTypeFCP StorageDomainType = "fcp"
// StorageDomainTypeGlance represents a glance host storage type.
StorageDomainTypeGlance StorageDomainType = "glance"
// StorageDomainTypeGlusterFS represents a glusterfs host storage type.
StorageDomainTypeGlusterFS StorageDomainType = "glusterfs"
// StorageDomainTypeISCSI represents a iscsi host storage type.
StorageDomainTypeISCSI StorageDomainType = "iscsi"
// StorageDomainTypeLocalFS represents a localfs host storage type.
StorageDomainTypeLocalFS StorageDomainType = "localfs"
// StorageDomainTypeManagedBlockStorage represents a managed block storage host storage type.
StorageDomainTypeManagedBlockStorage StorageDomainType = "managed_block_storage"
// StorageDomainTypeNFS represents a nfs host storage type.
StorageDomainTypeNFS StorageDomainType = "nfs"
// StorageDomainTypePosixFS represents a posixfs host storage type.
StorageDomainTypePosixFS StorageDomainType = "posixfs"
)
// StorageDomainTypeList is a list of possible StorageDomainTypes.
type StorageDomainTypeList []StorageDomainType
// FileStorageDomainTypeList is a list of possible StorageDomainTypes which are considered file storage.
type FileStorageDomainTypeList []StorageDomainType
// FileStorageDomainTypeValues returns all the StorageDomainTypes values which are considered file storage.
func FileStorageDomainTypeValues() FileStorageDomainTypeList {
return []StorageDomainType{
StorageDomainTypeGlusterFS,
StorageDomainTypeLocalFS,
StorageDomainTypeNFS,
StorageDomainTypePosixFS,
}
}
// StorageDomainTypeValues returns all possible StorageDomainTypeValues values.
func StorageDomainTypeValues() StorageDomainTypeList {
return []StorageDomainType{
StorageDomainTypeCinder,
StorageDomainTypeFCP,
StorageDomainTypeGlance,
StorageDomainTypeGlusterFS,
StorageDomainTypeISCSI,
StorageDomainTypeLocalFS,
StorageDomainTypeManagedBlockStorage,
StorageDomainTypeNFS,
StorageDomainTypePosixFS,
}
}
// StorageDomainStatus represents the status a domain can be in. Either this status field, or the
// StorageDomainExternalStatus must be set.
//
// Note: this is not well documented due to missing source documentation. If you know something about these statuses
// please contribute here:
// https://github.com/oVirt/ovirt-engine-api-model/blob/master/src/main/java/types/StorageDomainStatus.java
type StorageDomainStatus string
// Validate returns an error if the storage domain status doesn't have a valid value.
func (s StorageDomainStatus) Validate() error {
for _, format := range StorageDomainStatusValues() {
if format == s {
return nil
}
}
return newError(
EBadArgument,
"invalid storage domain status: %s must be one of: %s",
s,
strings.Join(ImageFormatValues().Strings(), ", "),
)
}
const (
// StorageDomainStatusActivating indicates that the storage domain is currently activating and will soon be active.
StorageDomainStatusActivating StorageDomainStatus = "activating"
// StorageDomainStatusActive is the normal status for a storage domain when it's working.
StorageDomainStatusActive StorageDomainStatus = "active"
// StorageDomainStatusDetaching is the status when it is being disconnected.
StorageDomainStatusDetaching StorageDomainStatus = "detaching"
// StorageDomainStatusInactive is an undocumented status of the storage domain.
StorageDomainStatusInactive StorageDomainStatus = "inactive"
// StorageDomainStatusLocked is an undocumented status of the storage domain.
StorageDomainStatusLocked StorageDomainStatus = "locked"
// StorageDomainStatusMaintenance is an undocumented status of the storage domain.
StorageDomainStatusMaintenance StorageDomainStatus = "maintenance"
// StorageDomainStatusMixed is an undocumented status of the storage domain.
StorageDomainStatusMixed StorageDomainStatus = "mixed"
// StorageDomainStatusPreparingForMaintenance is an undocumented status of the storage domain.
StorageDomainStatusPreparingForMaintenance StorageDomainStatus = "preparing_for_maintenance"
// StorageDomainStatusUnattached is an undocumented status of the storage domain.
StorageDomainStatusUnattached StorageDomainStatus = "unattached"
// StorageDomainStatusUnknown is an undocumented status of the storage domain.
StorageDomainStatusUnknown StorageDomainStatus = "unknown"
// StorageDomainStatusNA indicates that the storage domain does not have a status. Please check the external status
// instead.
StorageDomainStatusNA StorageDomainStatus = ""
)
// StorageDomainStatusList is a list of StorageDomainStatus.
type StorageDomainStatusList []StorageDomainStatus
// StorageDomainStatusValues returns all possible StorageDomainStatus values.
func StorageDomainStatusValues() StorageDomainStatusList {
return []StorageDomainStatus{
StorageDomainStatusActivating,
StorageDomainStatusActive,
StorageDomainStatusDetaching,
StorageDomainStatusInactive,
StorageDomainStatusLocked,
StorageDomainStatusMaintenance,
StorageDomainStatusMixed,
StorageDomainStatusPreparingForMaintenance,
StorageDomainStatusUnattached,
StorageDomainStatusUnknown,
StorageDomainStatusNA,
}
}
// Strings creates a string list of the values.
func (l StorageDomainStatusList) Strings() []string {
result := make([]string, len(l))
for i, status := range l {
result[i] = string(status)
}
return result
}
// StorageDomainExternalStatus represents the status of an external storage domain. This status is updated externally.
//
// Note: this is not well-defined as the oVirt model has only a very generic description. See
// https://github.com/oVirt/ovirt-engine-api-model/blob/9869596c298925538d510de5019195b488970738/src/main/java/types/ExternalStatus.java
// for details.
type StorageDomainExternalStatus string
const (
// StorageDomainExternalStatusNA represents an external status that is not applicable.
// Most likely, the status should be obtained from StorageDomainStatus, since the
// storage domain in question is not an external storage.
StorageDomainExternalStatusNA StorageDomainExternalStatus = ""
// StorageDomainExternalStatusError indicates an error state.
StorageDomainExternalStatusError StorageDomainExternalStatus = "error"
// StorageDomainExternalStatusFailure indicates a failure state.
StorageDomainExternalStatusFailure StorageDomainExternalStatus = "failure"
// StorageDomainExternalStatusInfo indicates an OK status, but there is information available for the administrator
// that might be relevant.
StorageDomainExternalStatusInfo StorageDomainExternalStatus = "info"
// StorageDomainExternalStatusOk indicates a working status.
StorageDomainExternalStatusOk StorageDomainExternalStatus = "ok"
// StorageDomainExternalStatusWarning indicates that the storage domain has warnings that may be relevant for the
// administrator.
StorageDomainExternalStatusWarning StorageDomainExternalStatus = "warning"
)
// StorageDomainExternalStatusList is a list of StorageDomainStatus.
type StorageDomainExternalStatusList []StorageDomainExternalStatus
// StorageDomainExternalStatusValues returns all possible StorageDomainExternalStatus values.
func StorageDomainExternalStatusValues() StorageDomainExternalStatusList {
return []StorageDomainExternalStatus{
StorageDomainExternalStatusNA,
StorageDomainExternalStatusError,
StorageDomainExternalStatusFailure,
StorageDomainExternalStatusInfo,
StorageDomainExternalStatusOk,
StorageDomainExternalStatusWarning,
}
}
// Strings creates a string list of the values.
func (l StorageDomainExternalStatusList) Strings() []string {
result := make([]string, len(l))
for i, status := range l {
result[i] = string(status)
}
return result
}
func convertSDKStorageDomain(sdkStorageDomain *ovirtsdk4.StorageDomain, client Client) (StorageDomain, error) {
id, ok := sdkStorageDomain.Id()
if !ok {
return nil, newError(EFieldMissing, "failed to fetch ID of storage domain")
}
name, ok := sdkStorageDomain.Name()
if !ok {
return nil, newError(EFieldMissing, "failed to fetch name of storage domain")
}
available, ok := sdkStorageDomain.Available()
if !ok {
// If this is not OK the status probably doesn't allow for reading disk space (e.g. unattached), so we return 0.
available = 0
}
if available < 0 {
return nil, newError(EBug, "invalid available bytes returned from storage domain: %d", available)
}
storage, ok := sdkStorageDomain.Storage()
if !ok {
return nil, newError(EFieldMissing, "failed to fetch hostStorage of storage domain")
}
storageType, ok := storage.Type()
if !ok {
return nil, newError(EFieldMissing, "failed to fetch storage type of storage domain")
}
// It is OK for the storage domain status to not be present if the external status is present.
status, _ := sdkStorageDomain.Status()
// It is OK for the storage domain external status to not be present if the status is present.
externalStatus, _ := sdkStorageDomain.ExternalStatus()
if status == "" && externalStatus == "" {
return nil, newError(EFieldMissing, "neither the status nor the external status is set for storage domain %s", id)
}
return &storageDomain{
client: client,
id: StorageDomainID(id),
name: name,
available: uint64(available),
storageType: StorageDomainType(storageType),
status: StorageDomainStatus(status),
externalStatus: StorageDomainExternalStatus(externalStatus),
}, nil
}
type storageDomain struct {
client Client
id StorageDomainID
name string
available uint64
storageType StorageDomainType
status StorageDomainStatus
externalStatus StorageDomainExternalStatus
}
func (s storageDomain) ID() StorageDomainID {
return s.id
}
func (s storageDomain) Name() string {
return s.name
}
func (s storageDomain) Available() uint64 {
return s.available
}
func (s storageDomain) StorageType() StorageDomainType {
return s.storageType
}
func (s storageDomain) Status() StorageDomainStatus {
return s.status
}
func (s storageDomain) ExternalStatus() StorageDomainExternalStatus {
return s.externalStatus
}
type storageDomainDiskWait struct {
client *oVirtClient
disk Disk
storageDomain StorageDomain
correlationID string
lock *sync.Mutex
}
func (d *storageDomainDiskWait) Disk() Disk {
d.lock.Lock()
defer d.lock.Unlock()
return d.disk
}
func (d *storageDomainDiskWait) Wait(retries ...RetryStrategy) (Disk, error) {
retries = defaultRetries(retries, defaultWriteTimeouts(d.client))
d.lock.Lock()
diskID := d.disk.ID()
storageDomainID := d.storageDomain.ID()
d.lock.Unlock()
if _, err := d.client.WaitForDiskOK(diskID, retries...); err != nil {
return nil, err
}
if err := d.client.waitForJobFinished(d.correlationID, retries); err != nil {
return nil, err
}
disk, err := d.client.GetDiskFromStorageDomain(storageDomainID, diskID)
if disk != nil {
d.disk = disk
}
return disk, err
}