forked from hpe-storage/common-host-libs
-
Notifications
You must be signed in to change notification settings - Fork 1
/
config.go
353 lines (322 loc) · 10.8 KB
/
config.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
package tunelinux
// Copyright 2019 Hewlett Packard Enterprise Development LP.
import (
"encoding/json"
"errors"
"github.com/hpe-storage/common-host-libs/linux"
log "github.com/hpe-storage/common-host-libs/logger"
"github.com/hpe-storage/common-host-libs/util"
"io/ioutil"
"os"
"sync"
)
// Category recommendation category type
type Category int
const (
// Filesystem category type
Filesystem = iota + 1
// Disk category type
Disk
// Multipath category type
Multipath
// Fc category type
Fc
// Iscsi category type
Iscsi
)
func (s Category) String() string {
switch s {
case Filesystem:
return "filesystem"
case Disk:
return "disk"
case Multipath:
return "multipath"
case Fc:
return "fc"
case Iscsi:
return "iscsi"
}
return ""
}
// ComplianceStatus of the compliance
type ComplianceStatus int
const (
// NotRecommended with recommended value
NotRecommended ComplianceStatus = iota
// Recommended with recommended value
Recommended
)
func (s ComplianceStatus) String() string {
switch s {
case Recommended:
return "recommended"
case NotRecommended:
return "not-recommended"
}
return ""
}
// Severity for the recommendation made
type Severity int
const (
// Info Information level
Info Severity = 1 + iota
// Warning level
Warning
// Critical level
Critical
// Error level
Error
)
func (s Severity) String() string {
switch s {
case Info:
return "info"
case Warning:
return "warning"
case Critical:
return "critical"
case Error:
return "error"
}
return ""
}
// Recommendation for the settings on the host
type Recommendation struct {
// ID unique identifier for the recommendation
ID string `json:"id,omitempty"`
// Category recommendation category among (filesystem, multipath, device, iscsi, fc, system)
Category string `json:"category,omitempty"`
// Level severity level of the recommendation
Level string `json:"severity,omitempty"`
// Description brief description about the recommendation
Description string `json:"description,omitempty"`
// Parameter parameter name for recommendation
Parameter string `json:"parameter,omitempty"`
// Value current parameter value
Value string `json:"value,omitempty"`
// Recommendation recommended parameter value
Recommendation string `json:"recommendation,omitempty"`
// Status compliant status of the setting
CompliantStatus string `json:"status,omitempty"`
// Device device name
Device string `json:"device,omitempty"`
// MountPoint mount point for the device
MountPoint string `json:"mountpoint,omitempty"`
// FsType filesystem type, one among (xfs, ext2, ext3 and brtfs)
FsType string `json:"fstype,omitempty"`
// Vendor fibrechannel vendor name
Vendor string `json:"vendor,omitempty"`
}
// TemplateSetting for the settings on the host
type TemplateSetting struct {
// Category recommendation category among (filesystem, multipath, device, iscsi, fc, system)
Category string `json:"category,omitempty"`
// Level severity level of the recommendation
Level string `json:"severity,omitempty"`
// Description brief description about the recommendation
Description string `json:"description,omitempty"`
// Parameter parameter name for recommendation
Parameter string `json:"parameter,omitempty"`
// Recommendation recommended parameter value
Recommendation string `json:"recommendation,omitempty"`
// Driver driver module name
Driver string `json:"driver,omitempty"`
}
var (
// Global config of template settings
templateSettings []TemplateSetting
// RW lock to prevent concurrent loading
configLock = new(sync.RWMutex)
// ConfigFile file path of template settings for recommendation.
ConfigFile = GetConfigFile()
)
const (
// NotApplicable recommendation attribute not applicable
NotApplicable = "n/a"
// All applicable to all devices
All = "all"
)
// GetConfigFile returns config.json file path if bundled with NLT accordingly
func GetConfigFile() (configFile string) {
pluginType := os.Getenv("PLUGIN_TYPE")
if pluginType != "" {
if pluginType == "cv" {
// path bundled with cloud container plugins
return "/opt/hpe-storage/nimbletune/cloud_vm_config.json"
}
// path bundled with on-prem container plugins
return "/opt/hpe-storage/nimbletune/config.json"
}
if util.GetNltHome() != "" {
// path bundled with NLT
return util.GetNltHome() + "nimbletune/config.json"
}
// path if nimbletune is delivered as separate utility
return "./config.json"
}
// SetConfigFile sets the config file path (overrides the default value)
func SetConfigFile(configFile string) {
ConfigFile = configFile
}
// loadTemplateSettings synchronize and load configuration for template of recommended settings
// File read will only happen during initial load, so we take lock for complete loading.
// TODO: make this optional only when configfile option is provided. Use in-memory settings
func loadTemplateSettings() (err error) {
configLock.Lock()
defer configLock.Unlock()
if len(templateSettings) != 0 {
// already loaded template settings from config file
// once the initial config is loaded, we should return most of the time from here
return nil
}
log.Traceln("Reading template config file:", ConfigFile)
file, err := ioutil.ReadFile(ConfigFile)
if err != nil {
log.Error("Template Config File read error: ", err.Error())
return err
}
err = json.Unmarshal(file, &templateSettings)
if err != nil {
log.Error("Template Config File Unmarshall error: ", err.Error())
return err
}
return nil
}
// get map of either parameter -> recommended value or param -> description fields for given category
func getParamToTemplateFieldMap(category Category, field string, driver string) (paramTemplateFieldMap map[string]string, err error) {
paramTemplateFieldMap = make(map[string]string)
for index := range templateSettings {
if templateSettings[index].Category == Category.String(category) {
if driver != "" && templateSettings[index].Driver != driver {
// obtain only specific driver params in a given category
continue
}
switch field {
case "recommendation":
// populate parameter -> recommended value map
paramTemplateFieldMap[templateSettings[index].Parameter] = templateSettings[index].Recommendation
case "description":
// populate parameter -> description map
paramTemplateFieldMap[templateSettings[index].Parameter] = templateSettings[index].Description
case "severity":
// populate parameter -> severity map
paramTemplateFieldMap[templateSettings[index].Parameter] = templateSettings[index].Level
default:
// populate parameter -> recommended value map by default
paramTemplateFieldMap[templateSettings[index].Parameter] = templateSettings[index].Recommendation
}
}
}
return paramTemplateFieldMap, nil
}
// copyTemplateFile copies the template config files supplied with the tool as destination file
func copyTemplateFile(srcFile string, destFile string) (err error) {
// Copy the multipath.conf supplied with utility
args := []string{srcFile, destFile}
out, _, err := util.ExecCommandOutput("cp", args)
if err != nil {
log.Error("Unable to create file ", destFile, ", ", err.Error())
return errors.New("error: unable to create " + destFile + " before applying settings, reason: " + err.Error() + out)
}
return nil
}
func appendRecommendations(current []*Recommendation, final []*Recommendation) (recommendations []*Recommendation, err error) {
// Append all current recommendations to the final list
if current != nil {
for _, recommendation := range current {
final = append(final, recommendation)
}
}
return final, nil
}
// GetRecommendations obtain various recommendations for non-compliant settings on host
func GetRecommendations() (settings []*Recommendation, err error) {
log.Trace(">>>>> GetRecommendations")
defer log.Trace("<<<<< GetRecommendations")
// Obtain recommendations for each category
var recommendations []*Recommendation
var fileSystemRecommendations []*Recommendation
var deviceRecommendations []*Recommendation
var iscsiRecommendations []*Recommendation
var multipathRecommendations []*Recommendation
var fcRecommendations []*Recommendation
// Get all nimble devices
devices, err := linux.GetLinuxDmDevices(false, "", "")
if err != nil {
log.Error("Unable to get Nimble devices ", err.Error())
return nil, err
}
// Get filesystem recommendations
fileSystemRecommendations, err = GetFileSystemRecommendations(devices)
if err != nil {
log.Error("Unable to get FS recommendations ", err.Error())
return nil, err
}
// get the appended final list
recommendations, _ = appendRecommendations(fileSystemRecommendations, recommendations)
// Get block device recommendations
deviceRecommendations, err = GetDeviceRecommendations(devices)
if err != nil {
log.Error("Unable to get device recommendations ", err.Error())
return nil, err
}
// get the appended final list
recommendations, _ = appendRecommendations(deviceRecommendations, recommendations)
if _, err = os.Stat(linux.IscsiConf); os.IsNotExist(err) == false {
// Get iscsi recommendations
iscsiRecommendations, err = GetIscsiRecommendations()
if err != nil {
log.Error("Unable to get iscsi recommendations ", err.Error())
return nil, err
}
// get the appended final list
recommendations, _ = appendRecommendations(iscsiRecommendations, recommendations)
}
// Get multipath recommendations
multipathRecommendations, err = GetMultipathRecommendations()
if err != nil {
log.Error("Unable to get multipath recommendations ", err.Error())
return nil, err
}
// get the appended final list
recommendations, _ = appendRecommendations(multipathRecommendations, recommendations)
// Get multipath recommendations
fcRecommendations, err = GetFcRecommendations()
if err != nil {
log.Error("Unable to get fc recommendations ", err.Error())
// Might not be an FC system. continue with other recommendations
}
// get the appended final list
recommendations, _ = appendRecommendations(fcRecommendations, recommendations)
return recommendations, nil
}
// SetRecommendations set recommendations for all categories except filesystem and fc
func SetRecommendations(global bool) (err error) {
err = SetMultipathRecommendations()
if err != nil {
return errors.New("unable to set multipath recommendations, error: " + err.Error())
}
err = SetBlockDeviceRecommendations()
if err != nil {
return errors.New("unable to set device recommendations, error: " + err.Error())
}
err = SetIscsiRecommendations(global)
if err != nil {
return errors.New("unable to set iscsi recommendations, error: " + err.Error())
}
return nil
}
// isNcmRunning check if NCM is running
func isNcmRunning() (isRunning bool, err error) {
isRunning = false
if _, err = os.Stat("/etc/ncm.conf"); err != nil {
return isRunning, err
}
args := []string{"nlt", "status"}
_, _, err = util.ExecCommandOutput("service", args)
if err == nil {
isRunning = true
}
return isRunning, nil
}