blob: 44eb5828d9aa413cc0c9ee7c37f2b6e0873b2e4c [file] [log] [blame]
Girish Kumarb03dcee2020-04-14 11:48:15 +00001/*
2 * Copyright 2019-present Ciena Corporation
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package commands
17
18import (
19 "context"
20 "encoding/json"
21 "errors"
22 "fmt"
23 "strings"
24
25 flags "github.com/jessevdk/go-flags"
26 "github.com/opencord/voltctl/pkg/format"
27 "github.com/opencord/voltctl/pkg/model"
David K. Bainbridge88781072021-06-28 09:59:42 -070028 "github.com/opencord/voltha-lib-go/v5/pkg/config"
29 "github.com/opencord/voltha-lib-go/v5/pkg/db/kvstore"
30 "github.com/opencord/voltha-lib-go/v5/pkg/log"
Girish Kumarb03dcee2020-04-14 11:48:15 +000031 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
32 "k8s.io/client-go/kubernetes"
33 "k8s.io/client-go/tools/clientcmd"
34)
35
36const (
Neha Sharma94313672020-08-25 07:52:28 +000037 defaultComponentName = "global"
38 defaultPackageName = "default"
39 logPackagesListKey = "log_package_list" // kvstore key containing list of allowed log packages
40 logTracingStatusKey = "trace_publish"
41 logCorrelationStatusKey = "log_correlation"
Girish Kumarb03dcee2020-04-14 11:48:15 +000042)
43
44// Custom Option representing <component-name>#<package-name> format (package is optional)
45// This is used by 'log level set' commands
46type ComponentAndPackageName string
47
48// Custom Option representing currently configured log configuration in <component-name>#<package-name> format (package is optional)
49// This is used by 'log level clear' commands
50type ConfiguredComponentAndPackageName string
51
52// Custom Option representing component-name. This is used by 'log level list' and 'log package list' commands
53type ComponentName string
54
55// Custom Option representing Log Level (one of debug, info, warn, error, fatal)
56type LevelName string
57
58// LogLevelOutput represents the output structure for the loglevel
59type LogLevelOutput struct {
60 ComponentName string
Girish Kumarda415372020-04-21 15:37:13 +000061 PackageName string
Girish Kumarb03dcee2020-04-14 11:48:15 +000062 Status string
63 Error string
64}
65
Girish Kumare48ec8a2020-08-18 12:28:51 +000066// SetLogLevelOpts represents the supported CLI arguments for the log level set command
Girish Kumarb03dcee2020-04-14 11:48:15 +000067type SetLogLevelOpts struct {
68 OutputOptions
69 Args struct {
70 Level LevelName
71 Component []ComponentAndPackageName
72 } `positional-args:"yes" required:"yes"`
73}
74
Girish Kumare48ec8a2020-08-18 12:28:51 +000075// ListLogLevelOpts represents the supported CLI arguments for the log level list command
Girish Kumarb03dcee2020-04-14 11:48:15 +000076type ListLogLevelsOpts struct {
77 ListOutputOptions
78 Args struct {
79 Component []ComponentName
80 } `positional-args:"yes" required:"yes"`
81}
82
Girish Kumare48ec8a2020-08-18 12:28:51 +000083// ClearLogLevelOpts represents the supported CLI arguments for the log level clear command
Girish Kumarb03dcee2020-04-14 11:48:15 +000084type ClearLogLevelsOpts struct {
85 OutputOptions
86 Args struct {
87 Component []ConfiguredComponentAndPackageName
88 } `positional-args:"yes" required:"yes"`
89}
90
Girish Kumare48ec8a2020-08-18 12:28:51 +000091// ListLogLevelOpts represents the supported CLI arguments for the log level list command
Girish Kumarb03dcee2020-04-14 11:48:15 +000092type ListLogPackagesOpts struct {
93 ListOutputOptions
94 Args struct {
95 Component []ComponentName
96 } `positional-args:"yes" required:"yes"`
97}
98
Girish Kumare48ec8a2020-08-18 12:28:51 +000099// EnableLogTracingOpts represents the supported CLI arguments for the log tracing enable command
100type EnableLogTracingOpts struct {
101 OutputOptions
102 Args struct {
103 Component []ComponentName
104 } `positional-args:"yes" required:"yes"`
105}
106
107// DisableLogTracingOpts represents the supported CLI arguments for the log tracing disable command
108type DisableLogTracingOpts struct {
109 OutputOptions
110 Args struct {
111 Component []ComponentName
112 } `positional-args:"yes" required:"yes"`
113}
114
115// ListLogTracingOpts represents the supported CLI arguments for the log tracing list command
116type ListLogTracingOpts struct {
117 ListOutputOptions
118 Args struct {
119 Component []ComponentName
120 } `positional-args:"yes" required:"yes"`
121}
122
Neha Sharma94313672020-08-25 07:52:28 +0000123// EnableLogCorrelationOpts represent the supported CLI arguments for the log correlation enable command
124type EnableLogCorrelationOpts struct {
125 OutputOptions
126 Args struct {
127 Component []ComponentName
128 } `positional-args:"yes" required:"yes"`
129}
130
131// DisableLogCorrelationOpts represent the supported CLI arguments for the log correlation disable command
132type DisableLogCorrelationOpts struct {
133 OutputOptions
134 Args struct {
135 Component []ComponentName
136 } `positional-args:"yes" required:"yes"`
137}
138
139// ListLogCorrelationOpts represents the supported CLI arguments for the log correlation list command
140type ListLogCorrelationOpts struct {
141 ListOutputOptions
142 Args struct {
143 Component []ComponentName
144 } `positional-args:"yes" required:"yes"`
145}
146
Girish Kumarb03dcee2020-04-14 11:48:15 +0000147// LogPackageOpts represents the log package commands
148type LogPackageOpts struct {
149 ListLogPackages ListLogPackagesOpts `command:"list"`
150}
151
152// LogLevelOpts represents the log level commands
153type LogLevelOpts struct {
154 SetLogLevel SetLogLevelOpts `command:"set"`
155 ListLogLevels ListLogLevelsOpts `command:"list"`
156 ClearLogLevels ClearLogLevelsOpts `command:"clear"`
157}
158
Girish Kumare48ec8a2020-08-18 12:28:51 +0000159// LogTracingOpts represents the log tracing commands
160type LogTracingOpts struct {
161 EnableLogTracing EnableLogTracingOpts `command:"enable"`
162 DisableLogTracing DisableLogTracingOpts `command:"disable"`
163 ListLogTracing ListLogTracingOpts `command:"list"`
164}
165
Neha Sharma94313672020-08-25 07:52:28 +0000166// LogCorrelationOpts represents the log correlation commands
167type LogCorrelationOpts struct {
168 EnableLogCorrelation EnableLogCorrelationOpts `command:"enable"`
169 DisableLogCorrelation DisableLogCorrelationOpts `command:"disable"`
170 ListLogCorrelation ListLogCorrelationOpts `command:"list"`
171}
172
Girish Kumarb03dcee2020-04-14 11:48:15 +0000173// LogOpts represents the log commands
174type LogOpts struct {
Neha Sharma94313672020-08-25 07:52:28 +0000175 LogLevel LogLevelOpts `command:"level"`
176 LogPackage LogPackageOpts `command:"package"`
177 LogTracing LogTracingOpts `command:"tracing"`
178 LogCorrelation LogCorrelationOpts `command:"correlation"`
Girish Kumarb03dcee2020-04-14 11:48:15 +0000179}
180
181var logOpts = LogOpts{}
182
183const (
Girish Kumare48ec8a2020-08-18 12:28:51 +0000184 DEFAULT_LOG_LEVELS_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}\t{{.Level}}"
185 DEFAULT_LOG_PACKAGES_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}"
186 DEFAULT_LOG_FEATURE_STATUS_FORMAT = "table{{ .ComponentName }}\t{{.Status}}"
187 DEFAULT_LOG_RESULT_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}\t{{.Status}}\t{{.Error}}"
188 DEFAULT_LOG_FEATURE_RESULT_FORMAT = "table{{ .ComponentName }}\t{{.Status}}\t{{.Error}}"
Girish Kumarb03dcee2020-04-14 11:48:15 +0000189)
190
191func toStringArray(arg interface{}) []string {
192 var list []string
193 if cnl, ok := arg.([]ComponentName); ok {
194 for _, cn := range cnl {
195 list = append(list, string(cn))
196 }
197 } else if cpnl, ok := arg.([]ComponentAndPackageName); ok {
198 for _, cpn := range cpnl {
199 list = append(list, string(cpn))
200 }
201 } else if ccpnl, ok := arg.([]ConfiguredComponentAndPackageName); ok {
202 for _, ccpn := range ccpnl {
203 list = append(list, string(ccpn))
204 }
205 }
206
207 return list
208}
209
210// RegisterLogCommands is used to register log and its sub-commands e.g. level, package etc
211func RegisterLogCommands(parent *flags.Parser) {
Girish Kumare48ec8a2020-08-18 12:28:51 +0000212 _, err := parent.AddCommand("log", "log configuration commands", "update/view log levels, correlation, tracing status and list packages of components", &logOpts)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000213 if err != nil {
214 Error.Fatalf("Unable to register log commands with voltctl command parser: %s", err.Error())
215 }
216}
217
218// Common method to get list of VOLTHA components using k8s API. Used for validation and auto-complete
219// Just return a blank list in case of any error
220func getVolthaComponentNames() []string {
221 var componentList []string
222
223 // use the current context in kubeconfig
224 config, err := clientcmd.BuildConfigFromFlags("", GlobalOptions.K8sConfig)
225 if err != nil {
226 // Ignore any error
227 return componentList
228 }
229
230 // create the clientset
231 clientset, err := kubernetes.NewForConfig(config)
232 if err != nil {
233 return componentList
234 }
235
236 pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{
237 LabelSelector: "app.kubernetes.io/part-of=voltha",
238 })
239 if err != nil {
240 return componentList
241 }
242
243 for _, pod := range pods.Items {
244 componentList = append(componentList, pod.ObjectMeta.Labels["app.kubernetes.io/name"])
245 }
246
247 return componentList
248}
249
Girish Kumare48ec8a2020-08-18 12:28:51 +0000250func constructConfigManager(ctx context.Context) (*config.ConfigManager, func(), error) {
David K. Bainbridge1d946442021-03-19 16:45:52 +0000251 client, err := kvstore.NewEtcdCustomClient(
252 ctx,
David K. Bainbridge88781072021-06-28 09:59:42 -0700253 GlobalConfig.Current().KvStore,
254 GlobalConfig.Current().KvStoreConfig.Timeout,
255 log.FatalLevel)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000256 if err != nil {
257 return nil, nil, fmt.Errorf("Unable to create kvstore client %s", err)
258 }
259
David K. Bainbridge9189c632021-03-26 21:52:21 +0000260 cm := config.NewConfigManager(ctx, client, supportedKvStoreType, GlobalConfig.Current().KvStore, GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000261 return cm, func() { client.Close(ctx) }, nil
262}
263
Girish Kumarda415372020-04-21 15:37:13 +0000264// Method to get list of allowed Package Names for a given component. This list
265// is saved into etcd kvstore by each active component at startup as a json array
Girish Kumarb03dcee2020-04-14 11:48:15 +0000266func getPackageNames(componentName string) ([]string, error) {
267 list := []string{defaultPackageName}
268
269 ProcessGlobalOptions()
270
271 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000272 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000273 defer cancel()
274
Girish Kumare48ec8a2020-08-18 12:28:51 +0000275 cm, cleanupFunc, err := constructConfigManager(ctx)
276 if err != nil {
277 return nil, fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
278 }
279 defer cleanupFunc()
280
Girish Kumarb03dcee2020-04-14 11:48:15 +0000281 componentMetadata := cm.InitComponentConfig(componentName, config.ConfigTypeMetadata)
282
283 value, err := componentMetadata.Retrieve(ctx, logPackagesListKey)
284 if err != nil || value == "" {
285 return list, nil
286 }
287
288 var packageList []string
289 if err = json.Unmarshal([]byte(value), &packageList); err != nil {
290 return list, nil
291 }
292
293 list = append(list, packageList...)
294
295 return list, nil
296}
297
298func (ln *LevelName) Complete(match string) []flags.Completion {
299 levels := []string{"debug", "info", "warn", "error", "fatal"}
300
301 var list []flags.Completion
302 for _, name := range levels {
303 if strings.HasPrefix(name, strings.ToLower(match)) {
304 list = append(list, flags.Completion{Item: name})
305 }
306 }
307
308 return list
309}
310
311func (cpn *ComponentAndPackageName) Complete(match string) []flags.Completion {
312
313 componentNames := getVolthaComponentNames()
314
315 // Return nil if no component names could be fetched
316 if len(componentNames) == 0 {
317 return nil
318 }
319
320 // Check to see if #was specified, and if so, we know we have
321 // to split component name and package
322 parts := strings.SplitN(match, "#", 2)
323
324 var list []flags.Completion
325 for _, name := range componentNames {
326 if strings.HasPrefix(name, parts[0]) {
327 list = append(list, flags.Completion{Item: name})
328 }
329 }
330
331 // If the possible completions > 1 then we have to stop here
332 // as we can't suggest packages
333 if len(parts) == 1 || len(list) > 1 {
334 return list
335 }
336
337 // Ok, we have a valid, unambiguous component name and there
338 // is a package separator, so lets try to expand the package
339 // and in this case we will replace the list we have so
340 // far with the new list
341 cname := list[0].Item
342 base := []flags.Completion{{Item: fmt.Sprintf("%s#%s", cname, parts[1])}}
343 packages, err := getPackageNames(cname)
344 if err != nil || len(packages) == 0 {
345 return base
346 }
347
348 list = []flags.Completion{}
349 for _, pname := range packages {
350 if strings.HasPrefix(pname, parts[1]) {
351 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, pname)})
352 }
353 }
354
355 // if package part is present and still no match found based on prefix, user may be using
356 // short-hand notation for package name (last element of package path string e.g. kafka).
357 // Attempt prefix match against last element of package path (after the last / character)
358 if len(list) == 0 && len(parts[1]) >= 3 {
359 var mplist []string
360 for _, pname := range packages {
361 pnameparts := strings.Split(pname, "/")
362 if strings.HasPrefix(pnameparts[len(pnameparts)-1], parts[1]) {
363 mplist = append(mplist, pname)
364 }
365 }
366
367 // add to completion list if only a single match is found
368 if len(mplist) == 1 {
369 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, mplist[0])})
370 }
371 }
372
373 // If the component name was expanded but package name match was not found, list will still be empty
374 // We should return entry with just component name auto-completed and package name unchanged.
375 if len(list) == 0 && cname != parts[0] {
376 // Returning 2 entries with <completed-component-name>#<package-part> as prefix
377 // Just 1 entry will auto-complete the argument
378 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s1", cname, parts[1])})
379 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s2", cname, parts[1])})
380 }
381
382 return list
383}
384
385func (ccpn *ConfiguredComponentAndPackageName) Complete(match string) []flags.Completion {
386
387 var list []flags.Completion
388
389 ProcessGlobalOptions()
390
391 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000392 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000393 defer cancel()
394
395 cm, cleanupFunc, err := constructConfigManager(ctx)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000396 if err != nil {
397 return list
398 }
Girish Kumare48ec8a2020-08-18 12:28:51 +0000399 defer cleanupFunc()
Girish Kumarb03dcee2020-04-14 11:48:15 +0000400
401 var componentNames []string
402 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
403
404 // Return nil if no component names could be fetched
405 if err != nil || len(componentNames) == 0 {
406 return nil
407 }
408
409 // Check to see if #was specified, and if so, we know we have
410 // to split component name and package
411 parts := strings.SplitN(match, "#", 2)
412
413 for _, name := range componentNames {
414 if strings.HasPrefix(name, parts[0]) {
415 list = append(list, flags.Completion{Item: name})
416
417 // Handle scenario when one component is exact substring of other e.g. read-write-cor
418 // is substring of read-write-core (last e missing). Such a wrong component name
419 // can get configured during log level set operation
420 // In case of exact match of component name, use it if package part is present
421 if name == parts[0] && len(parts) == 2 {
422 list = []flags.Completion{{Item: name}}
423 break
424 }
425 }
426 }
427
428 // If the possible completions > 1 then we have to stop here
429 // as we can't suggest packages
430 if len(parts) == 1 || len(list) > 1 {
431 return list
432 }
433
434 // Ok, we have a valid, unambiguous component name and there
435 // is a package separator, so lets try to expand the package
436 // and in this case we will replace the list we have so
437 // far with the new list
438 cname := list[0].Item
439 base := []flags.Completion{{Item: fmt.Sprintf("%s#%s", cname, parts[1])}}
440
441 // Get list of packages configured for matching component name
442 logConfig := cm.InitComponentConfig(cname, config.ConfigTypeLogLevel)
443 logLevels, err1 := logConfig.RetrieveAll(ctx)
444 if err1 != nil || len(logLevels) == 0 {
445 return base
446 }
447
448 packages := make([]string, len(logLevels))
449 list = []flags.Completion{}
450 for pname := range logLevels {
451 pname = strings.ReplaceAll(pname, "#", "/")
452 packages = append(packages, pname)
453 if strings.HasPrefix(pname, parts[1]) {
454 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, pname)})
455 }
456 }
457
458 // if package part is present and still no match found based on prefix, user may be using
459 // short-hand notation for package name (last element of package path string e.g. kafka).
460 // Attempt prefix match against last element of package path (after the last / character)
461 if len(list) == 0 && len(parts[1]) >= 3 {
462 var mplist []string
463 for _, pname := range packages {
464 pnameparts := strings.Split(pname, "/")
465 if strings.HasPrefix(pnameparts[len(pnameparts)-1], parts[1]) {
466 mplist = append(mplist, pname)
467 }
468 }
469
470 // add to completion list if only a single match is found
471 if len(mplist) == 1 {
472 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, mplist[0])})
473 }
474 }
475
476 // If the component name was expanded but package name match was not found, list will still be empty
477 // We should return entry with just component name auto-completed and package name unchanged.
478 if len(list) == 0 && cname != parts[0] {
479 // Returning 2 entries with <completed-component-name>#<package-part> as prefix
480 // Just 1 entry will auto-complete the argument
481 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s1", cname, parts[1])})
482 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s2", cname, parts[1])})
483 }
484
485 return list
486}
487
488func (cn *ComponentName) Complete(match string) []flags.Completion {
489
490 componentNames := getVolthaComponentNames()
491
492 // Return nil if no component names could be fetched
493 if len(componentNames) == 0 {
494 return nil
495 }
496
497 var list []flags.Completion
498 for _, name := range componentNames {
499 if strings.HasPrefix(name, match) {
500 list = append(list, flags.Completion{Item: name})
501 }
502 }
503
504 return list
505}
506
507// Return nil if no component names could be fetched
508// processComponentListArgs stores the component name and package names given in command arguments to LogLevel
509// It checks the given argument has # key or not, if # is present then split the argument for # then stores first part as component name
510// and second part as package name
511func processComponentListArgs(Components []string) ([]model.LogLevel, error) {
512
513 var logLevelConfig []model.LogLevel
514
515 if len(Components) == 0 {
516 Components = append(Components, defaultComponentName)
517 }
518
519 for _, component := range Components {
520 logConfig := model.LogLevel{}
521 val := strings.SplitN(component, "#", 2)
522
523 if strings.Contains(val[0], "/") {
524 return nil, errors.New("the component name '" + val[0] + "' contains an invalid character '/'")
525 }
526
Girish Kumarda415372020-04-21 15:37:13 +0000527 // Breakup into component and package name; consider default package name if it is blank (e.g. read-write-core#)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000528 if len(val) > 1 {
529 if val[0] == defaultComponentName {
530 return nil, errors.New("global level doesn't support packageName")
531 }
Girish Kumarda415372020-04-21 15:37:13 +0000532
Girish Kumarb03dcee2020-04-14 11:48:15 +0000533 logConfig.ComponentName = val[0]
Girish Kumarda415372020-04-21 15:37:13 +0000534 logConfig.PackageName = val[1]
535 if logConfig.PackageName == "" {
536 logConfig.PackageName = defaultPackageName
537 }
Girish Kumarb03dcee2020-04-14 11:48:15 +0000538 } else {
539 logConfig.ComponentName = component
540 logConfig.PackageName = defaultPackageName
541 }
542 logLevelConfig = append(logLevelConfig, logConfig)
543 }
544 return logLevelConfig, nil
545}
546
547// This method set loglevel for components.
548// For example, using below command loglevel can be set for specific component with default packageName
549// voltctl loglevel set level <componentName>
550// For example, using below command loglevel can be set for specific component with specific packageName
551// voltctl loglevel set level <componentName#packageName>
552// For example, using below command loglevel can be set for more than one component for default package and other component for specific packageName
553// voltctl loglevel set level <componentName1#packageName> <componentName2>
554func (options *SetLogLevelOpts) Execute(args []string) error {
555 var (
556 logLevelConfig []model.LogLevel
557 err error
558 )
559 ProcessGlobalOptions()
560
561 log.SetAllLogLevel(log.FatalLevel)
562
563 if options.Args.Level != "" {
564 if _, err := log.StringToLogLevel(string(options.Args.Level)); err != nil {
565 return fmt.Errorf("Unknown log level '%s'. Allowed values are DEBUG, INFO, WARN, ERROR, FATAL", options.Args.Level)
566 }
567 }
568
569 logLevelConfig, err = processComponentListArgs(toStringArray(options.Args.Component))
570 if err != nil {
571 return fmt.Errorf(err.Error())
572 }
573
David K. Bainbridge9189c632021-03-26 21:52:21 +0000574 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000575 defer cancel()
576
Girish Kumare48ec8a2020-08-18 12:28:51 +0000577 cm, cleanupFunc, err := constructConfigManager(ctx)
578 if err != nil {
579 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
580 }
581 defer cleanupFunc()
582
583 var output []LogLevelOutput
584
Girish Kumarda415372020-04-21 15:37:13 +0000585 validComponents := getVolthaComponentNames()
586
Girish Kumarb03dcee2020-04-14 11:48:15 +0000587 for _, lConfig := range logLevelConfig {
588
589 logConfig := cm.InitComponentConfig(lConfig.ComponentName, config.ConfigTypeLogLevel)
Girish Kumarda415372020-04-21 15:37:13 +0000590 err := logConfig.Save(ctx, strings.ReplaceAll(lConfig.PackageName, "/", "#"), strings.ToUpper(string(options.Args.Level)))
591
Girish Kumarb03dcee2020-04-14 11:48:15 +0000592 if err != nil {
Girish Kumarda415372020-04-21 15:37:13 +0000593 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Failure", Error: err.Error()})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000594 } else {
Girish Kumarda415372020-04-21 15:37:13 +0000595 var outmsg string
596 cvalid := false
597 pvalid := false
598
599 // Validate if component and package name being set are correct. Add a * against the invalid value
600
601 // For global level, only default package is valid
602 if lConfig.ComponentName == defaultComponentName {
603 if lConfig.PackageName != defaultPackageName {
604 lConfig.PackageName = "*" + lConfig.PackageName
605 outmsg = "Only default package is valid for global"
606 }
607 } else {
608
609 for _, cname := range validComponents {
610 if lConfig.ComponentName == cname {
611 cvalid = true
612 break
613 }
614 }
615
616 // If component is valid, fetch and validate entered package name
617 if cvalid {
618 if validPackages, err := getPackageNames(lConfig.ComponentName); err == nil {
619 for _, pname := range validPackages {
620 if lConfig.PackageName == pname {
621 pvalid = true
622 break
623 }
624 }
625 }
626
627 if !pvalid {
628 lConfig.PackageName = "*" + lConfig.PackageName
629 outmsg = "Entered Package Name is not valid"
630 }
631 } else {
632
633 lConfig.ComponentName = "*" + lConfig.ComponentName
634 outmsg = "Entered Component Name is not Currently active in Voltha"
635 }
636 }
637
638 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Success", Error: outmsg})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000639 }
640
641 }
642
643 outputFormat := CharReplacer.Replace(options.Format)
644 if outputFormat == "" {
645 outputFormat = GetCommandOptionWithDefault("log-level-set", "format", DEFAULT_LOG_RESULT_FORMAT)
646 }
647 result := CommandResult{
648 Format: format.Format(outputFormat),
649 OutputAs: toOutputType(options.OutputAs),
650 NameLimit: options.NameLimit,
651 Data: output,
652 }
653
654 GenerateOutput(&result)
655 return nil
656}
657
658// This method list loglevel for components.
659// For example, using below command loglevel can be list for specific component
660// voltctl loglevel list <componentName>
661// For example, using below command loglevel can be list for all the components with all the packageName
662// voltctl loglevel list
663func (options *ListLogLevelsOpts) Execute(args []string) error {
664
665 var (
666 // Initialize to empty as opposed to nil so that -o json will
667 // display empty list and not null VOL-2742
668 data []model.LogLevel = []model.LogLevel{}
669 componentList []string
670 logLevelConfig map[string]string
671 err error
672 )
673 ProcessGlobalOptions()
674
675 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000676 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000677 defer cancel()
678
Girish Kumare48ec8a2020-08-18 12:28:51 +0000679 cm, cleanupFunc, err := constructConfigManager(ctx)
680 if err != nil {
681 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
682 }
683 defer cleanupFunc()
684
Girish Kumarb03dcee2020-04-14 11:48:15 +0000685 if len(options.Args.Component) == 0 {
686 componentList, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
687 if err != nil {
David K. Bainbridge9189c632021-03-26 21:52:21 +0000688 return fmt.Errorf("Unable to retrieve list of voltha components : %s \nIs ETCD available at %s?", err, GlobalConfig.Current().KvStore)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000689 }
690 } else {
691 componentList = toStringArray(options.Args.Component)
692 }
693
Girish Kumarda415372020-04-21 15:37:13 +0000694 validComponents := getVolthaComponentNames()
695
Girish Kumarb03dcee2020-04-14 11:48:15 +0000696 for _, componentName := range componentList {
697 logConfig := cm.InitComponentConfig(componentName, config.ConfigTypeLogLevel)
698
699 logLevelConfig, err = logConfig.RetrieveAll(ctx)
700 if err != nil {
701 return fmt.Errorf("Unable to retrieve loglevel configuration for component %s : %s", componentName, err)
702 }
703
704 for packageName, level := range logLevelConfig {
705 logLevel := model.LogLevel{}
706 if packageName == "" {
707 continue
708 }
709
Girish Kumarda415372020-04-21 15:37:13 +0000710 cvalid := false
711 pvalid := false
712 outPackageName := strings.ReplaceAll(packageName, "#", "/")
713 outComponentName := componentName
714
715 // Validate retrieved component and package names before printing. Add a * against the invalid value
716 if componentName == defaultComponentName {
717 if packageName != defaultPackageName {
718 outPackageName = "*" + outPackageName
719 }
720 } else {
721 for _, cname := range validComponents {
722 if componentName == cname {
723 cvalid = true
724 break
725 }
726 }
727
728 // For valid component, fetch and verify package name as well
729 if cvalid {
730 if validPackages, err := getPackageNames(componentName); err == nil {
731 for _, pname := range validPackages {
732 if outPackageName == pname {
733 pvalid = true
734 break
735 }
736 }
737 }
738
739 if !pvalid {
740 outPackageName = "*" + outPackageName
741 }
742 } else {
743
744 outComponentName = "*" + componentName
745 }
746 }
747
748 logLevel.PopulateFrom(outComponentName, outPackageName, level)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000749 data = append(data, logLevel)
750 }
751 }
752
753 outputFormat := CharReplacer.Replace(options.Format)
754 if outputFormat == "" {
755 outputFormat = GetCommandOptionWithDefault("log-level-list", "format", DEFAULT_LOG_LEVELS_FORMAT)
756 }
757 orderBy := options.OrderBy
758 if orderBy == "" {
759 orderBy = GetCommandOptionWithDefault("log-level-list", "order", "")
760 }
761
762 result := CommandResult{
763 Format: format.Format(outputFormat),
764 Filter: options.Filter,
765 OrderBy: orderBy,
766 OutputAs: toOutputType(options.OutputAs),
767 NameLimit: options.NameLimit,
768 Data: data,
769 }
770 GenerateOutput(&result)
771 return nil
772}
773
774// This method clear loglevel for components.
775// For example, using below command loglevel can be clear for specific component with default packageName
776// voltctl loglevel clear <componentName>
777// For example, using below command loglevel can be clear for specific component with specific packageName
778// voltctl loglevel clear <componentName#packageName>
779func (options *ClearLogLevelsOpts) Execute(args []string) error {
780
781 var (
782 logLevelConfig []model.LogLevel
783 err error
784 )
785 ProcessGlobalOptions()
786
787 log.SetAllLogLevel(log.FatalLevel)
788
789 logLevelConfig, err = processComponentListArgs(toStringArray(options.Args.Component))
790 if err != nil {
791 return fmt.Errorf("%s", err)
792 }
793
David K. Bainbridge9189c632021-03-26 21:52:21 +0000794 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000795 defer cancel()
796
Girish Kumare48ec8a2020-08-18 12:28:51 +0000797 cm, cleanupFunc, err := constructConfigManager(ctx)
798 if err != nil {
799 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
800 }
801 defer cleanupFunc()
802
803 var output []LogLevelOutput
804
Girish Kumarb03dcee2020-04-14 11:48:15 +0000805 for _, lConfig := range logLevelConfig {
806
807 if lConfig.ComponentName == defaultComponentName {
808 return fmt.Errorf("The global default loglevel cannot be cleared.")
809 }
810
811 logConfig := cm.InitComponentConfig(lConfig.ComponentName, config.ConfigTypeLogLevel)
812
Girish Kumarda415372020-04-21 15:37:13 +0000813 err := logConfig.Delete(ctx, strings.ReplaceAll(lConfig.PackageName, "/", "#"))
Girish Kumarb03dcee2020-04-14 11:48:15 +0000814 if err != nil {
Girish Kumarda415372020-04-21 15:37:13 +0000815 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Failure", Error: err.Error()})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000816 } else {
Girish Kumarda415372020-04-21 15:37:13 +0000817 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Success"})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000818 }
819 }
820
821 outputFormat := CharReplacer.Replace(options.Format)
822 if outputFormat == "" {
823 outputFormat = GetCommandOptionWithDefault("log-level-clear", "format", DEFAULT_LOG_RESULT_FORMAT)
824 }
825
826 result := CommandResult{
827 Format: format.Format(outputFormat),
828 OutputAs: toOutputType(options.OutputAs),
829 NameLimit: options.NameLimit,
830 Data: output,
831 }
832
833 GenerateOutput(&result)
834 return nil
835}
836
837// This method lists registered log packages for components.
838// For example, available log packages can be listed for specific component using below command
839// voltctl loglevel listpackage <componentName>
840// For example, available log packages can be listed for all the components using below command (omitting component name)
841// voltctl loglevel listpackage
842func (options *ListLogPackagesOpts) Execute(args []string) error {
843
844 var (
845 // Initialize to empty as opposed to nil so that -o json will
846 // display empty list and not null VOL-2742
847 data []model.LogLevel = []model.LogLevel{}
848 componentList []string
849 err error
850 )
851
852 ProcessGlobalOptions()
853
854 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000855 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000856 defer cancel()
857
Girish Kumare48ec8a2020-08-18 12:28:51 +0000858 cm, cleanupFunc, err := constructConfigManager(ctx)
859 if err != nil {
860 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
861 }
862 defer cleanupFunc()
863
Girish Kumarb03dcee2020-04-14 11:48:15 +0000864 if len(options.Args.Component) == 0 {
865 componentList, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
866 if err != nil {
867 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
868 }
869
870 // Include default global package as well when displaying packages for all components
871 logLevel := model.LogLevel{}
872 logLevel.PopulateFrom(defaultComponentName, defaultPackageName, "")
873 data = append(data, logLevel)
874 } else {
875 for _, name := range options.Args.Component {
876 componentList = append(componentList, string(name))
877 }
878 }
879
880 for _, componentName := range componentList {
881 componentMetadata := cm.InitComponentConfig(componentName, config.ConfigTypeMetadata)
882
883 value, err := componentMetadata.Retrieve(ctx, logPackagesListKey)
884 if err != nil || value == "" {
885 // Ignore any error in retrieval for log package list; some components may not store it
886 continue
887 }
888
889 var packageList []string
890 if err = json.Unmarshal([]byte(value), &packageList); err != nil {
891 continue
892 }
893
894 for _, packageName := range packageList {
895 logLevel := model.LogLevel{}
896 logLevel.PopulateFrom(componentName, packageName, "")
897 data = append(data, logLevel)
898 }
899 }
900
901 outputFormat := CharReplacer.Replace(options.Format)
902 if outputFormat == "" {
903 outputFormat = GetCommandOptionWithDefault("log-package-list", "format", DEFAULT_LOG_PACKAGES_FORMAT)
904 }
905 orderBy := options.OrderBy
906 if orderBy == "" {
907 orderBy = GetCommandOptionWithDefault("log-package-list", "order", "ComponentName,PackageName")
908 }
909
910 result := CommandResult{
911 Format: format.Format(outputFormat),
912 Filter: options.Filter,
913 OrderBy: orderBy,
914 OutputAs: toOutputType(options.OutputAs),
915 NameLimit: options.NameLimit,
916 Data: data,
917 }
918 GenerateOutput(&result)
919 return nil
920}
Girish Kumare48ec8a2020-08-18 12:28:51 +0000921
922// This method enables log trace publishing for components.
923// For example, using below command, trace publishing can be enabled for specific component
924// voltctl log tracing enable <componentName>
925// Omitting the component name will enable trace publishing for all the components, as shown in below command.
926// voltctl log tracing enable
927func (options *EnableLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +0000928 ProcessGlobalOptions()
929
930 log.SetAllLogLevel(log.FatalLevel)
931
David K. Bainbridge9189c632021-03-26 21:52:21 +0000932 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000933 defer cancel()
934
935 cm, cleanupFunc, err := constructConfigManager(ctx)
936 if err != nil {
937 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
938 }
939 defer cleanupFunc()
940
941 var output []LogLevelOutput
Neha Sharma94313672020-08-25 07:52:28 +0000942 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +0000943
944 if len(options.Args.Component) == 0 {
945 // Apply to all components if no specific component has been indicated
946 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
947 if err != nil {
948 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
949 }
950
951 } else {
952 for _, name := range options.Args.Component {
953 componentNames = append(componentNames, string(name))
954 }
955 }
956
957 validComponents := getVolthaComponentNames()
958
959 for _, component := range componentNames {
960
961 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
962
963 err := config.Save(ctx, logTracingStatusKey, "ENABLED")
964 if err != nil {
965 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
Neha Sharma94313672020-08-25 07:52:28 +0000966 continue
Girish Kumare48ec8a2020-08-18 12:28:51 +0000967 }
Neha Sharma94313672020-08-25 07:52:28 +0000968 outmsg := ""
969 cvalid := false
970 for _, cname := range validComponents {
971 if component == cname {
972 cvalid = true
973 break
974 }
975 }
976
977 // For invalid component, add * against its name to indicate possible mis-configuration
978 if !cvalid {
979 component = "*" + component
980 outmsg = "Entered Component Name is not Currently active in Voltha"
981 }
982 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
Girish Kumare48ec8a2020-08-18 12:28:51 +0000983 }
984
985 outputFormat := CharReplacer.Replace(options.Format)
986 if outputFormat == "" {
987 outputFormat = GetCommandOptionWithDefault("log-tracing-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
988 }
989
990 result := CommandResult{
991 Format: format.Format(outputFormat),
992 OutputAs: toOutputType(options.OutputAs),
993 NameLimit: options.NameLimit,
994 Data: output,
995 }
996
997 GenerateOutput(&result)
998 return nil
999}
1000
1001// This method disables log trace publishing for components.
1002// For example, using below command, trace publishing can be disabled for specific component
1003// voltctl log tracing disable <componentName>
1004// Omitting the component name will disable trace publishing for all the components, as shown in below command.
1005// voltctl log tracing disable
1006func (options *DisableLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +00001007 ProcessGlobalOptions()
1008
1009 log.SetAllLogLevel(log.FatalLevel)
1010
David K. Bainbridge9189c632021-03-26 21:52:21 +00001011 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +00001012 defer cancel()
1013
1014 cm, cleanupFunc, err := constructConfigManager(ctx)
1015 if err != nil {
1016 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1017 }
1018 defer cleanupFunc()
1019
1020 var output []LogLevelOutput
Neha Sharma94313672020-08-25 07:52:28 +00001021 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +00001022
1023 if len(options.Args.Component) == 0 {
1024 // Apply to all components if no specific component has been indicated
1025 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1026 if err != nil {
1027 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1028 }
1029
1030 } else {
1031 for _, name := range options.Args.Component {
1032 componentNames = append(componentNames, string(name))
1033 }
1034 }
1035
1036 validComponents := getVolthaComponentNames()
1037
1038 for _, component := range componentNames {
1039
1040 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1041
1042 err := config.Save(ctx, logTracingStatusKey, "DISABLED")
1043
1044 if err != nil {
1045 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
Neha Sharma94313672020-08-25 07:52:28 +00001046 continue
Girish Kumare48ec8a2020-08-18 12:28:51 +00001047 }
Neha Sharma94313672020-08-25 07:52:28 +00001048 outmsg := ""
1049 cvalid := false
1050 for _, cname := range validComponents {
1051 if component == cname {
1052 cvalid = true
1053 break
1054 }
1055 }
1056
1057 // For invalid component, add * against its name to indicate possible mis-configuration
1058 if !cvalid {
1059 component = "*" + component
1060 outmsg = "Entered Component Name is not Currently active in Voltha"
1061 }
1062
1063 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
Girish Kumare48ec8a2020-08-18 12:28:51 +00001064 }
1065
1066 outputFormat := CharReplacer.Replace(options.Format)
1067 if outputFormat == "" {
1068 outputFormat = GetCommandOptionWithDefault("log-tracing-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1069 }
1070
1071 result := CommandResult{
1072 Format: format.Format(outputFormat),
1073 OutputAs: toOutputType(options.OutputAs),
1074 NameLimit: options.NameLimit,
1075 Data: output,
1076 }
1077
1078 GenerateOutput(&result)
1079 return nil
1080}
1081
1082// This method lists current status of log trace publishing for components.
1083// For example, using below command, trace publishing can be queried for specific component
1084// voltctl log tracing list <componentName>
1085// Omitting the component name will list trace publishing for all the components, as shown in below command.
1086// voltctl log tracing list
1087func (options *ListLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +00001088 ProcessGlobalOptions()
1089
1090 log.SetAllLogLevel(log.FatalLevel)
1091
David K. Bainbridge9189c632021-03-26 21:52:21 +00001092 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +00001093 defer cancel()
1094
1095 cm, cleanupFunc, err := constructConfigManager(ctx)
1096 if err != nil {
1097 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1098 }
1099 defer cleanupFunc()
1100
Neha Sharma94313672020-08-25 07:52:28 +00001101 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +00001102 if len(options.Args.Component) == 0 {
1103 // Apply to all components if no specific component has been indicated
1104 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1105 if err != nil {
1106 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1107 }
1108
1109 } else {
1110 for _, name := range options.Args.Component {
1111 componentNames = append(componentNames, string(name))
1112 }
1113 }
1114
1115 validComponents := getVolthaComponentNames()
Neha Sharma94313672020-08-25 07:52:28 +00001116 data := []model.LogFeature{}
Girish Kumare48ec8a2020-08-18 12:28:51 +00001117
1118 for _, component := range componentNames {
1119
1120 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1121
1122 value, err := config.Retrieve(ctx, logTracingStatusKey)
1123 if err != nil || value == "" {
1124 // Ignore any error in retrieval; move to next component
1125 continue
1126 }
1127
1128 cvalid := false
1129 for _, cname := range validComponents {
1130 if component == cname {
1131 cvalid = true
1132 break
1133 }
1134 }
1135
1136 // For invalid component, add * against its name to indicate possible mis-configuration
1137 if !cvalid {
1138 component = "*" + component
1139 }
1140
1141 logTracingStatus := model.LogFeature{}
1142 logTracingStatus.PopulateFrom(component, value)
1143 data = append(data, logTracingStatus)
1144 }
1145
1146 outputFormat := CharReplacer.Replace(options.Format)
1147 if outputFormat == "" {
1148 outputFormat = GetCommandOptionWithDefault("log-tracing-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
1149 }
1150 orderBy := options.OrderBy
1151 if orderBy == "" {
1152 orderBy = GetCommandOptionWithDefault("log-tracing-list", "order", "ComponentName,Status")
1153 }
1154
1155 result := CommandResult{
1156 Format: format.Format(outputFormat),
1157 Filter: options.Filter,
1158 OrderBy: orderBy,
1159 OutputAs: toOutputType(options.OutputAs),
1160 NameLimit: options.NameLimit,
1161 Data: data,
1162 }
1163 GenerateOutput(&result)
1164 return nil
1165}
Neha Sharma94313672020-08-25 07:52:28 +00001166
1167// This method enables log correlation for components.
1168// For example, using below command, log correlation can be enabled for specific component
1169// voltctl log correlation enable <componentName>
1170// Omitting the component name will enable log correlation for all the components, as shown in below command.
1171// voltctl log correlation enable
1172func (options *EnableLogCorrelationOpts) Execute(args []string) error {
1173 ProcessGlobalOptions()
1174
1175 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001176 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001177 defer cancel()
1178
1179 cm, cleanupFunc, err := constructConfigManager(ctx)
1180 if err != nil {
1181 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1182 }
1183 defer cleanupFunc()
1184
1185 var output []LogLevelOutput
1186 var componentNames []string
1187
1188 if len(options.Args.Component) == 0 {
1189 // Apply to all components if no specific component has been indicated
1190 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1191 if err != nil {
1192 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1193 }
1194 } else {
1195 for _, name := range options.Args.Component {
1196 componentNames = append(componentNames, string(name))
1197 }
1198 }
1199
1200 validComponents := getVolthaComponentNames()
1201
1202 for _, component := range componentNames {
1203 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1204 err := config.Save(ctx, logCorrelationStatusKey, "ENABLED")
1205
1206 if err != nil {
1207 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
1208 continue
1209 }
1210
1211 outmsg := ""
1212 cvalid := false
1213 for _, cname := range validComponents {
1214 if component == cname {
1215 cvalid = true
1216 break
1217 }
1218 }
1219 // For invalid component, add * against its name to indicate possible mis-configuration
1220 if !cvalid {
1221 component = "*" + component
1222 outmsg = "Entered Component Name is not Currently active in Voltha"
1223 }
1224 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
1225 }
1226
1227 outputFormat := CharReplacer.Replace(options.Format)
1228 if outputFormat == "" {
1229 outputFormat = GetCommandOptionWithDefault("log-correlation-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1230 }
1231
1232 result := CommandResult{
1233 Format: format.Format(outputFormat),
1234 OutputAs: toOutputType(options.OutputAs),
1235 NameLimit: options.NameLimit,
1236 Data: output,
1237 }
1238 GenerateOutput(&result)
1239 return nil
1240}
1241
1242// This method disables log correlation for components.
1243// For example, using below command, log correlation can be disabled for specific component
1244// voltctl log correlation disable <componentName>
1245// Omitting the component name will disable log correlation for all the components, as shown in below command.
1246// voltctl log correlation disable
1247func (options *DisableLogCorrelationOpts) Execute(args []string) error {
1248 ProcessGlobalOptions()
1249
1250 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001251 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001252 defer cancel()
1253
1254 cm, cleanupFunc, err := constructConfigManager(ctx)
1255 if err != nil {
1256 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1257 }
1258 defer cleanupFunc()
1259
1260 var output []LogLevelOutput
1261 var componentNames []string
1262
1263 if len(options.Args.Component) == 0 {
1264 // Apply to all components if no specific component has been indicated
1265 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1266 if err != nil {
1267 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1268 }
1269 } else {
1270 for _, name := range options.Args.Component {
1271 componentNames = append(componentNames, string(name))
1272 }
1273 }
1274
1275 validComponents := getVolthaComponentNames()
1276
1277 for _, component := range componentNames {
1278
1279 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1280 err := config.Save(ctx, logCorrelationStatusKey, "DISABLED")
1281
1282 if err != nil {
1283 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
1284 continue
1285 }
1286 outmsg := ""
1287 cvalid := false
1288 for _, cname := range validComponents {
1289 if component == cname {
1290 cvalid = true
1291 break
1292 }
1293 }
1294 // For invalid component, add * against its name to indicate possible mis-configuration
1295 if !cvalid {
1296 component = "*" + component
1297 outmsg = "Entered Component Name is not Currently active in Voltha"
1298 }
1299 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
1300 }
1301
1302 outputFormat := CharReplacer.Replace(options.Format)
1303 if outputFormat == "" {
1304 outputFormat = GetCommandOptionWithDefault("log-correlation-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1305 }
1306
1307 result := CommandResult{
1308 Format: format.Format(outputFormat),
1309 OutputAs: toOutputType(options.OutputAs),
1310 NameLimit: options.NameLimit,
1311 Data: output,
1312 }
1313 GenerateOutput(&result)
1314 return nil
1315}
1316
1317// This method lists current status of log correlation for components.
1318// For example, using below command, log correlation can be queried for specific component
1319// voltctl log correlation list <componentName>
1320// Omitting the component name will list log correlation for all the components, as shown in below command.
1321// voltctl log correlation list
1322func (options *ListLogCorrelationOpts) Execute(args []string) error {
1323 ProcessGlobalOptions()
1324 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001325 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001326 defer cancel()
1327
1328 cm, cleanupFunc, err := constructConfigManager(ctx)
1329 if err != nil {
1330 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1331 }
1332 defer cleanupFunc()
1333
1334 var componentNames []string
1335 if len(options.Args.Component) == 0 {
1336 // Apply to all components if no specific component has been indicated
1337 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1338 if err != nil {
1339 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1340 }
1341 } else {
1342 for _, name := range options.Args.Component {
1343 componentNames = append(componentNames, string(name))
1344 }
1345 }
1346
1347 validComponents := getVolthaComponentNames()
1348 data := []model.LogFeature{}
1349
1350 for _, component := range componentNames {
1351 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1352 value, err := config.Retrieve(ctx, logCorrelationStatusKey)
1353 if err != nil || value == "" {
1354 // Ignore any error in retrieval; move to next component
1355 continue
1356 }
1357 cvalid := false
1358
1359 for _, cname := range validComponents {
1360 if component == cname {
1361 cvalid = true
1362 break
1363 }
1364 }
1365 // For invalid component, add * against its name to indicate possible mis-configuration
1366 if !cvalid {
1367 component = "*" + component
1368 }
1369 logCorrelationStatus := model.LogFeature{}
1370 logCorrelationStatus.PopulateFrom(component, value)
1371 data = append(data, logCorrelationStatus)
1372 }
1373
1374 outputFormat := CharReplacer.Replace(options.Format)
1375 if outputFormat == "" {
1376 outputFormat = GetCommandOptionWithDefault("log-correlation-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
1377 }
1378 orderBy := options.OrderBy
1379 if orderBy == "" {
1380 orderBy = GetCommandOptionWithDefault("log-correlation-list", "order", "ComponentName,Status")
1381 }
1382
1383 result := CommandResult{
1384 Format: format.Format(outputFormat),
1385 Filter: options.Filter,
1386 OrderBy: orderBy,
1387 OutputAs: toOutputType(options.OutputAs),
1388 NameLimit: options.NameLimit,
1389 Data: data,
1390 }
1391 GenerateOutput(&result)
1392 return nil
1393}