forked from hpe-storage/common-host-libs
-
Notifications
You must be signed in to change notification settings - Fork 1
/
mount.go
274 lines (228 loc) · 10.2 KB
/
mount.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
// (c) Copyright 2019 Hewlett Packard Enterprise Development LP
package mount
import (
"path/filepath"
"github.com/hpe-storage/common-host-libs/chapi2/cerrors"
"github.com/hpe-storage/common-host-libs/chapi2/model"
"github.com/hpe-storage/common-host-libs/chapi2/multipath"
"github.com/hpe-storage/common-host-libs/chapi2/virtualdevice"
log "github.com/hpe-storage/common-host-libs/logger"
)
const (
// Shared error messages
errorMessageInvalidInputParameter = "invalid input parameter"
errorMessageMissingMountPoint = "missing mount point"
errorMessageMissingMountPointID = "missing mount point ID"
errorMessageMissingSerialNumber = "missing serial number"
errorMessageMountPointInUse = `mount point "%v" already in use`
errorMessageMountPointNotEmpty = `mount point "%v" is not empty`
errorMessageMountPointNotFound = "mount point not found"
errorMessageMultipathPluginNotSet = "multipathPlugin not set"
errorMessageMultipleMountPointsDetected = "multiple mount points detected"
errorMessageUnsupportedPartition = "unsupported partition"
errorMessageVolumeAlreadyMounted = `volume already mounted at "%v"`
)
type Mounter struct {
multipathPlugin *multipath.MultipathPlugin
virtualDevPlugin *virtualdevice.VirtualDevPlugin
}
func NewMounter() *Mounter {
return &Mounter{
multipathPlugin: multipath.NewMultipathPlugin(),
virtualDevPlugin: virtualdevice.NewVirtualDevPlugin(),
}
}
// GetMounts reports all mounts on this host for the specified Nimble volume
func (mounter *Mounter) GetMounts(serialNumber string) ([]*model.Mount, error) {
return mounter.getMounts(serialNumber, "", false, true)
}
// GetAllMountDetails enumerates the specified mount point ID
func (mounter *Mounter) GetAllMountDetails(serialNumber string, mountId string) ([]*model.Mount, error) {
return mounter.getMounts(serialNumber, mountId, true, true)
}
// CreateMount is called to mount the given device to the given mount point
func (mounter *Mounter) CreateMount(serialNumber string, mountPoint string, fsOptions *model.FileSystemOptions) (*model.Mount, error) {
log.Tracef(">>>>> CreateMount, serialNumber=%v, mountPoint=%v, fsOptions=%v", serialNumber, mountPoint, fsOptions)
defer log.Trace("<<<<< CreateMount")
// Validate and enumerate the mount object for the given serial number and mount point
mount, alreadyMounted, err := mounter.getMountForCreate(serialNumber, mountPoint)
// Fail request if unable to validate and enumerate the mount object
if err != nil {
return nil, err
}
// If the volume is already mounted, at the requested mount point, return mount object with success
if alreadyMounted {
return mount, nil
}
// Mount the volume at the specified mount point
err = mounter.createMount(mount, mountPoint, fsOptions)
if err != nil {
return nil, err
}
// Now that the device has been mounted, adjust the mount point and return the mount object
mount.MountPoint = mountPoint
return mount, nil
}
// DeleteMount is called to unmount the given mount point ID
func (mounter *Mounter) DeleteMount(serialNumber string, mountId string) error {
log.Tracef(">>>>> DeleteMount, serialNumber=%v, mountId=%v", serialNumber, mountId)
defer log.Trace("<<<<< DeleteMount")
// Validate and enumerate the mount object for the given serial number and mount point ID
mount, err := mounter.getMountForDelete(serialNumber, mountId)
// Fail request if unable to validate and enumerate the mount object
if err != nil {
return err
}
// Call the platform specific deleteMount routine to dismount the volume
return mounter.deleteMount(mount)
}
// enumerateDevices enumerates the given serialNumber (or all devices if serialNumber is empty).
// The allDetails boolean lets us know if we just need to enumerate basic details (false) or if
// all details are required (true). We can optimize our enumeration (e.g. reduce the amount of
// enumeration required) if we only need basic details.
func (mounter *Mounter) enumerateDevices(serialNumber string, allDetails bool) ([]*model.Device, error) {
if !allDetails {
return mounter.multipathPlugin.GetDevices(serialNumber)
}
return mounter.multipathPlugin.GetAllDeviceDetails(serialNumber)
}
// getMountForCreate takes the Nimble serial number, and mount point path, validates the input
// data, and enumerates the Mount object. The following properties are returned:
// mount - Enumerated model.Mount object for the provided serialNumber/mountPoint
// alreadyMounted - If volume is already mounted, at the requested mount point, true is
// returned else false ("mount" object returned if alreadyMounted==true)
// err - If volume cannot be mounted, an error object is returned ("mount" and
// "alreadyMounted" are invalid)
func (mounter *Mounter) getMountForCreate(serialNumber string, mountPoint string) (mount *model.Mount, alreadyMounted bool, err error) {
log.Tracef(">>>>> getMountForCreate, serialNumber=%v, mountPoint=%v", serialNumber, mountPoint)
defer log.Trace("<<<<< getMountForCreate")
// If the serialNumber is not provided, fail the request
if serialNumber == "" {
err = cerrors.NewChapiError(cerrors.InvalidArgument, errorMessageMissingSerialNumber)
log.Error(err)
return nil, false, err
}
// If the mountPoint is not provided, fail the request
if mountPoint == "" {
err = cerrors.NewChapiError(cerrors.InvalidArgument, errorMessageMissingMountPoint)
log.Error(err)
return nil, false, err
}
// Enumerate all the mount points, with all details, for the given serial number
var mounts []*model.Mount
mounts, err = mounter.getMounts(serialNumber, "", true, false)
if err != nil {
return nil, false, err
}
// Fail request if no mount points detected
if len(mounts) == 0 {
err = cerrors.NewChapiError(cerrors.InvalidArgument, errorMessageMountPointNotFound)
log.Error(err)
return nil, false, err
}
// We only support a device with a single mount point (e.g. one created by CHAPI).
// Fail request if multiple mount points detected.
if len(mounts) > 1 {
err = cerrors.NewChapiError(cerrors.InvalidArgument, errorMessageMultipleMountPointsDetected)
log.Error(err)
return nil, false, err
}
// Get the mount point object we're going to try and mount
mount = mounts[0]
// Handle case where Nimble volume is already mounted
if mount.MountPoint != "" {
// Convert the current mount point to its absolute path
var currentMountPoint string
currentMountPoint, err = filepath.Abs(mount.MountPoint)
if err != nil {
log.Errorf("Invalid current mount point, MountPoint=%v, err=%v", mount.MountPoint, err)
return nil, false, err
}
// Convert the target mount point to its absolute path
var requestedMountPoint string
requestedMountPoint, err = filepath.Abs(mountPoint)
if err != nil {
log.Errorf("Invalid requested mount point, MountPoint=%v, err=%v", mountPoint, err)
return nil, false, err
}
// If the current mount point matches the target mount point, there is nothing to do as we
// are already mounted at the requested location.
if isSamePathName(currentMountPoint, requestedMountPoint) {
log.Tracef(`Mount point ID=%v, SerialNumber=%v, currentMountPoint=%v, already mounted`, mount.ID, mount.SerialNumber, currentMountPoint)
return mount, true, nil
}
// If here, the device is already mounted but to a different location. Log the error and
// fail the request.
err = cerrors.NewChapiErrorf(cerrors.AlreadyExists, errorMessageVolumeAlreadyMounted, currentMountPoint)
log.Error(err)
return nil, false, err
}
// Routine passed all checks; safe to attempt to mount volume to the given mount point
return mount, false, nil
}
// getMountForDelete takes the Nimble serial number, and mount point ID, validates the input
// data, and enumerates the Mount object. The following properties are returned:
// mount - Enumerated model.Mount object for the provided serialNumber/mountPointId
// err - If volume cannot be dismounted, an error object is returned
func (mounter *Mounter) getMountForDelete(serialNumber string, mountId string) (mount *model.Mount, err error) {
log.Tracef(">>>>> getMountForDelete, serialNumber=%v, mountId=%v", serialNumber, mountId)
defer log.Trace("<<<<< getMountForDelete")
// If the serialNumber is not provided, fail the request
if serialNumber == "" {
err = cerrors.NewChapiError(cerrors.InvalidArgument, errorMessageMissingSerialNumber)
log.Error(err)
return nil, err
}
// If the mountId is not provided, fail the request
if mountId == "" {
err = cerrors.NewChapiError(cerrors.InvalidArgument, errorMessageMissingMountPointID)
log.Error(err)
return nil, err
}
// Find the specified mount point ID with all details
var mounts []*model.Mount
mounts, err = mounter.getMounts(serialNumber, mountId, true, true)
if err != nil {
return nil, err
}
// There should only be a single mount point object
if len(mounts) != 1 {
err = cerrors.NewChapiError(cerrors.InvalidArgument, errorMessageMountPointNotFound)
log.Error(err)
return nil, err
}
// Routine passed all checks; safe to attempt to dismount volume at the given mount point
return mounts[0], nil
}
// logMountPoints logs the mount points array to our log file
func logMountPoints(mountPoints []*model.Mount, allDetails bool) {
log.Tracef(">>>>> logMountPoints, allDetails=%v", allDetails)
defer log.Trace("<<<<< logMountPoints")
for _, mountPoint := range mountPoints {
logMessage := "Enumerated mount point, ID=" + mountPoint.ID
if allDetails {
logMessage += ", SerialNumber=" + mountPoint.SerialNumber
logMessage += ", MountPoint=" + mountPoint.MountPoint
}
log.Trace(logMessage)
}
}
// TODO, Remove or implement member functions below that are not utilized
// func (mounter *Mounter) Mount(mount *model.Mount) error {
// return nil
// }
// func (mounter *Mounter) Unmount(mountPoint string) error {
// return nil
// }
// func (mounter *Mounter) ReMount(mount *model.Mount) error {
// return nil
// }
// func (mounter *Mounter) BindMount(mount *model.Mount) error {
// return nil
// }
// func (mounter *Mounter) GetFsType(mountPoint string) (*string, error) {
// return nil, nil
// }
// func (mounter *Mounter) GetFsOptions(mountPoint string) (*model.FileSystemOptions, error) {
// return nil, nil
// }