blob: 77d97e9beab0ebc0ca056939ac1489ea4de56585 [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"
Neha Sharmadb06e2b2020-05-07 20:39:10 +000023 "net"
24 "strconv"
Girish Kumarb03dcee2020-04-14 11:48:15 +000025 "strings"
26
27 flags "github.com/jessevdk/go-flags"
28 "github.com/opencord/voltctl/pkg/format"
29 "github.com/opencord/voltctl/pkg/model"
kesavand8ec4fc02021-01-27 09:10:22 -050030 "github.com/opencord/voltha-lib-go/v4/pkg/config"
31 "github.com/opencord/voltha-lib-go/v4/pkg/db/kvstore"
32 "github.com/opencord/voltha-lib-go/v4/pkg/log"
Girish Kumarb03dcee2020-04-14 11:48:15 +000033 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
34 "k8s.io/client-go/kubernetes"
35 "k8s.io/client-go/tools/clientcmd"
36)
37
38const (
Neha Sharma94313672020-08-25 07:52:28 +000039 defaultComponentName = "global"
40 defaultPackageName = "default"
41 logPackagesListKey = "log_package_list" // kvstore key containing list of allowed log packages
42 logTracingStatusKey = "trace_publish"
43 logCorrelationStatusKey = "log_correlation"
Girish Kumarb03dcee2020-04-14 11:48:15 +000044)
45
46// Custom Option representing <component-name>#<package-name> format (package is optional)
47// This is used by 'log level set' commands
48type ComponentAndPackageName string
49
50// Custom Option representing currently configured log configuration in <component-name>#<package-name> format (package is optional)
51// This is used by 'log level clear' commands
52type ConfiguredComponentAndPackageName string
53
54// Custom Option representing component-name. This is used by 'log level list' and 'log package list' commands
55type ComponentName string
56
57// Custom Option representing Log Level (one of debug, info, warn, error, fatal)
58type LevelName string
59
60// LogLevelOutput represents the output structure for the loglevel
61type LogLevelOutput struct {
62 ComponentName string
Girish Kumarda415372020-04-21 15:37:13 +000063 PackageName string
Girish Kumarb03dcee2020-04-14 11:48:15 +000064 Status string
65 Error string
66}
67
Girish Kumare48ec8a2020-08-18 12:28:51 +000068// SetLogLevelOpts represents the supported CLI arguments for the log level set command
Girish Kumarb03dcee2020-04-14 11:48:15 +000069type SetLogLevelOpts struct {
70 OutputOptions
71 Args struct {
72 Level LevelName
73 Component []ComponentAndPackageName
74 } `positional-args:"yes" required:"yes"`
75}
76
Girish Kumare48ec8a2020-08-18 12:28:51 +000077// ListLogLevelOpts represents the supported CLI arguments for the log level list command
Girish Kumarb03dcee2020-04-14 11:48:15 +000078type ListLogLevelsOpts struct {
79 ListOutputOptions
80 Args struct {
81 Component []ComponentName
82 } `positional-args:"yes" required:"yes"`
83}
84
Girish Kumare48ec8a2020-08-18 12:28:51 +000085// ClearLogLevelOpts represents the supported CLI arguments for the log level clear command
Girish Kumarb03dcee2020-04-14 11:48:15 +000086type ClearLogLevelsOpts struct {
87 OutputOptions
88 Args struct {
89 Component []ConfiguredComponentAndPackageName
90 } `positional-args:"yes" required:"yes"`
91}
92
Girish Kumare48ec8a2020-08-18 12:28:51 +000093// ListLogLevelOpts represents the supported CLI arguments for the log level list command
Girish Kumarb03dcee2020-04-14 11:48:15 +000094type ListLogPackagesOpts struct {
95 ListOutputOptions
96 Args struct {
97 Component []ComponentName
98 } `positional-args:"yes" required:"yes"`
99}
100
Girish Kumare48ec8a2020-08-18 12:28:51 +0000101// EnableLogTracingOpts represents the supported CLI arguments for the log tracing enable command
102type EnableLogTracingOpts struct {
103 OutputOptions
104 Args struct {
105 Component []ComponentName
106 } `positional-args:"yes" required:"yes"`
107}
108
109// DisableLogTracingOpts represents the supported CLI arguments for the log tracing disable command
110type DisableLogTracingOpts struct {
111 OutputOptions
112 Args struct {
113 Component []ComponentName
114 } `positional-args:"yes" required:"yes"`
115}
116
117// ListLogTracingOpts represents the supported CLI arguments for the log tracing list command
118type ListLogTracingOpts struct {
119 ListOutputOptions
120 Args struct {
121 Component []ComponentName
122 } `positional-args:"yes" required:"yes"`
123}
124
Neha Sharma94313672020-08-25 07:52:28 +0000125// EnableLogCorrelationOpts represent the supported CLI arguments for the log correlation enable command
126type EnableLogCorrelationOpts struct {
127 OutputOptions
128 Args struct {
129 Component []ComponentName
130 } `positional-args:"yes" required:"yes"`
131}
132
133// DisableLogCorrelationOpts represent the supported CLI arguments for the log correlation disable command
134type DisableLogCorrelationOpts struct {
135 OutputOptions
136 Args struct {
137 Component []ComponentName
138 } `positional-args:"yes" required:"yes"`
139}
140
141// ListLogCorrelationOpts represents the supported CLI arguments for the log correlation list command
142type ListLogCorrelationOpts struct {
143 ListOutputOptions
144 Args struct {
145 Component []ComponentName
146 } `positional-args:"yes" required:"yes"`
147}
148
Girish Kumarb03dcee2020-04-14 11:48:15 +0000149// LogPackageOpts represents the log package commands
150type LogPackageOpts struct {
151 ListLogPackages ListLogPackagesOpts `command:"list"`
152}
153
154// LogLevelOpts represents the log level commands
155type LogLevelOpts struct {
156 SetLogLevel SetLogLevelOpts `command:"set"`
157 ListLogLevels ListLogLevelsOpts `command:"list"`
158 ClearLogLevels ClearLogLevelsOpts `command:"clear"`
159}
160
Girish Kumare48ec8a2020-08-18 12:28:51 +0000161// LogTracingOpts represents the log tracing commands
162type LogTracingOpts struct {
163 EnableLogTracing EnableLogTracingOpts `command:"enable"`
164 DisableLogTracing DisableLogTracingOpts `command:"disable"`
165 ListLogTracing ListLogTracingOpts `command:"list"`
166}
167
Neha Sharma94313672020-08-25 07:52:28 +0000168// LogCorrelationOpts represents the log correlation commands
169type LogCorrelationOpts struct {
170 EnableLogCorrelation EnableLogCorrelationOpts `command:"enable"`
171 DisableLogCorrelation DisableLogCorrelationOpts `command:"disable"`
172 ListLogCorrelation ListLogCorrelationOpts `command:"list"`
173}
174
Girish Kumarb03dcee2020-04-14 11:48:15 +0000175// LogOpts represents the log commands
176type LogOpts struct {
Neha Sharma94313672020-08-25 07:52:28 +0000177 LogLevel LogLevelOpts `command:"level"`
178 LogPackage LogPackageOpts `command:"package"`
179 LogTracing LogTracingOpts `command:"tracing"`
180 LogCorrelation LogCorrelationOpts `command:"correlation"`
Girish Kumarb03dcee2020-04-14 11:48:15 +0000181}
182
183var logOpts = LogOpts{}
184
185const (
Girish Kumare48ec8a2020-08-18 12:28:51 +0000186 DEFAULT_LOG_LEVELS_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}\t{{.Level}}"
187 DEFAULT_LOG_PACKAGES_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}"
188 DEFAULT_LOG_FEATURE_STATUS_FORMAT = "table{{ .ComponentName }}\t{{.Status}}"
189 DEFAULT_LOG_RESULT_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}\t{{.Status}}\t{{.Error}}"
190 DEFAULT_LOG_FEATURE_RESULT_FORMAT = "table{{ .ComponentName }}\t{{.Status}}\t{{.Error}}"
Girish Kumarb03dcee2020-04-14 11:48:15 +0000191)
192
193func toStringArray(arg interface{}) []string {
194 var list []string
195 if cnl, ok := arg.([]ComponentName); ok {
196 for _, cn := range cnl {
197 list = append(list, string(cn))
198 }
199 } else if cpnl, ok := arg.([]ComponentAndPackageName); ok {
200 for _, cpn := range cpnl {
201 list = append(list, string(cpn))
202 }
203 } else if ccpnl, ok := arg.([]ConfiguredComponentAndPackageName); ok {
204 for _, ccpn := range ccpnl {
205 list = append(list, string(ccpn))
206 }
207 }
208
209 return list
210}
211
212// RegisterLogCommands is used to register log and its sub-commands e.g. level, package etc
213func RegisterLogCommands(parent *flags.Parser) {
Girish Kumare48ec8a2020-08-18 12:28:51 +0000214 _, 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 +0000215 if err != nil {
216 Error.Fatalf("Unable to register log commands with voltctl command parser: %s", err.Error())
217 }
218}
219
220// Common method to get list of VOLTHA components using k8s API. Used for validation and auto-complete
221// Just return a blank list in case of any error
222func getVolthaComponentNames() []string {
223 var componentList []string
224
225 // use the current context in kubeconfig
226 config, err := clientcmd.BuildConfigFromFlags("", GlobalOptions.K8sConfig)
227 if err != nil {
228 // Ignore any error
229 return componentList
230 }
231
232 // create the clientset
233 clientset, err := kubernetes.NewForConfig(config)
234 if err != nil {
235 return componentList
236 }
237
238 pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{
239 LabelSelector: "app.kubernetes.io/part-of=voltha",
240 })
241 if err != nil {
242 return componentList
243 }
244
245 for _, pod := range pods.Items {
246 componentList = append(componentList, pod.ObjectMeta.Labels["app.kubernetes.io/name"])
247 }
248
249 return componentList
250}
251
Girish Kumare48ec8a2020-08-18 12:28:51 +0000252func constructConfigManager(ctx context.Context) (*config.ConfigManager, func(), error) {
253 client, err := kvstore.NewEtcdClient(ctx, GlobalConfig.KvStore, GlobalConfig.KvStoreConfig.Timeout, log.FatalLevel)
254 if err != nil {
255 return nil, nil, fmt.Errorf("Unable to create kvstore client %s", err)
256 }
257
258 // Already error checked during option processing
259 host, port, _ := splitEndpoint(GlobalConfig.KvStore, defaultKvHost, defaultKvPort)
260 cm := config.NewConfigManager(ctx, client, supportedKvStoreType, net.JoinHostPort(host, strconv.Itoa(port)), GlobalConfig.KvStoreConfig.Timeout)
261 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)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000272 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
273 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)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000392 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
393 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
Girish Kumarb03dcee2020-04-14 11:48:15 +0000574 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
575 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)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000676 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
677 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 {
Girish Kumare48ec8a2020-08-18 12:28:51 +0000688 host, port, _ := splitEndpoint(GlobalConfig.KvStore, defaultKvHost, defaultKvPort)
Matteo Scandolo11b74902020-06-09 15:27:32 -0700689 return fmt.Errorf("Unable to retrieve list of voltha components : %s \nIs ETCD available at %s:%d?", err, host, port)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000690 }
691 } else {
692 componentList = toStringArray(options.Args.Component)
693 }
694
Girish Kumarda415372020-04-21 15:37:13 +0000695 validComponents := getVolthaComponentNames()
696
Girish Kumarb03dcee2020-04-14 11:48:15 +0000697 for _, componentName := range componentList {
698 logConfig := cm.InitComponentConfig(componentName, config.ConfigTypeLogLevel)
699
700 logLevelConfig, err = logConfig.RetrieveAll(ctx)
701 if err != nil {
702 return fmt.Errorf("Unable to retrieve loglevel configuration for component %s : %s", componentName, err)
703 }
704
705 for packageName, level := range logLevelConfig {
706 logLevel := model.LogLevel{}
707 if packageName == "" {
708 continue
709 }
710
Girish Kumarda415372020-04-21 15:37:13 +0000711 cvalid := false
712 pvalid := false
713 outPackageName := strings.ReplaceAll(packageName, "#", "/")
714 outComponentName := componentName
715
716 // Validate retrieved component and package names before printing. Add a * against the invalid value
717 if componentName == defaultComponentName {
718 if packageName != defaultPackageName {
719 outPackageName = "*" + outPackageName
720 }
721 } else {
722 for _, cname := range validComponents {
723 if componentName == cname {
724 cvalid = true
725 break
726 }
727 }
728
729 // For valid component, fetch and verify package name as well
730 if cvalid {
731 if validPackages, err := getPackageNames(componentName); err == nil {
732 for _, pname := range validPackages {
733 if outPackageName == pname {
734 pvalid = true
735 break
736 }
737 }
738 }
739
740 if !pvalid {
741 outPackageName = "*" + outPackageName
742 }
743 } else {
744
745 outComponentName = "*" + componentName
746 }
747 }
748
749 logLevel.PopulateFrom(outComponentName, outPackageName, level)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000750 data = append(data, logLevel)
751 }
752 }
753
754 outputFormat := CharReplacer.Replace(options.Format)
755 if outputFormat == "" {
756 outputFormat = GetCommandOptionWithDefault("log-level-list", "format", DEFAULT_LOG_LEVELS_FORMAT)
757 }
758 orderBy := options.OrderBy
759 if orderBy == "" {
760 orderBy = GetCommandOptionWithDefault("log-level-list", "order", "")
761 }
762
763 result := CommandResult{
764 Format: format.Format(outputFormat),
765 Filter: options.Filter,
766 OrderBy: orderBy,
767 OutputAs: toOutputType(options.OutputAs),
768 NameLimit: options.NameLimit,
769 Data: data,
770 }
771 GenerateOutput(&result)
772 return nil
773}
774
775// This method clear loglevel for components.
776// For example, using below command loglevel can be clear for specific component with default packageName
777// voltctl loglevel clear <componentName>
778// For example, using below command loglevel can be clear for specific component with specific packageName
779// voltctl loglevel clear <componentName#packageName>
780func (options *ClearLogLevelsOpts) Execute(args []string) error {
781
782 var (
783 logLevelConfig []model.LogLevel
784 err error
785 )
786 ProcessGlobalOptions()
787
788 log.SetAllLogLevel(log.FatalLevel)
789
790 logLevelConfig, err = processComponentListArgs(toStringArray(options.Args.Component))
791 if err != nil {
792 return fmt.Errorf("%s", err)
793 }
794
Girish Kumarb03dcee2020-04-14 11:48:15 +0000795 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
796 defer cancel()
797
Girish Kumare48ec8a2020-08-18 12:28:51 +0000798 cm, cleanupFunc, err := constructConfigManager(ctx)
799 if err != nil {
800 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
801 }
802 defer cleanupFunc()
803
804 var output []LogLevelOutput
805
Girish Kumarb03dcee2020-04-14 11:48:15 +0000806 for _, lConfig := range logLevelConfig {
807
808 if lConfig.ComponentName == defaultComponentName {
809 return fmt.Errorf("The global default loglevel cannot be cleared.")
810 }
811
812 logConfig := cm.InitComponentConfig(lConfig.ComponentName, config.ConfigTypeLogLevel)
813
Girish Kumarda415372020-04-21 15:37:13 +0000814 err := logConfig.Delete(ctx, strings.ReplaceAll(lConfig.PackageName, "/", "#"))
Girish Kumarb03dcee2020-04-14 11:48:15 +0000815 if err != nil {
Girish Kumarda415372020-04-21 15:37:13 +0000816 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Failure", Error: err.Error()})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000817 } else {
Girish Kumarda415372020-04-21 15:37:13 +0000818 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Success"})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000819 }
820 }
821
822 outputFormat := CharReplacer.Replace(options.Format)
823 if outputFormat == "" {
824 outputFormat = GetCommandOptionWithDefault("log-level-clear", "format", DEFAULT_LOG_RESULT_FORMAT)
825 }
826
827 result := CommandResult{
828 Format: format.Format(outputFormat),
829 OutputAs: toOutputType(options.OutputAs),
830 NameLimit: options.NameLimit,
831 Data: output,
832 }
833
834 GenerateOutput(&result)
835 return nil
836}
837
838// This method lists registered log packages for components.
839// For example, available log packages can be listed for specific component using below command
840// voltctl loglevel listpackage <componentName>
841// For example, available log packages can be listed for all the components using below command (omitting component name)
842// voltctl loglevel listpackage
843func (options *ListLogPackagesOpts) Execute(args []string) error {
844
845 var (
846 // Initialize to empty as opposed to nil so that -o json will
847 // display empty list and not null VOL-2742
848 data []model.LogLevel = []model.LogLevel{}
849 componentList []string
850 err error
851 )
852
853 ProcessGlobalOptions()
854
855 log.SetAllLogLevel(log.FatalLevel)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000856 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
857 defer cancel()
858
Girish Kumare48ec8a2020-08-18 12:28:51 +0000859 cm, cleanupFunc, err := constructConfigManager(ctx)
860 if err != nil {
861 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
862 }
863 defer cleanupFunc()
864
Girish Kumarb03dcee2020-04-14 11:48:15 +0000865 if len(options.Args.Component) == 0 {
866 componentList, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
867 if err != nil {
868 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
869 }
870
871 // Include default global package as well when displaying packages for all components
872 logLevel := model.LogLevel{}
873 logLevel.PopulateFrom(defaultComponentName, defaultPackageName, "")
874 data = append(data, logLevel)
875 } else {
876 for _, name := range options.Args.Component {
877 componentList = append(componentList, string(name))
878 }
879 }
880
881 for _, componentName := range componentList {
882 componentMetadata := cm.InitComponentConfig(componentName, config.ConfigTypeMetadata)
883
884 value, err := componentMetadata.Retrieve(ctx, logPackagesListKey)
885 if err != nil || value == "" {
886 // Ignore any error in retrieval for log package list; some components may not store it
887 continue
888 }
889
890 var packageList []string
891 if err = json.Unmarshal([]byte(value), &packageList); err != nil {
892 continue
893 }
894
895 for _, packageName := range packageList {
896 logLevel := model.LogLevel{}
897 logLevel.PopulateFrom(componentName, packageName, "")
898 data = append(data, logLevel)
899 }
900 }
901
902 outputFormat := CharReplacer.Replace(options.Format)
903 if outputFormat == "" {
904 outputFormat = GetCommandOptionWithDefault("log-package-list", "format", DEFAULT_LOG_PACKAGES_FORMAT)
905 }
906 orderBy := options.OrderBy
907 if orderBy == "" {
908 orderBy = GetCommandOptionWithDefault("log-package-list", "order", "ComponentName,PackageName")
909 }
910
911 result := CommandResult{
912 Format: format.Format(outputFormat),
913 Filter: options.Filter,
914 OrderBy: orderBy,
915 OutputAs: toOutputType(options.OutputAs),
916 NameLimit: options.NameLimit,
917 Data: data,
918 }
919 GenerateOutput(&result)
920 return nil
921}
Girish Kumare48ec8a2020-08-18 12:28:51 +0000922
923// This method enables log trace publishing for components.
924// For example, using below command, trace publishing can be enabled for specific component
925// voltctl log tracing enable <componentName>
926// Omitting the component name will enable trace publishing for all the components, as shown in below command.
927// voltctl log tracing enable
928func (options *EnableLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +0000929 ProcessGlobalOptions()
930
931 log.SetAllLogLevel(log.FatalLevel)
932
933 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
934 defer cancel()
935
936 cm, cleanupFunc, err := constructConfigManager(ctx)
937 if err != nil {
938 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
939 }
940 defer cleanupFunc()
941
942 var output []LogLevelOutput
Neha Sharma94313672020-08-25 07:52:28 +0000943 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +0000944
945 if len(options.Args.Component) == 0 {
946 // Apply to all components if no specific component has been indicated
947 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
948 if err != nil {
949 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
950 }
951
952 } else {
953 for _, name := range options.Args.Component {
954 componentNames = append(componentNames, string(name))
955 }
956 }
957
958 validComponents := getVolthaComponentNames()
959
960 for _, component := range componentNames {
961
962 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
963
964 err := config.Save(ctx, logTracingStatusKey, "ENABLED")
965 if err != nil {
966 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
Neha Sharma94313672020-08-25 07:52:28 +0000967 continue
Girish Kumare48ec8a2020-08-18 12:28:51 +0000968 }
Neha Sharma94313672020-08-25 07:52:28 +0000969 outmsg := ""
970 cvalid := false
971 for _, cname := range validComponents {
972 if component == cname {
973 cvalid = true
974 break
975 }
976 }
977
978 // For invalid component, add * against its name to indicate possible mis-configuration
979 if !cvalid {
980 component = "*" + component
981 outmsg = "Entered Component Name is not Currently active in Voltha"
982 }
983 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
Girish Kumare48ec8a2020-08-18 12:28:51 +0000984 }
985
986 outputFormat := CharReplacer.Replace(options.Format)
987 if outputFormat == "" {
988 outputFormat = GetCommandOptionWithDefault("log-tracing-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
989 }
990
991 result := CommandResult{
992 Format: format.Format(outputFormat),
993 OutputAs: toOutputType(options.OutputAs),
994 NameLimit: options.NameLimit,
995 Data: output,
996 }
997
998 GenerateOutput(&result)
999 return nil
1000}
1001
1002// This method disables log trace publishing for components.
1003// For example, using below command, trace publishing can be disabled for specific component
1004// voltctl log tracing disable <componentName>
1005// Omitting the component name will disable trace publishing for all the components, as shown in below command.
1006// voltctl log tracing disable
1007func (options *DisableLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +00001008 ProcessGlobalOptions()
1009
1010 log.SetAllLogLevel(log.FatalLevel)
1011
1012 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
1013 defer cancel()
1014
1015 cm, cleanupFunc, err := constructConfigManager(ctx)
1016 if err != nil {
1017 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1018 }
1019 defer cleanupFunc()
1020
1021 var output []LogLevelOutput
Neha Sharma94313672020-08-25 07:52:28 +00001022 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +00001023
1024 if len(options.Args.Component) == 0 {
1025 // Apply to all components if no specific component has been indicated
1026 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1027 if err != nil {
1028 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1029 }
1030
1031 } else {
1032 for _, name := range options.Args.Component {
1033 componentNames = append(componentNames, string(name))
1034 }
1035 }
1036
1037 validComponents := getVolthaComponentNames()
1038
1039 for _, component := range componentNames {
1040
1041 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1042
1043 err := config.Save(ctx, logTracingStatusKey, "DISABLED")
1044
1045 if err != nil {
1046 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
Neha Sharma94313672020-08-25 07:52:28 +00001047 continue
Girish Kumare48ec8a2020-08-18 12:28:51 +00001048 }
Neha Sharma94313672020-08-25 07:52:28 +00001049 outmsg := ""
1050 cvalid := false
1051 for _, cname := range validComponents {
1052 if component == cname {
1053 cvalid = true
1054 break
1055 }
1056 }
1057
1058 // For invalid component, add * against its name to indicate possible mis-configuration
1059 if !cvalid {
1060 component = "*" + component
1061 outmsg = "Entered Component Name is not Currently active in Voltha"
1062 }
1063
1064 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
Girish Kumare48ec8a2020-08-18 12:28:51 +00001065 }
1066
1067 outputFormat := CharReplacer.Replace(options.Format)
1068 if outputFormat == "" {
1069 outputFormat = GetCommandOptionWithDefault("log-tracing-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1070 }
1071
1072 result := CommandResult{
1073 Format: format.Format(outputFormat),
1074 OutputAs: toOutputType(options.OutputAs),
1075 NameLimit: options.NameLimit,
1076 Data: output,
1077 }
1078
1079 GenerateOutput(&result)
1080 return nil
1081}
1082
1083// This method lists current status of log trace publishing for components.
1084// For example, using below command, trace publishing can be queried for specific component
1085// voltctl log tracing list <componentName>
1086// Omitting the component name will list trace publishing for all the components, as shown in below command.
1087// voltctl log tracing list
1088func (options *ListLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +00001089 ProcessGlobalOptions()
1090
1091 log.SetAllLogLevel(log.FatalLevel)
1092
1093 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
1094 defer cancel()
1095
1096 cm, cleanupFunc, err := constructConfigManager(ctx)
1097 if err != nil {
1098 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1099 }
1100 defer cleanupFunc()
1101
Neha Sharma94313672020-08-25 07:52:28 +00001102 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +00001103 if len(options.Args.Component) == 0 {
1104 // Apply to all components if no specific component has been indicated
1105 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1106 if err != nil {
1107 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1108 }
1109
1110 } else {
1111 for _, name := range options.Args.Component {
1112 componentNames = append(componentNames, string(name))
1113 }
1114 }
1115
1116 validComponents := getVolthaComponentNames()
Neha Sharma94313672020-08-25 07:52:28 +00001117 data := []model.LogFeature{}
Girish Kumare48ec8a2020-08-18 12:28:51 +00001118
1119 for _, component := range componentNames {
1120
1121 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1122
1123 value, err := config.Retrieve(ctx, logTracingStatusKey)
1124 if err != nil || value == "" {
1125 // Ignore any error in retrieval; move to next component
1126 continue
1127 }
1128
1129 cvalid := false
1130 for _, cname := range validComponents {
1131 if component == cname {
1132 cvalid = true
1133 break
1134 }
1135 }
1136
1137 // For invalid component, add * against its name to indicate possible mis-configuration
1138 if !cvalid {
1139 component = "*" + component
1140 }
1141
1142 logTracingStatus := model.LogFeature{}
1143 logTracingStatus.PopulateFrom(component, value)
1144 data = append(data, logTracingStatus)
1145 }
1146
1147 outputFormat := CharReplacer.Replace(options.Format)
1148 if outputFormat == "" {
1149 outputFormat = GetCommandOptionWithDefault("log-tracing-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
1150 }
1151 orderBy := options.OrderBy
1152 if orderBy == "" {
1153 orderBy = GetCommandOptionWithDefault("log-tracing-list", "order", "ComponentName,Status")
1154 }
1155
1156 result := CommandResult{
1157 Format: format.Format(outputFormat),
1158 Filter: options.Filter,
1159 OrderBy: orderBy,
1160 OutputAs: toOutputType(options.OutputAs),
1161 NameLimit: options.NameLimit,
1162 Data: data,
1163 }
1164 GenerateOutput(&result)
1165 return nil
1166}
Neha Sharma94313672020-08-25 07:52:28 +00001167
1168// This method enables log correlation for components.
1169// For example, using below command, log correlation can be enabled for specific component
1170// voltctl log correlation enable <componentName>
1171// Omitting the component name will enable log correlation for all the components, as shown in below command.
1172// voltctl log correlation enable
1173func (options *EnableLogCorrelationOpts) Execute(args []string) error {
1174 ProcessGlobalOptions()
1175
1176 log.SetAllLogLevel(log.FatalLevel)
1177 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
1178 defer cancel()
1179
1180 cm, cleanupFunc, err := constructConfigManager(ctx)
1181 if err != nil {
1182 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1183 }
1184 defer cleanupFunc()
1185
1186 var output []LogLevelOutput
1187 var componentNames []string
1188
1189 if len(options.Args.Component) == 0 {
1190 // Apply to all components if no specific component has been indicated
1191 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1192 if err != nil {
1193 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1194 }
1195 } else {
1196 for _, name := range options.Args.Component {
1197 componentNames = append(componentNames, string(name))
1198 }
1199 }
1200
1201 validComponents := getVolthaComponentNames()
1202
1203 for _, component := range componentNames {
1204 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1205 err := config.Save(ctx, logCorrelationStatusKey, "ENABLED")
1206
1207 if err != nil {
1208 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
1209 continue
1210 }
1211
1212 outmsg := ""
1213 cvalid := false
1214 for _, cname := range validComponents {
1215 if component == cname {
1216 cvalid = true
1217 break
1218 }
1219 }
1220 // For invalid component, add * against its name to indicate possible mis-configuration
1221 if !cvalid {
1222 component = "*" + component
1223 outmsg = "Entered Component Name is not Currently active in Voltha"
1224 }
1225 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
1226 }
1227
1228 outputFormat := CharReplacer.Replace(options.Format)
1229 if outputFormat == "" {
1230 outputFormat = GetCommandOptionWithDefault("log-correlation-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1231 }
1232
1233 result := CommandResult{
1234 Format: format.Format(outputFormat),
1235 OutputAs: toOutputType(options.OutputAs),
1236 NameLimit: options.NameLimit,
1237 Data: output,
1238 }
1239 GenerateOutput(&result)
1240 return nil
1241}
1242
1243// This method disables log correlation for components.
1244// For example, using below command, log correlation can be disabled for specific component
1245// voltctl log correlation disable <componentName>
1246// Omitting the component name will disable log correlation for all the components, as shown in below command.
1247// voltctl log correlation disable
1248func (options *DisableLogCorrelationOpts) Execute(args []string) error {
1249 ProcessGlobalOptions()
1250
1251 log.SetAllLogLevel(log.FatalLevel)
1252 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
1253 defer cancel()
1254
1255 cm, cleanupFunc, err := constructConfigManager(ctx)
1256 if err != nil {
1257 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1258 }
1259 defer cleanupFunc()
1260
1261 var output []LogLevelOutput
1262 var componentNames []string
1263
1264 if len(options.Args.Component) == 0 {
1265 // Apply to all components if no specific component has been indicated
1266 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1267 if err != nil {
1268 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1269 }
1270 } else {
1271 for _, name := range options.Args.Component {
1272 componentNames = append(componentNames, string(name))
1273 }
1274 }
1275
1276 validComponents := getVolthaComponentNames()
1277
1278 for _, component := range componentNames {
1279
1280 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1281 err := config.Save(ctx, logCorrelationStatusKey, "DISABLED")
1282
1283 if err != nil {
1284 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
1285 continue
1286 }
1287 outmsg := ""
1288 cvalid := false
1289 for _, cname := range validComponents {
1290 if component == cname {
1291 cvalid = true
1292 break
1293 }
1294 }
1295 // For invalid component, add * against its name to indicate possible mis-configuration
1296 if !cvalid {
1297 component = "*" + component
1298 outmsg = "Entered Component Name is not Currently active in Voltha"
1299 }
1300 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
1301 }
1302
1303 outputFormat := CharReplacer.Replace(options.Format)
1304 if outputFormat == "" {
1305 outputFormat = GetCommandOptionWithDefault("log-correlation-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1306 }
1307
1308 result := CommandResult{
1309 Format: format.Format(outputFormat),
1310 OutputAs: toOutputType(options.OutputAs),
1311 NameLimit: options.NameLimit,
1312 Data: output,
1313 }
1314 GenerateOutput(&result)
1315 return nil
1316}
1317
1318// This method lists current status of log correlation for components.
1319// For example, using below command, log correlation can be queried for specific component
1320// voltctl log correlation list <componentName>
1321// Omitting the component name will list log correlation for all the components, as shown in below command.
1322// voltctl log correlation list
1323func (options *ListLogCorrelationOpts) Execute(args []string) error {
1324 ProcessGlobalOptions()
1325 log.SetAllLogLevel(log.FatalLevel)
1326 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
1327 defer cancel()
1328
1329 cm, cleanupFunc, err := constructConfigManager(ctx)
1330 if err != nil {
1331 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1332 }
1333 defer cleanupFunc()
1334
1335 var componentNames []string
1336 if len(options.Args.Component) == 0 {
1337 // Apply to all components if no specific component has been indicated
1338 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1339 if err != nil {
1340 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1341 }
1342 } else {
1343 for _, name := range options.Args.Component {
1344 componentNames = append(componentNames, string(name))
1345 }
1346 }
1347
1348 validComponents := getVolthaComponentNames()
1349 data := []model.LogFeature{}
1350
1351 for _, component := range componentNames {
1352 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1353 value, err := config.Retrieve(ctx, logCorrelationStatusKey)
1354 if err != nil || value == "" {
1355 // Ignore any error in retrieval; move to next component
1356 continue
1357 }
1358 cvalid := false
1359
1360 for _, cname := range validComponents {
1361 if component == cname {
1362 cvalid = true
1363 break
1364 }
1365 }
1366 // For invalid component, add * against its name to indicate possible mis-configuration
1367 if !cvalid {
1368 component = "*" + component
1369 }
1370 logCorrelationStatus := model.LogFeature{}
1371 logCorrelationStatus.PopulateFrom(component, value)
1372 data = append(data, logCorrelationStatus)
1373 }
1374
1375 outputFormat := CharReplacer.Replace(options.Format)
1376 if outputFormat == "" {
1377 outputFormat = GetCommandOptionWithDefault("log-correlation-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
1378 }
1379 orderBy := options.OrderBy
1380 if orderBy == "" {
1381 orderBy = GetCommandOptionWithDefault("log-correlation-list", "order", "ComponentName,Status")
1382 }
1383
1384 result := CommandResult{
1385 Format: format.Format(outputFormat),
1386 Filter: options.Filter,
1387 OrderBy: orderBy,
1388 OutputAs: toOutputType(options.OutputAs),
1389 NameLimit: options.NameLimit,
1390 Data: data,
1391 }
1392 GenerateOutput(&result)
1393 return nil
1394}