blob: 8663b70a378757d625b89e8dc7450362d53a39d2 [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"
David K. Bainbridge1d946442021-03-19 16:45:52 +000020 "crypto/tls"
Girish Kumarb03dcee2020-04-14 11:48:15 +000021 "encoding/json"
22 "errors"
23 "fmt"
24 "strings"
25
26 flags "github.com/jessevdk/go-flags"
27 "github.com/opencord/voltctl/pkg/format"
28 "github.com/opencord/voltctl/pkg/model"
kesavand8ec4fc02021-01-27 09:10:22 -050029 "github.com/opencord/voltha-lib-go/v4/pkg/config"
30 "github.com/opencord/voltha-lib-go/v4/pkg/db/kvstore"
31 "github.com/opencord/voltha-lib-go/v4/pkg/log"
David K. Bainbridge1d946442021-03-19 16:45:52 +000032 v3Client "go.etcd.io/etcd/clientv3"
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) {
David K. Bainbridge1d946442021-03-19 16:45:52 +0000253 var tlsConfig *tls.Config
David K. Bainbridge9189c632021-03-26 21:52:21 +0000254 if GlobalConfig.Current().Tls.UseTls {
255 tlsConfig = &tls.Config{InsecureSkipVerify: !GlobalConfig.Current().Tls.Verify}
David K. Bainbridge1d946442021-03-19 16:45:52 +0000256 }
257 logconfig := log.ConstructZapConfig(log.JSON, log.FatalLevel, log.Fields{})
258 client, err := kvstore.NewEtcdCustomClient(
259 ctx,
260 &v3Client.Config{
David K. Bainbridge9189c632021-03-26 21:52:21 +0000261 Endpoints: []string{GlobalConfig.Current().KvStore},
262 DialTimeout: GlobalConfig.Current().KvStoreConfig.Timeout,
David K. Bainbridge1d946442021-03-19 16:45:52 +0000263 LogConfig: &logconfig,
264 TLS: tlsConfig,
265 })
Girish Kumare48ec8a2020-08-18 12:28:51 +0000266 if err != nil {
267 return nil, nil, fmt.Errorf("Unable to create kvstore client %s", err)
268 }
269
David K. Bainbridge9189c632021-03-26 21:52:21 +0000270 cm := config.NewConfigManager(ctx, client, supportedKvStoreType, GlobalConfig.Current().KvStore, GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000271 return cm, func() { client.Close(ctx) }, nil
272}
273
Girish Kumarda415372020-04-21 15:37:13 +0000274// Method to get list of allowed Package Names for a given component. This list
275// is saved into etcd kvstore by each active component at startup as a json array
Girish Kumarb03dcee2020-04-14 11:48:15 +0000276func getPackageNames(componentName string) ([]string, error) {
277 list := []string{defaultPackageName}
278
279 ProcessGlobalOptions()
280
281 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000282 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000283 defer cancel()
284
Girish Kumare48ec8a2020-08-18 12:28:51 +0000285 cm, cleanupFunc, err := constructConfigManager(ctx)
286 if err != nil {
287 return nil, fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
288 }
289 defer cleanupFunc()
290
Girish Kumarb03dcee2020-04-14 11:48:15 +0000291 componentMetadata := cm.InitComponentConfig(componentName, config.ConfigTypeMetadata)
292
293 value, err := componentMetadata.Retrieve(ctx, logPackagesListKey)
294 if err != nil || value == "" {
295 return list, nil
296 }
297
298 var packageList []string
299 if err = json.Unmarshal([]byte(value), &packageList); err != nil {
300 return list, nil
301 }
302
303 list = append(list, packageList...)
304
305 return list, nil
306}
307
308func (ln *LevelName) Complete(match string) []flags.Completion {
309 levels := []string{"debug", "info", "warn", "error", "fatal"}
310
311 var list []flags.Completion
312 for _, name := range levels {
313 if strings.HasPrefix(name, strings.ToLower(match)) {
314 list = append(list, flags.Completion{Item: name})
315 }
316 }
317
318 return list
319}
320
321func (cpn *ComponentAndPackageName) Complete(match string) []flags.Completion {
322
323 componentNames := getVolthaComponentNames()
324
325 // Return nil if no component names could be fetched
326 if len(componentNames) == 0 {
327 return nil
328 }
329
330 // Check to see if #was specified, and if so, we know we have
331 // to split component name and package
332 parts := strings.SplitN(match, "#", 2)
333
334 var list []flags.Completion
335 for _, name := range componentNames {
336 if strings.HasPrefix(name, parts[0]) {
337 list = append(list, flags.Completion{Item: name})
338 }
339 }
340
341 // If the possible completions > 1 then we have to stop here
342 // as we can't suggest packages
343 if len(parts) == 1 || len(list) > 1 {
344 return list
345 }
346
347 // Ok, we have a valid, unambiguous component name and there
348 // is a package separator, so lets try to expand the package
349 // and in this case we will replace the list we have so
350 // far with the new list
351 cname := list[0].Item
352 base := []flags.Completion{{Item: fmt.Sprintf("%s#%s", cname, parts[1])}}
353 packages, err := getPackageNames(cname)
354 if err != nil || len(packages) == 0 {
355 return base
356 }
357
358 list = []flags.Completion{}
359 for _, pname := range packages {
360 if strings.HasPrefix(pname, parts[1]) {
361 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, pname)})
362 }
363 }
364
365 // if package part is present and still no match found based on prefix, user may be using
366 // short-hand notation for package name (last element of package path string e.g. kafka).
367 // Attempt prefix match against last element of package path (after the last / character)
368 if len(list) == 0 && len(parts[1]) >= 3 {
369 var mplist []string
370 for _, pname := range packages {
371 pnameparts := strings.Split(pname, "/")
372 if strings.HasPrefix(pnameparts[len(pnameparts)-1], parts[1]) {
373 mplist = append(mplist, pname)
374 }
375 }
376
377 // add to completion list if only a single match is found
378 if len(mplist) == 1 {
379 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, mplist[0])})
380 }
381 }
382
383 // If the component name was expanded but package name match was not found, list will still be empty
384 // We should return entry with just component name auto-completed and package name unchanged.
385 if len(list) == 0 && cname != parts[0] {
386 // Returning 2 entries with <completed-component-name>#<package-part> as prefix
387 // Just 1 entry will auto-complete the argument
388 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s1", cname, parts[1])})
389 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s2", cname, parts[1])})
390 }
391
392 return list
393}
394
395func (ccpn *ConfiguredComponentAndPackageName) Complete(match string) []flags.Completion {
396
397 var list []flags.Completion
398
399 ProcessGlobalOptions()
400
401 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000402 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000403 defer cancel()
404
405 cm, cleanupFunc, err := constructConfigManager(ctx)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000406 if err != nil {
407 return list
408 }
Girish Kumare48ec8a2020-08-18 12:28:51 +0000409 defer cleanupFunc()
Girish Kumarb03dcee2020-04-14 11:48:15 +0000410
411 var componentNames []string
412 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
413
414 // Return nil if no component names could be fetched
415 if err != nil || len(componentNames) == 0 {
416 return nil
417 }
418
419 // Check to see if #was specified, and if so, we know we have
420 // to split component name and package
421 parts := strings.SplitN(match, "#", 2)
422
423 for _, name := range componentNames {
424 if strings.HasPrefix(name, parts[0]) {
425 list = append(list, flags.Completion{Item: name})
426
427 // Handle scenario when one component is exact substring of other e.g. read-write-cor
428 // is substring of read-write-core (last e missing). Such a wrong component name
429 // can get configured during log level set operation
430 // In case of exact match of component name, use it if package part is present
431 if name == parts[0] && len(parts) == 2 {
432 list = []flags.Completion{{Item: name}}
433 break
434 }
435 }
436 }
437
438 // If the possible completions > 1 then we have to stop here
439 // as we can't suggest packages
440 if len(parts) == 1 || len(list) > 1 {
441 return list
442 }
443
444 // Ok, we have a valid, unambiguous component name and there
445 // is a package separator, so lets try to expand the package
446 // and in this case we will replace the list we have so
447 // far with the new list
448 cname := list[0].Item
449 base := []flags.Completion{{Item: fmt.Sprintf("%s#%s", cname, parts[1])}}
450
451 // Get list of packages configured for matching component name
452 logConfig := cm.InitComponentConfig(cname, config.ConfigTypeLogLevel)
453 logLevels, err1 := logConfig.RetrieveAll(ctx)
454 if err1 != nil || len(logLevels) == 0 {
455 return base
456 }
457
458 packages := make([]string, len(logLevels))
459 list = []flags.Completion{}
460 for pname := range logLevels {
461 pname = strings.ReplaceAll(pname, "#", "/")
462 packages = append(packages, pname)
463 if strings.HasPrefix(pname, parts[1]) {
464 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, pname)})
465 }
466 }
467
468 // if package part is present and still no match found based on prefix, user may be using
469 // short-hand notation for package name (last element of package path string e.g. kafka).
470 // Attempt prefix match against last element of package path (after the last / character)
471 if len(list) == 0 && len(parts[1]) >= 3 {
472 var mplist []string
473 for _, pname := range packages {
474 pnameparts := strings.Split(pname, "/")
475 if strings.HasPrefix(pnameparts[len(pnameparts)-1], parts[1]) {
476 mplist = append(mplist, pname)
477 }
478 }
479
480 // add to completion list if only a single match is found
481 if len(mplist) == 1 {
482 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, mplist[0])})
483 }
484 }
485
486 // If the component name was expanded but package name match was not found, list will still be empty
487 // We should return entry with just component name auto-completed and package name unchanged.
488 if len(list) == 0 && cname != parts[0] {
489 // Returning 2 entries with <completed-component-name>#<package-part> as prefix
490 // Just 1 entry will auto-complete the argument
491 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s1", cname, parts[1])})
492 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s2", cname, parts[1])})
493 }
494
495 return list
496}
497
498func (cn *ComponentName) Complete(match string) []flags.Completion {
499
500 componentNames := getVolthaComponentNames()
501
502 // Return nil if no component names could be fetched
503 if len(componentNames) == 0 {
504 return nil
505 }
506
507 var list []flags.Completion
508 for _, name := range componentNames {
509 if strings.HasPrefix(name, match) {
510 list = append(list, flags.Completion{Item: name})
511 }
512 }
513
514 return list
515}
516
517// Return nil if no component names could be fetched
518// processComponentListArgs stores the component name and package names given in command arguments to LogLevel
519// It checks the given argument has # key or not, if # is present then split the argument for # then stores first part as component name
520// and second part as package name
521func processComponentListArgs(Components []string) ([]model.LogLevel, error) {
522
523 var logLevelConfig []model.LogLevel
524
525 if len(Components) == 0 {
526 Components = append(Components, defaultComponentName)
527 }
528
529 for _, component := range Components {
530 logConfig := model.LogLevel{}
531 val := strings.SplitN(component, "#", 2)
532
533 if strings.Contains(val[0], "/") {
534 return nil, errors.New("the component name '" + val[0] + "' contains an invalid character '/'")
535 }
536
Girish Kumarda415372020-04-21 15:37:13 +0000537 // 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 +0000538 if len(val) > 1 {
539 if val[0] == defaultComponentName {
540 return nil, errors.New("global level doesn't support packageName")
541 }
Girish Kumarda415372020-04-21 15:37:13 +0000542
Girish Kumarb03dcee2020-04-14 11:48:15 +0000543 logConfig.ComponentName = val[0]
Girish Kumarda415372020-04-21 15:37:13 +0000544 logConfig.PackageName = val[1]
545 if logConfig.PackageName == "" {
546 logConfig.PackageName = defaultPackageName
547 }
Girish Kumarb03dcee2020-04-14 11:48:15 +0000548 } else {
549 logConfig.ComponentName = component
550 logConfig.PackageName = defaultPackageName
551 }
552 logLevelConfig = append(logLevelConfig, logConfig)
553 }
554 return logLevelConfig, nil
555}
556
557// This method set loglevel for components.
558// For example, using below command loglevel can be set for specific component with default packageName
559// voltctl loglevel set level <componentName>
560// For example, using below command loglevel can be set for specific component with specific packageName
561// voltctl loglevel set level <componentName#packageName>
562// For example, using below command loglevel can be set for more than one component for default package and other component for specific packageName
563// voltctl loglevel set level <componentName1#packageName> <componentName2>
564func (options *SetLogLevelOpts) Execute(args []string) error {
565 var (
566 logLevelConfig []model.LogLevel
567 err error
568 )
569 ProcessGlobalOptions()
570
571 log.SetAllLogLevel(log.FatalLevel)
572
573 if options.Args.Level != "" {
574 if _, err := log.StringToLogLevel(string(options.Args.Level)); err != nil {
575 return fmt.Errorf("Unknown log level '%s'. Allowed values are DEBUG, INFO, WARN, ERROR, FATAL", options.Args.Level)
576 }
577 }
578
579 logLevelConfig, err = processComponentListArgs(toStringArray(options.Args.Component))
580 if err != nil {
581 return fmt.Errorf(err.Error())
582 }
583
David K. Bainbridge9189c632021-03-26 21:52:21 +0000584 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000585 defer cancel()
586
Girish Kumare48ec8a2020-08-18 12:28:51 +0000587 cm, cleanupFunc, err := constructConfigManager(ctx)
588 if err != nil {
589 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
590 }
591 defer cleanupFunc()
592
593 var output []LogLevelOutput
594
Girish Kumarda415372020-04-21 15:37:13 +0000595 validComponents := getVolthaComponentNames()
596
Girish Kumarb03dcee2020-04-14 11:48:15 +0000597 for _, lConfig := range logLevelConfig {
598
599 logConfig := cm.InitComponentConfig(lConfig.ComponentName, config.ConfigTypeLogLevel)
Girish Kumarda415372020-04-21 15:37:13 +0000600 err := logConfig.Save(ctx, strings.ReplaceAll(lConfig.PackageName, "/", "#"), strings.ToUpper(string(options.Args.Level)))
601
Girish Kumarb03dcee2020-04-14 11:48:15 +0000602 if err != nil {
Girish Kumarda415372020-04-21 15:37:13 +0000603 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Failure", Error: err.Error()})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000604 } else {
Girish Kumarda415372020-04-21 15:37:13 +0000605 var outmsg string
606 cvalid := false
607 pvalid := false
608
609 // Validate if component and package name being set are correct. Add a * against the invalid value
610
611 // For global level, only default package is valid
612 if lConfig.ComponentName == defaultComponentName {
613 if lConfig.PackageName != defaultPackageName {
614 lConfig.PackageName = "*" + lConfig.PackageName
615 outmsg = "Only default package is valid for global"
616 }
617 } else {
618
619 for _, cname := range validComponents {
620 if lConfig.ComponentName == cname {
621 cvalid = true
622 break
623 }
624 }
625
626 // If component is valid, fetch and validate entered package name
627 if cvalid {
628 if validPackages, err := getPackageNames(lConfig.ComponentName); err == nil {
629 for _, pname := range validPackages {
630 if lConfig.PackageName == pname {
631 pvalid = true
632 break
633 }
634 }
635 }
636
637 if !pvalid {
638 lConfig.PackageName = "*" + lConfig.PackageName
639 outmsg = "Entered Package Name is not valid"
640 }
641 } else {
642
643 lConfig.ComponentName = "*" + lConfig.ComponentName
644 outmsg = "Entered Component Name is not Currently active in Voltha"
645 }
646 }
647
648 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Success", Error: outmsg})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000649 }
650
651 }
652
653 outputFormat := CharReplacer.Replace(options.Format)
654 if outputFormat == "" {
655 outputFormat = GetCommandOptionWithDefault("log-level-set", "format", DEFAULT_LOG_RESULT_FORMAT)
656 }
657 result := CommandResult{
658 Format: format.Format(outputFormat),
659 OutputAs: toOutputType(options.OutputAs),
660 NameLimit: options.NameLimit,
661 Data: output,
662 }
663
664 GenerateOutput(&result)
665 return nil
666}
667
668// This method list loglevel for components.
669// For example, using below command loglevel can be list for specific component
670// voltctl loglevel list <componentName>
671// For example, using below command loglevel can be list for all the components with all the packageName
672// voltctl loglevel list
673func (options *ListLogLevelsOpts) Execute(args []string) error {
674
675 var (
676 // Initialize to empty as opposed to nil so that -o json will
677 // display empty list and not null VOL-2742
678 data []model.LogLevel = []model.LogLevel{}
679 componentList []string
680 logLevelConfig map[string]string
681 err error
682 )
683 ProcessGlobalOptions()
684
685 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000686 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000687 defer cancel()
688
Girish Kumare48ec8a2020-08-18 12:28:51 +0000689 cm, cleanupFunc, err := constructConfigManager(ctx)
690 if err != nil {
691 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
692 }
693 defer cleanupFunc()
694
Girish Kumarb03dcee2020-04-14 11:48:15 +0000695 if len(options.Args.Component) == 0 {
696 componentList, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
697 if err != nil {
David K. Bainbridge9189c632021-03-26 21:52:21 +0000698 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 +0000699 }
700 } else {
701 componentList = toStringArray(options.Args.Component)
702 }
703
Girish Kumarda415372020-04-21 15:37:13 +0000704 validComponents := getVolthaComponentNames()
705
Girish Kumarb03dcee2020-04-14 11:48:15 +0000706 for _, componentName := range componentList {
707 logConfig := cm.InitComponentConfig(componentName, config.ConfigTypeLogLevel)
708
709 logLevelConfig, err = logConfig.RetrieveAll(ctx)
710 if err != nil {
711 return fmt.Errorf("Unable to retrieve loglevel configuration for component %s : %s", componentName, err)
712 }
713
714 for packageName, level := range logLevelConfig {
715 logLevel := model.LogLevel{}
716 if packageName == "" {
717 continue
718 }
719
Girish Kumarda415372020-04-21 15:37:13 +0000720 cvalid := false
721 pvalid := false
722 outPackageName := strings.ReplaceAll(packageName, "#", "/")
723 outComponentName := componentName
724
725 // Validate retrieved component and package names before printing. Add a * against the invalid value
726 if componentName == defaultComponentName {
727 if packageName != defaultPackageName {
728 outPackageName = "*" + outPackageName
729 }
730 } else {
731 for _, cname := range validComponents {
732 if componentName == cname {
733 cvalid = true
734 break
735 }
736 }
737
738 // For valid component, fetch and verify package name as well
739 if cvalid {
740 if validPackages, err := getPackageNames(componentName); err == nil {
741 for _, pname := range validPackages {
742 if outPackageName == pname {
743 pvalid = true
744 break
745 }
746 }
747 }
748
749 if !pvalid {
750 outPackageName = "*" + outPackageName
751 }
752 } else {
753
754 outComponentName = "*" + componentName
755 }
756 }
757
758 logLevel.PopulateFrom(outComponentName, outPackageName, level)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000759 data = append(data, logLevel)
760 }
761 }
762
763 outputFormat := CharReplacer.Replace(options.Format)
764 if outputFormat == "" {
765 outputFormat = GetCommandOptionWithDefault("log-level-list", "format", DEFAULT_LOG_LEVELS_FORMAT)
766 }
767 orderBy := options.OrderBy
768 if orderBy == "" {
769 orderBy = GetCommandOptionWithDefault("log-level-list", "order", "")
770 }
771
772 result := CommandResult{
773 Format: format.Format(outputFormat),
774 Filter: options.Filter,
775 OrderBy: orderBy,
776 OutputAs: toOutputType(options.OutputAs),
777 NameLimit: options.NameLimit,
778 Data: data,
779 }
780 GenerateOutput(&result)
781 return nil
782}
783
784// This method clear loglevel for components.
785// For example, using below command loglevel can be clear for specific component with default packageName
786// voltctl loglevel clear <componentName>
787// For example, using below command loglevel can be clear for specific component with specific packageName
788// voltctl loglevel clear <componentName#packageName>
789func (options *ClearLogLevelsOpts) Execute(args []string) error {
790
791 var (
792 logLevelConfig []model.LogLevel
793 err error
794 )
795 ProcessGlobalOptions()
796
797 log.SetAllLogLevel(log.FatalLevel)
798
799 logLevelConfig, err = processComponentListArgs(toStringArray(options.Args.Component))
800 if err != nil {
801 return fmt.Errorf("%s", err)
802 }
803
David K. Bainbridge9189c632021-03-26 21:52:21 +0000804 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000805 defer cancel()
806
Girish Kumare48ec8a2020-08-18 12:28:51 +0000807 cm, cleanupFunc, err := constructConfigManager(ctx)
808 if err != nil {
809 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
810 }
811 defer cleanupFunc()
812
813 var output []LogLevelOutput
814
Girish Kumarb03dcee2020-04-14 11:48:15 +0000815 for _, lConfig := range logLevelConfig {
816
817 if lConfig.ComponentName == defaultComponentName {
818 return fmt.Errorf("The global default loglevel cannot be cleared.")
819 }
820
821 logConfig := cm.InitComponentConfig(lConfig.ComponentName, config.ConfigTypeLogLevel)
822
Girish Kumarda415372020-04-21 15:37:13 +0000823 err := logConfig.Delete(ctx, strings.ReplaceAll(lConfig.PackageName, "/", "#"))
Girish Kumarb03dcee2020-04-14 11:48:15 +0000824 if err != nil {
Girish Kumarda415372020-04-21 15:37:13 +0000825 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Failure", Error: err.Error()})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000826 } else {
Girish Kumarda415372020-04-21 15:37:13 +0000827 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Success"})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000828 }
829 }
830
831 outputFormat := CharReplacer.Replace(options.Format)
832 if outputFormat == "" {
833 outputFormat = GetCommandOptionWithDefault("log-level-clear", "format", DEFAULT_LOG_RESULT_FORMAT)
834 }
835
836 result := CommandResult{
837 Format: format.Format(outputFormat),
838 OutputAs: toOutputType(options.OutputAs),
839 NameLimit: options.NameLimit,
840 Data: output,
841 }
842
843 GenerateOutput(&result)
844 return nil
845}
846
847// This method lists registered log packages for components.
848// For example, available log packages can be listed for specific component using below command
849// voltctl loglevel listpackage <componentName>
850// For example, available log packages can be listed for all the components using below command (omitting component name)
851// voltctl loglevel listpackage
852func (options *ListLogPackagesOpts) Execute(args []string) error {
853
854 var (
855 // Initialize to empty as opposed to nil so that -o json will
856 // display empty list and not null VOL-2742
857 data []model.LogLevel = []model.LogLevel{}
858 componentList []string
859 err error
860 )
861
862 ProcessGlobalOptions()
863
864 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000865 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000866 defer cancel()
867
Girish Kumare48ec8a2020-08-18 12:28:51 +0000868 cm, cleanupFunc, err := constructConfigManager(ctx)
869 if err != nil {
870 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
871 }
872 defer cleanupFunc()
873
Girish Kumarb03dcee2020-04-14 11:48:15 +0000874 if len(options.Args.Component) == 0 {
875 componentList, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
876 if err != nil {
877 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
878 }
879
880 // Include default global package as well when displaying packages for all components
881 logLevel := model.LogLevel{}
882 logLevel.PopulateFrom(defaultComponentName, defaultPackageName, "")
883 data = append(data, logLevel)
884 } else {
885 for _, name := range options.Args.Component {
886 componentList = append(componentList, string(name))
887 }
888 }
889
890 for _, componentName := range componentList {
891 componentMetadata := cm.InitComponentConfig(componentName, config.ConfigTypeMetadata)
892
893 value, err := componentMetadata.Retrieve(ctx, logPackagesListKey)
894 if err != nil || value == "" {
895 // Ignore any error in retrieval for log package list; some components may not store it
896 continue
897 }
898
899 var packageList []string
900 if err = json.Unmarshal([]byte(value), &packageList); err != nil {
901 continue
902 }
903
904 for _, packageName := range packageList {
905 logLevel := model.LogLevel{}
906 logLevel.PopulateFrom(componentName, packageName, "")
907 data = append(data, logLevel)
908 }
909 }
910
911 outputFormat := CharReplacer.Replace(options.Format)
912 if outputFormat == "" {
913 outputFormat = GetCommandOptionWithDefault("log-package-list", "format", DEFAULT_LOG_PACKAGES_FORMAT)
914 }
915 orderBy := options.OrderBy
916 if orderBy == "" {
917 orderBy = GetCommandOptionWithDefault("log-package-list", "order", "ComponentName,PackageName")
918 }
919
920 result := CommandResult{
921 Format: format.Format(outputFormat),
922 Filter: options.Filter,
923 OrderBy: orderBy,
924 OutputAs: toOutputType(options.OutputAs),
925 NameLimit: options.NameLimit,
926 Data: data,
927 }
928 GenerateOutput(&result)
929 return nil
930}
Girish Kumare48ec8a2020-08-18 12:28:51 +0000931
932// This method enables log trace publishing for components.
933// For example, using below command, trace publishing can be enabled for specific component
934// voltctl log tracing enable <componentName>
935// Omitting the component name will enable trace publishing for all the components, as shown in below command.
936// voltctl log tracing enable
937func (options *EnableLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +0000938 ProcessGlobalOptions()
939
940 log.SetAllLogLevel(log.FatalLevel)
941
David K. Bainbridge9189c632021-03-26 21:52:21 +0000942 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000943 defer cancel()
944
945 cm, cleanupFunc, err := constructConfigManager(ctx)
946 if err != nil {
947 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
948 }
949 defer cleanupFunc()
950
951 var output []LogLevelOutput
Neha Sharma94313672020-08-25 07:52:28 +0000952 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +0000953
954 if len(options.Args.Component) == 0 {
955 // Apply to all components if no specific component has been indicated
956 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
957 if err != nil {
958 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
959 }
960
961 } else {
962 for _, name := range options.Args.Component {
963 componentNames = append(componentNames, string(name))
964 }
965 }
966
967 validComponents := getVolthaComponentNames()
968
969 for _, component := range componentNames {
970
971 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
972
973 err := config.Save(ctx, logTracingStatusKey, "ENABLED")
974 if err != nil {
975 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
Neha Sharma94313672020-08-25 07:52:28 +0000976 continue
Girish Kumare48ec8a2020-08-18 12:28:51 +0000977 }
Neha Sharma94313672020-08-25 07:52:28 +0000978 outmsg := ""
979 cvalid := false
980 for _, cname := range validComponents {
981 if component == cname {
982 cvalid = true
983 break
984 }
985 }
986
987 // For invalid component, add * against its name to indicate possible mis-configuration
988 if !cvalid {
989 component = "*" + component
990 outmsg = "Entered Component Name is not Currently active in Voltha"
991 }
992 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
Girish Kumare48ec8a2020-08-18 12:28:51 +0000993 }
994
995 outputFormat := CharReplacer.Replace(options.Format)
996 if outputFormat == "" {
997 outputFormat = GetCommandOptionWithDefault("log-tracing-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
998 }
999
1000 result := CommandResult{
1001 Format: format.Format(outputFormat),
1002 OutputAs: toOutputType(options.OutputAs),
1003 NameLimit: options.NameLimit,
1004 Data: output,
1005 }
1006
1007 GenerateOutput(&result)
1008 return nil
1009}
1010
1011// This method disables log trace publishing for components.
1012// For example, using below command, trace publishing can be disabled for specific component
1013// voltctl log tracing disable <componentName>
1014// Omitting the component name will disable trace publishing for all the components, as shown in below command.
1015// voltctl log tracing disable
1016func (options *DisableLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +00001017 ProcessGlobalOptions()
1018
1019 log.SetAllLogLevel(log.FatalLevel)
1020
David K. Bainbridge9189c632021-03-26 21:52:21 +00001021 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +00001022 defer cancel()
1023
1024 cm, cleanupFunc, err := constructConfigManager(ctx)
1025 if err != nil {
1026 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1027 }
1028 defer cleanupFunc()
1029
1030 var output []LogLevelOutput
Neha Sharma94313672020-08-25 07:52:28 +00001031 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +00001032
1033 if len(options.Args.Component) == 0 {
1034 // Apply to all components if no specific component has been indicated
1035 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1036 if err != nil {
1037 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1038 }
1039
1040 } else {
1041 for _, name := range options.Args.Component {
1042 componentNames = append(componentNames, string(name))
1043 }
1044 }
1045
1046 validComponents := getVolthaComponentNames()
1047
1048 for _, component := range componentNames {
1049
1050 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1051
1052 err := config.Save(ctx, logTracingStatusKey, "DISABLED")
1053
1054 if err != nil {
1055 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
Neha Sharma94313672020-08-25 07:52:28 +00001056 continue
Girish Kumare48ec8a2020-08-18 12:28:51 +00001057 }
Neha Sharma94313672020-08-25 07:52:28 +00001058 outmsg := ""
1059 cvalid := false
1060 for _, cname := range validComponents {
1061 if component == cname {
1062 cvalid = true
1063 break
1064 }
1065 }
1066
1067 // For invalid component, add * against its name to indicate possible mis-configuration
1068 if !cvalid {
1069 component = "*" + component
1070 outmsg = "Entered Component Name is not Currently active in Voltha"
1071 }
1072
1073 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
Girish Kumare48ec8a2020-08-18 12:28:51 +00001074 }
1075
1076 outputFormat := CharReplacer.Replace(options.Format)
1077 if outputFormat == "" {
1078 outputFormat = GetCommandOptionWithDefault("log-tracing-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1079 }
1080
1081 result := CommandResult{
1082 Format: format.Format(outputFormat),
1083 OutputAs: toOutputType(options.OutputAs),
1084 NameLimit: options.NameLimit,
1085 Data: output,
1086 }
1087
1088 GenerateOutput(&result)
1089 return nil
1090}
1091
1092// This method lists current status of log trace publishing for components.
1093// For example, using below command, trace publishing can be queried for specific component
1094// voltctl log tracing list <componentName>
1095// Omitting the component name will list trace publishing for all the components, as shown in below command.
1096// voltctl log tracing list
1097func (options *ListLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +00001098 ProcessGlobalOptions()
1099
1100 log.SetAllLogLevel(log.FatalLevel)
1101
David K. Bainbridge9189c632021-03-26 21:52:21 +00001102 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +00001103 defer cancel()
1104
1105 cm, cleanupFunc, err := constructConfigManager(ctx)
1106 if err != nil {
1107 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1108 }
1109 defer cleanupFunc()
1110
Neha Sharma94313672020-08-25 07:52:28 +00001111 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +00001112 if len(options.Args.Component) == 0 {
1113 // Apply to all components if no specific component has been indicated
1114 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1115 if err != nil {
1116 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1117 }
1118
1119 } else {
1120 for _, name := range options.Args.Component {
1121 componentNames = append(componentNames, string(name))
1122 }
1123 }
1124
1125 validComponents := getVolthaComponentNames()
Neha Sharma94313672020-08-25 07:52:28 +00001126 data := []model.LogFeature{}
Girish Kumare48ec8a2020-08-18 12:28:51 +00001127
1128 for _, component := range componentNames {
1129
1130 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1131
1132 value, err := config.Retrieve(ctx, logTracingStatusKey)
1133 if err != nil || value == "" {
1134 // Ignore any error in retrieval; move to next component
1135 continue
1136 }
1137
1138 cvalid := false
1139 for _, cname := range validComponents {
1140 if component == cname {
1141 cvalid = true
1142 break
1143 }
1144 }
1145
1146 // For invalid component, add * against its name to indicate possible mis-configuration
1147 if !cvalid {
1148 component = "*" + component
1149 }
1150
1151 logTracingStatus := model.LogFeature{}
1152 logTracingStatus.PopulateFrom(component, value)
1153 data = append(data, logTracingStatus)
1154 }
1155
1156 outputFormat := CharReplacer.Replace(options.Format)
1157 if outputFormat == "" {
1158 outputFormat = GetCommandOptionWithDefault("log-tracing-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
1159 }
1160 orderBy := options.OrderBy
1161 if orderBy == "" {
1162 orderBy = GetCommandOptionWithDefault("log-tracing-list", "order", "ComponentName,Status")
1163 }
1164
1165 result := CommandResult{
1166 Format: format.Format(outputFormat),
1167 Filter: options.Filter,
1168 OrderBy: orderBy,
1169 OutputAs: toOutputType(options.OutputAs),
1170 NameLimit: options.NameLimit,
1171 Data: data,
1172 }
1173 GenerateOutput(&result)
1174 return nil
1175}
Neha Sharma94313672020-08-25 07:52:28 +00001176
1177// This method enables log correlation for components.
1178// For example, using below command, log correlation can be enabled for specific component
1179// voltctl log correlation enable <componentName>
1180// Omitting the component name will enable log correlation for all the components, as shown in below command.
1181// voltctl log correlation enable
1182func (options *EnableLogCorrelationOpts) Execute(args []string) error {
1183 ProcessGlobalOptions()
1184
1185 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001186 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001187 defer cancel()
1188
1189 cm, cleanupFunc, err := constructConfigManager(ctx)
1190 if err != nil {
1191 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1192 }
1193 defer cleanupFunc()
1194
1195 var output []LogLevelOutput
1196 var componentNames []string
1197
1198 if len(options.Args.Component) == 0 {
1199 // Apply to all components if no specific component has been indicated
1200 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1201 if err != nil {
1202 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1203 }
1204 } else {
1205 for _, name := range options.Args.Component {
1206 componentNames = append(componentNames, string(name))
1207 }
1208 }
1209
1210 validComponents := getVolthaComponentNames()
1211
1212 for _, component := range componentNames {
1213 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1214 err := config.Save(ctx, logCorrelationStatusKey, "ENABLED")
1215
1216 if err != nil {
1217 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
1218 continue
1219 }
1220
1221 outmsg := ""
1222 cvalid := false
1223 for _, cname := range validComponents {
1224 if component == cname {
1225 cvalid = true
1226 break
1227 }
1228 }
1229 // For invalid component, add * against its name to indicate possible mis-configuration
1230 if !cvalid {
1231 component = "*" + component
1232 outmsg = "Entered Component Name is not Currently active in Voltha"
1233 }
1234 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
1235 }
1236
1237 outputFormat := CharReplacer.Replace(options.Format)
1238 if outputFormat == "" {
1239 outputFormat = GetCommandOptionWithDefault("log-correlation-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1240 }
1241
1242 result := CommandResult{
1243 Format: format.Format(outputFormat),
1244 OutputAs: toOutputType(options.OutputAs),
1245 NameLimit: options.NameLimit,
1246 Data: output,
1247 }
1248 GenerateOutput(&result)
1249 return nil
1250}
1251
1252// This method disables log correlation for components.
1253// For example, using below command, log correlation can be disabled for specific component
1254// voltctl log correlation disable <componentName>
1255// Omitting the component name will disable log correlation for all the components, as shown in below command.
1256// voltctl log correlation disable
1257func (options *DisableLogCorrelationOpts) Execute(args []string) error {
1258 ProcessGlobalOptions()
1259
1260 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001261 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001262 defer cancel()
1263
1264 cm, cleanupFunc, err := constructConfigManager(ctx)
1265 if err != nil {
1266 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1267 }
1268 defer cleanupFunc()
1269
1270 var output []LogLevelOutput
1271 var componentNames []string
1272
1273 if len(options.Args.Component) == 0 {
1274 // Apply to all components if no specific component has been indicated
1275 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1276 if err != nil {
1277 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1278 }
1279 } else {
1280 for _, name := range options.Args.Component {
1281 componentNames = append(componentNames, string(name))
1282 }
1283 }
1284
1285 validComponents := getVolthaComponentNames()
1286
1287 for _, component := range componentNames {
1288
1289 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1290 err := config.Save(ctx, logCorrelationStatusKey, "DISABLED")
1291
1292 if err != nil {
1293 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
1294 continue
1295 }
1296 outmsg := ""
1297 cvalid := false
1298 for _, cname := range validComponents {
1299 if component == cname {
1300 cvalid = true
1301 break
1302 }
1303 }
1304 // For invalid component, add * against its name to indicate possible mis-configuration
1305 if !cvalid {
1306 component = "*" + component
1307 outmsg = "Entered Component Name is not Currently active in Voltha"
1308 }
1309 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
1310 }
1311
1312 outputFormat := CharReplacer.Replace(options.Format)
1313 if outputFormat == "" {
1314 outputFormat = GetCommandOptionWithDefault("log-correlation-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1315 }
1316
1317 result := CommandResult{
1318 Format: format.Format(outputFormat),
1319 OutputAs: toOutputType(options.OutputAs),
1320 NameLimit: options.NameLimit,
1321 Data: output,
1322 }
1323 GenerateOutput(&result)
1324 return nil
1325}
1326
1327// This method lists current status of log correlation for components.
1328// For example, using below command, log correlation can be queried for specific component
1329// voltctl log correlation list <componentName>
1330// Omitting the component name will list log correlation for all the components, as shown in below command.
1331// voltctl log correlation list
1332func (options *ListLogCorrelationOpts) Execute(args []string) error {
1333 ProcessGlobalOptions()
1334 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001335 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001336 defer cancel()
1337
1338 cm, cleanupFunc, err := constructConfigManager(ctx)
1339 if err != nil {
1340 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1341 }
1342 defer cleanupFunc()
1343
1344 var componentNames []string
1345 if len(options.Args.Component) == 0 {
1346 // Apply to all components if no specific component has been indicated
1347 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1348 if err != nil {
1349 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1350 }
1351 } else {
1352 for _, name := range options.Args.Component {
1353 componentNames = append(componentNames, string(name))
1354 }
1355 }
1356
1357 validComponents := getVolthaComponentNames()
1358 data := []model.LogFeature{}
1359
1360 for _, component := range componentNames {
1361 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1362 value, err := config.Retrieve(ctx, logCorrelationStatusKey)
1363 if err != nil || value == "" {
1364 // Ignore any error in retrieval; move to next component
1365 continue
1366 }
1367 cvalid := false
1368
1369 for _, cname := range validComponents {
1370 if component == cname {
1371 cvalid = true
1372 break
1373 }
1374 }
1375 // For invalid component, add * against its name to indicate possible mis-configuration
1376 if !cvalid {
1377 component = "*" + component
1378 }
1379 logCorrelationStatus := model.LogFeature{}
1380 logCorrelationStatus.PopulateFrom(component, value)
1381 data = append(data, logCorrelationStatus)
1382 }
1383
1384 outputFormat := CharReplacer.Replace(options.Format)
1385 if outputFormat == "" {
1386 outputFormat = GetCommandOptionWithDefault("log-correlation-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
1387 }
1388 orderBy := options.OrderBy
1389 if orderBy == "" {
1390 orderBy = GetCommandOptionWithDefault("log-correlation-list", "order", "ComponentName,Status")
1391 }
1392
1393 result := CommandResult{
1394 Format: format.Format(outputFormat),
1395 Filter: options.Filter,
1396 OrderBy: orderBy,
1397 OutputAs: toOutputType(options.OutputAs),
1398 NameLimit: options.NameLimit,
1399 Data: data,
1400 }
1401 GenerateOutput(&result)
1402 return nil
1403}