forked from hpe-storage/common-host-libs
-
Notifications
You must be signed in to change notification settings - Fork 0
/
plugin.go
339 lines (311 loc) · 9.96 KB
/
plugin.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
// Copyright 2019 Hewlett Packard Enterprise Development LP
package plugin
import (
"fmt"
"os"
"strconv"
"strings"
"sync"
"time"
"github.com/hpe-storage/common-host-libs/jconfig"
log "github.com/hpe-storage/common-host-libs/logger"
"github.com/hpe-storage/common-host-libs/util"
)
const (
// DriverConfigFile represents volume driver config file
DriverConfigFile = "volume-driver.json"
// EnvManagedPlugin represents if running as docker managed plugin
EnvManagedPlugin = "MANAGED_PLUGIN"
// EnvPluginType represents underlying storage platform type which plugin is servicing
EnvPluginType = "PLUGIN_TYPE"
// EnvScope represents plugin scope, i.e global or local
EnvScope = "SCOPE"
// DeleteConflictDelayKey represents key name for wait on conflicts during remove
DeleteConflictDelayKey = "deleteConflictDelay"
// DefaultDeleteConflictDelay represents delay to wait on conflicts during remove
DefaultDeleteConflictDelay = 150
// MountConflictDelayKey represents the key name for wait on conflicts for mount
MountConflictDelayKey = "mountConflictDelay"
// DefaultMountConflictDelay represents the default delay to wait on conflicts during mount
DefaultMountConflictDelay = 120
)
var (
// VolumeDriverConfig represent cache of volume-driver.json loaded
VolumeDriverConfig *ConfigCache
configLock sync.Mutex
// Version of Plugin
Version = "dev"
// DeleteConflictDelay represent conflict delay to wait during remove
DeleteConflictDelay = DefaultDeleteConflictDelay
// MountConflictDelay represent conflict delay to wait during mount
MountConflictDelay = DefaultMountConflictDelay
)
// ConfigCache to store config options
type ConfigCache struct {
cache *jconfig.Config
updateTime time.Time
}
// GetCache returns volume-driver.json config
func (c *ConfigCache) GetCache() *jconfig.Config {
return c.cache
}
// Section different config section
type Section int
const (
// Global section type
Global = iota + 1
// Defaults section type
Defaults
// Overrides section type
Overrides
)
func (s Section) String() string {
switch s {
case Global:
return "global"
case Defaults:
return "defaults"
case Overrides:
return "overrides"
}
return ""
}
// PluginType indicates the docker volume plugin type
type PluginType int
const (
// nimble on-prem plugin type
Nimble PluginType = 1 + iota
// hpe cloud volumes plugin type
Cv
// simplivity plugin type
Simplivity
)
func (plugin PluginType) String() string {
switch plugin {
case Nimble:
return "nimble"
case Cv:
return "cv"
case Simplivity:
return "simplivity"
default:
return ""
}
}
func GetPluginType() (plugin PluginType) {
switch strings.ToLower(os.Getenv(EnvPluginType)) {
case Nimble.String():
return Nimble
case Cv.String():
return Cv
case Simplivity.String():
return Simplivity
default:
log.Infof("%s env is not set, assuming nimble type by default", EnvPluginType)
return Nimble
}
}
// IsManagedPlugin returns true if the plugin is deployed as docker managed plugin
func IsManagedPlugin() bool {
managedPlugin := os.Getenv(EnvManagedPlugin)
if managedPlugin != "" && strings.EqualFold(managedPlugin, "true") {
return true
}
return false
}
// IsLocalScopeDriver return true if its a local scoped driver, false otherwise
func IsLocalScopeDriver() bool {
scope := GetDriverScope()
if scope == "local" {
return true
}
return false
}
// GetDriverScope returns plugin scope configured
func GetDriverScope() (scope string) {
scope, ok := os.LookupEnv(EnvScope)
if !ok || len(scope) == 0 {
// if not set assume global scope
log.Tracef("plugin %s env is not specified, assuming global scope as default", EnvScope)
return "global"
}
log.Tracef("obtained volume driver scope as %s", scope)
return scope
}
// LoadHPEVolConfig loads the container config json file
func LoadHPEVolConfig() (err error) {
PluginConfigDir, _ = GetOrCreatePluginConfigDirectory()
exists, _, _ := util.FileExists(PluginConfigDir)
if !exists {
err = os.MkdirAll(PluginConfigDir, 0644)
if err != nil {
return fmt.Errorf("unable to create plugin config directory %s, err %s", PluginConfigDir, err.Error())
}
}
volumeDriverConfFile := PluginConfigDir + DriverConfigFile
exists, _, _ = util.FileExists(volumeDriverConfFile)
if !exists {
return fmt.Errorf("%s not present", volumeDriverConfFile)
}
log.Tracef("loading volumedriver config file %s", volumeDriverConfFile)
configLock.Lock()
defer configLock.Unlock()
localConfig := &ConfigCache{}
localConfig.cache, err = jconfig.NewConfig(volumeDriverConfFile)
if err != nil {
log.Error("unable to load volume driver config options", err.Error())
return err
}
localConfig.updateTime = time.Now()
// if no errors update volumeDriverConfig
VolumeDriverConfig = localConfig
log.Debugf("volumeDriverConfig cache :%v updatetime :%v", VolumeDriverConfig.cache, VolumeDriverConfig.updateTime)
_, err = VolumeDriverConfig.cache.GetMap(Section.String(Global))
if err != nil {
return err
}
return nil
}
// UpdateVolumeDriverConfigCache updates the volume driver config cache
func UpdateVolumeDriverConfigCache(volumeDriverConfigFile string) {
log.Tracef("updateVolumeDriverConfigCache called with %s", volumeDriverConfigFile)
is, _, _ := util.FileExists(volumeDriverConfigFile)
if !is {
log.Tracef("config file not present to update cache")
return
}
if VolumeDriverConfig != nil {
// check the last modidication time stamp of the config file if existing cache is not nil
file, err := os.Stat(volumeDriverConfigFile)
if err != nil {
log.Tracef("unable to read config file :%s", err.Error())
return
}
modifiedTime := file.ModTime()
if modifiedTime.Sub(VolumeDriverConfig.updateTime) > 0 {
// cache is dirty update it
log.Tracef("volumeDriverConfig cache is dirty. cache last updated at (%v), config file modified at(%v), updating cache", VolumeDriverConfig.updateTime.String(), modifiedTime.String())
err = LoadHPEVolConfig()
if err != nil {
// if there is any error to update cache reuse the dirty cache
log.Trace("error updating cache ", err.Error())
return
}
}
}
}
// GetUpdatedOptsFromConfig returns updated options after combining options from config files
func GetUpdatedOptsFromConfig(reqOpts map[string]interface{}) (updatedOpts map[string]interface{}, err error) {
// get global options
err = setDefaultFilesystem(reqOpts)
updatedOpts, err = populateConfigOptsBySection(Section.String(Global), reqOpts)
if err != nil {
return nil, fmt.Errorf("unable to obtain %s options %s", Section.String(Global), err.Error())
}
// get default options and populate
updatedOpts, err = populateConfigOptsBySection(Section.String(Defaults), updatedOpts)
if err != nil {
return nil, fmt.Errorf("unable to obtain %s options %s", Section.String(Defaults), err.Error())
}
err = setDefaultVolumeDir(reqOpts)
// get override options and populate
updatedOpts, err = populateConfigOptsBySection(Section.String(Overrides), updatedOpts)
if err != nil {
return nil, fmt.Errorf("unable to obtain %s options %s", Section.String(Overrides), err.Error())
}
return updatedOpts, nil
}
func populateConfigOptsBySection(section string, reqOpts map[string]interface{}) (opts map[string]interface{}, err error) {
log.Tracef("populateConfigOptsBySection called with section %s", section)
// get config options map based on section name as key
if VolumeDriverConfig == nil {
return nil, fmt.Errorf("no config cache present to populate")
}
optionMap, err := VolumeDriverConfig.GetCache().GetMap(section)
if err != nil {
return nil, err
}
for key, value := range optionMap {
// change to sizeInGiB if short form size is given in config file
if key == "size" {
key = "sizeInGiB"
}
// apply default or global options only when not present in command line
if _, present := reqOpts[key]; present && section != Section.String(Overrides) {
continue
}
reqOpts[key] = value
}
return reqOpts, nil
}
//CreateConfDirectory :
func CreateConfDirectory(confDir string) error {
log.Trace("createConfDirectory called with ", confDir)
_, isDir, err := util.FileExists(confDir)
if err != nil {
log.Error("CreateConfDirectory failed for %s, err %s", confDir, err.Error())
return err
}
if isDir == false {
log.Trace("creating conf directory ", confDir)
os.MkdirAll(confDir, 0700)
}
log.Tracef("%s exists", confDir)
return nil
}
// InitializeMountConflictDelay initializes mountConflictDelay
//nolint : dupl
func InitializeMountConflictDelay() {
MountConflictDelay = DefaultMountConflictDelay
if VolumeDriverConfig == nil {
log.Debugf("unable to load hpe volume config")
return
}
optsMap, err := VolumeDriverConfig.cache.GetMap(Section.String(Global))
if err != nil {
log.Debugf("failed to read from config file with err %s", err.Error())
return
}
if val, ok := optsMap[MountConflictDelayKey]; ok {
switch v := val.(type) {
case string:
intVal, err := strconv.Atoi(v)
if err != nil {
log.Warnf("unable to parse %s from config file, setting mountConflictDelay=%d", MountConflictDelayKey, DefaultMountConflictDelay)
return
}
MountConflictDelay = intVal
case int:
MountConflictDelay = v
}
}
log.Debugf("%s is set to %d", MountConflictDelayKey, MountConflictDelay)
}
// InitializeDeleteConflictDelay initializes deleteConflictDelay
//nolint : dupl
func InitializeDeleteConflictDelay() {
DeleteConflictDelay = DefaultDeleteConflictDelay
if VolumeDriverConfig == nil {
log.Debugf("unable to load hpe volume config")
return
}
optsMap, err := VolumeDriverConfig.cache.GetMap(Section.String(Global))
if err != nil {
log.Debugf("failed to read from config file with err %s", err.Error())
return
}
if val, ok := optsMap[DeleteConflictDelayKey]; ok {
switch v := val.(type) {
case string:
intVal, err := strconv.Atoi(v)
if err != nil {
log.Warnf("unable to parse %s setting deleteConflictDelay=%d", DeleteConflictDelayKey, DefaultDeleteConflictDelay)
return
}
DeleteConflictDelay = intVal
case int:
DeleteConflictDelay = v
}
}
log.Debugf("%s is set to %d", DeleteConflictDelayKey, DeleteConflictDelay)
}