blob: 56c27cd52a1eff6e9076b37ae915a8181b30c0a4 [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"
serkantul3ed5bde2022-10-07 10:48:50 +030023 "os"
Girish Kumarb03dcee2020-04-14 11:48:15 +000024 "strings"
25
26 flags "github.com/jessevdk/go-flags"
27 "github.com/opencord/voltctl/pkg/format"
28 "github.com/opencord/voltctl/pkg/model"
David K. Bainbridgebd6b2882021-08-26 13:31:02 +000029 "github.com/opencord/voltha-lib-go/v7/pkg/config"
30 "github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
31 "github.com/opencord/voltha-lib-go/v7/pkg/log"
Girish Kumarb03dcee2020-04-14 11:48:15 +000032 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
33 "k8s.io/client-go/kubernetes"
34 "k8s.io/client-go/tools/clientcmd"
35)
36
37const (
Neha Sharma94313672020-08-25 07:52:28 +000038 defaultComponentName = "global"
39 defaultPackageName = "default"
40 logPackagesListKey = "log_package_list" // kvstore key containing list of allowed log packages
41 logTracingStatusKey = "trace_publish"
42 logCorrelationStatusKey = "log_correlation"
Girish Kumarb03dcee2020-04-14 11:48:15 +000043)
44
45// Custom Option representing <component-name>#<package-name> format (package is optional)
46// This is used by 'log level set' commands
47type ComponentAndPackageName string
48
49// Custom Option representing currently configured log configuration in <component-name>#<package-name> format (package is optional)
50// This is used by 'log level clear' commands
51type ConfiguredComponentAndPackageName string
52
53// Custom Option representing component-name. This is used by 'log level list' and 'log package list' commands
54type ComponentName string
55
56// Custom Option representing Log Level (one of debug, info, warn, error, fatal)
57type LevelName string
58
59// LogLevelOutput represents the output structure for the loglevel
60type LogLevelOutput struct {
61 ComponentName string
Girish Kumarda415372020-04-21 15:37:13 +000062 PackageName string
Girish Kumarb03dcee2020-04-14 11:48:15 +000063 Status string
64 Error string
65}
66
Girish Kumare48ec8a2020-08-18 12:28:51 +000067// SetLogLevelOpts represents the supported CLI arguments for the log level set command
Girish Kumarb03dcee2020-04-14 11:48:15 +000068type SetLogLevelOpts struct {
69 OutputOptions
70 Args struct {
71 Level LevelName
72 Component []ComponentAndPackageName
73 } `positional-args:"yes" required:"yes"`
74}
75
Girish Kumare48ec8a2020-08-18 12:28:51 +000076// ListLogLevelOpts represents the supported CLI arguments for the log level list command
Girish Kumarb03dcee2020-04-14 11:48:15 +000077type ListLogLevelsOpts struct {
78 ListOutputOptions
79 Args struct {
80 Component []ComponentName
81 } `positional-args:"yes" required:"yes"`
82}
83
Girish Kumare48ec8a2020-08-18 12:28:51 +000084// ClearLogLevelOpts represents the supported CLI arguments for the log level clear command
Girish Kumarb03dcee2020-04-14 11:48:15 +000085type ClearLogLevelsOpts struct {
86 OutputOptions
87 Args struct {
88 Component []ConfiguredComponentAndPackageName
89 } `positional-args:"yes" required:"yes"`
90}
91
Girish Kumare48ec8a2020-08-18 12:28:51 +000092// ListLogLevelOpts represents the supported CLI arguments for the log level list command
Girish Kumarb03dcee2020-04-14 11:48:15 +000093type ListLogPackagesOpts struct {
94 ListOutputOptions
95 Args struct {
96 Component []ComponentName
97 } `positional-args:"yes" required:"yes"`
98}
99
Girish Kumare48ec8a2020-08-18 12:28:51 +0000100// EnableLogTracingOpts represents the supported CLI arguments for the log tracing enable command
101type EnableLogTracingOpts struct {
102 OutputOptions
103 Args struct {
104 Component []ComponentName
105 } `positional-args:"yes" required:"yes"`
106}
107
108// DisableLogTracingOpts represents the supported CLI arguments for the log tracing disable command
109type DisableLogTracingOpts struct {
110 OutputOptions
111 Args struct {
112 Component []ComponentName
113 } `positional-args:"yes" required:"yes"`
114}
115
116// ListLogTracingOpts represents the supported CLI arguments for the log tracing list command
117type ListLogTracingOpts struct {
118 ListOutputOptions
119 Args struct {
120 Component []ComponentName
121 } `positional-args:"yes" required:"yes"`
122}
123
Neha Sharma94313672020-08-25 07:52:28 +0000124// EnableLogCorrelationOpts represent the supported CLI arguments for the log correlation enable command
125type EnableLogCorrelationOpts struct {
126 OutputOptions
127 Args struct {
128 Component []ComponentName
129 } `positional-args:"yes" required:"yes"`
130}
131
132// DisableLogCorrelationOpts represent the supported CLI arguments for the log correlation disable command
133type DisableLogCorrelationOpts struct {
134 OutputOptions
135 Args struct {
136 Component []ComponentName
137 } `positional-args:"yes" required:"yes"`
138}
139
140// ListLogCorrelationOpts represents the supported CLI arguments for the log correlation list command
141type ListLogCorrelationOpts struct {
142 ListOutputOptions
143 Args struct {
144 Component []ComponentName
145 } `positional-args:"yes" required:"yes"`
146}
147
Girish Kumarb03dcee2020-04-14 11:48:15 +0000148// LogPackageOpts represents the log package commands
149type LogPackageOpts struct {
150 ListLogPackages ListLogPackagesOpts `command:"list"`
151}
152
153// LogLevelOpts represents the log level commands
154type LogLevelOpts struct {
155 SetLogLevel SetLogLevelOpts `command:"set"`
156 ListLogLevels ListLogLevelsOpts `command:"list"`
157 ClearLogLevels ClearLogLevelsOpts `command:"clear"`
158}
159
Girish Kumare48ec8a2020-08-18 12:28:51 +0000160// LogTracingOpts represents the log tracing commands
161type LogTracingOpts struct {
162 EnableLogTracing EnableLogTracingOpts `command:"enable"`
163 DisableLogTracing DisableLogTracingOpts `command:"disable"`
164 ListLogTracing ListLogTracingOpts `command:"list"`
165}
166
Neha Sharma94313672020-08-25 07:52:28 +0000167// LogCorrelationOpts represents the log correlation commands
168type LogCorrelationOpts struct {
169 EnableLogCorrelation EnableLogCorrelationOpts `command:"enable"`
170 DisableLogCorrelation DisableLogCorrelationOpts `command:"disable"`
171 ListLogCorrelation ListLogCorrelationOpts `command:"list"`
172}
173
Girish Kumarb03dcee2020-04-14 11:48:15 +0000174// LogOpts represents the log commands
175type LogOpts struct {
Neha Sharma94313672020-08-25 07:52:28 +0000176 LogLevel LogLevelOpts `command:"level"`
177 LogPackage LogPackageOpts `command:"package"`
178 LogTracing LogTracingOpts `command:"tracing"`
179 LogCorrelation LogCorrelationOpts `command:"correlation"`
Girish Kumarb03dcee2020-04-14 11:48:15 +0000180}
181
182var logOpts = LogOpts{}
183
184const (
Girish Kumare48ec8a2020-08-18 12:28:51 +0000185 DEFAULT_LOG_LEVELS_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}\t{{.Level}}"
186 DEFAULT_LOG_PACKAGES_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}"
187 DEFAULT_LOG_FEATURE_STATUS_FORMAT = "table{{ .ComponentName }}\t{{.Status}}"
188 DEFAULT_LOG_RESULT_FORMAT = "table{{ .ComponentName }}\t{{.PackageName}}\t{{.Status}}\t{{.Error}}"
189 DEFAULT_LOG_FEATURE_RESULT_FORMAT = "table{{ .ComponentName }}\t{{.Status}}\t{{.Error}}"
Girish Kumarb03dcee2020-04-14 11:48:15 +0000190)
191
192func toStringArray(arg interface{}) []string {
193 var list []string
194 if cnl, ok := arg.([]ComponentName); ok {
195 for _, cn := range cnl {
196 list = append(list, string(cn))
197 }
198 } else if cpnl, ok := arg.([]ComponentAndPackageName); ok {
199 for _, cpn := range cpnl {
200 list = append(list, string(cpn))
201 }
202 } else if ccpnl, ok := arg.([]ConfiguredComponentAndPackageName); ok {
203 for _, ccpn := range ccpnl {
204 list = append(list, string(ccpn))
205 }
206 }
207
208 return list
209}
210
211// RegisterLogCommands is used to register log and its sub-commands e.g. level, package etc
212func RegisterLogCommands(parent *flags.Parser) {
Girish Kumare48ec8a2020-08-18 12:28:51 +0000213 _, 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 +0000214 if err != nil {
215 Error.Fatalf("Unable to register log commands with voltctl command parser: %s", err.Error())
216 }
217}
218
219// Common method to get list of VOLTHA components using k8s API. Used for validation and auto-complete
220// Just return a blank list in case of any error
221func getVolthaComponentNames() []string {
222 var componentList []string
223
224 // use the current context in kubeconfig
225 config, err := clientcmd.BuildConfigFromFlags("", GlobalOptions.K8sConfig)
226 if err != nil {
227 // Ignore any error
228 return componentList
229 }
230
231 // create the clientset
232 clientset, err := kubernetes.NewForConfig(config)
233 if err != nil {
234 return componentList
235 }
236
237 pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{
238 LabelSelector: "app.kubernetes.io/part-of=voltha",
239 })
240 if err != nil {
241 return componentList
242 }
243
244 for _, pod := range pods.Items {
245 componentList = append(componentList, pod.ObjectMeta.Labels["app.kubernetes.io/name"])
246 }
247
248 return componentList
249}
250
Girish Kumare48ec8a2020-08-18 12:28:51 +0000251func constructConfigManager(ctx context.Context) (*config.ConfigManager, func(), error) {
David K. Bainbridge1d946442021-03-19 16:45:52 +0000252 client, err := kvstore.NewEtcdCustomClient(
253 ctx,
David K. Bainbridge894f7ef2021-06-28 09:59:42 -0700254 GlobalConfig.Current().KvStore,
255 GlobalConfig.Current().KvStoreConfig.Timeout,
256 log.FatalLevel)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000257 if err != nil {
serkantul3ed5bde2022-10-07 10:48:50 +0300258 return nil, nil, fmt.Errorf("unable to create kvstore client %s", err)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000259 }
260
serkantul3ed5bde2022-10-07 10:48:50 +0300261 //voltha-helm-charts sets the KV_STORE_DATA_PREFIX env variable in the following format
262 //kv_store_data_prefix: 'service/{{ .Values.global.stack_name }}_voltha'
263 //set the KV_STORE_DATA_PREFIX using the current stack name to read correct configuration from KV.
264 if GlobalConfig.CurrentStack != "" {
265 os.Setenv("KV_STORE_DATAPATH_PREFIX", fmt.Sprintf("service/%s_voltha", GlobalConfig.CurrentStack))
266 }
David K. Bainbridge9189c632021-03-26 21:52:21 +0000267 cm := config.NewConfigManager(ctx, client, supportedKvStoreType, GlobalConfig.Current().KvStore, GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000268 return cm, func() { client.Close(ctx) }, nil
269}
270
Girish Kumarda415372020-04-21 15:37:13 +0000271// Method to get list of allowed Package Names for a given component. This list
272// is saved into etcd kvstore by each active component at startup as a json array
Girish Kumarb03dcee2020-04-14 11:48:15 +0000273func getPackageNames(componentName string) ([]string, error) {
274 list := []string{defaultPackageName}
275
276 ProcessGlobalOptions()
277
278 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000279 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000280 defer cancel()
281
Girish Kumare48ec8a2020-08-18 12:28:51 +0000282 cm, cleanupFunc, err := constructConfigManager(ctx)
283 if err != nil {
284 return nil, fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
285 }
286 defer cleanupFunc()
287
Girish Kumarb03dcee2020-04-14 11:48:15 +0000288 componentMetadata := cm.InitComponentConfig(componentName, config.ConfigTypeMetadata)
289
290 value, err := componentMetadata.Retrieve(ctx, logPackagesListKey)
291 if err != nil || value == "" {
292 return list, nil
293 }
294
295 var packageList []string
296 if err = json.Unmarshal([]byte(value), &packageList); err != nil {
297 return list, nil
298 }
299
300 list = append(list, packageList...)
301
302 return list, nil
303}
304
305func (ln *LevelName) Complete(match string) []flags.Completion {
306 levels := []string{"debug", "info", "warn", "error", "fatal"}
307
308 var list []flags.Completion
309 for _, name := range levels {
310 if strings.HasPrefix(name, strings.ToLower(match)) {
311 list = append(list, flags.Completion{Item: name})
312 }
313 }
314
315 return list
316}
317
318func (cpn *ComponentAndPackageName) Complete(match string) []flags.Completion {
319
320 componentNames := getVolthaComponentNames()
321
322 // Return nil if no component names could be fetched
323 if len(componentNames) == 0 {
324 return nil
325 }
326
327 // Check to see if #was specified, and if so, we know we have
328 // to split component name and package
329 parts := strings.SplitN(match, "#", 2)
330
331 var list []flags.Completion
332 for _, name := range componentNames {
333 if strings.HasPrefix(name, parts[0]) {
334 list = append(list, flags.Completion{Item: name})
335 }
336 }
337
338 // If the possible completions > 1 then we have to stop here
339 // as we can't suggest packages
340 if len(parts) == 1 || len(list) > 1 {
341 return list
342 }
343
344 // Ok, we have a valid, unambiguous component name and there
345 // is a package separator, so lets try to expand the package
346 // and in this case we will replace the list we have so
347 // far with the new list
348 cname := list[0].Item
349 base := []flags.Completion{{Item: fmt.Sprintf("%s#%s", cname, parts[1])}}
350 packages, err := getPackageNames(cname)
351 if err != nil || len(packages) == 0 {
352 return base
353 }
354
355 list = []flags.Completion{}
356 for _, pname := range packages {
357 if strings.HasPrefix(pname, parts[1]) {
358 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, pname)})
359 }
360 }
361
362 // if package part is present and still no match found based on prefix, user may be using
363 // short-hand notation for package name (last element of package path string e.g. kafka).
364 // Attempt prefix match against last element of package path (after the last / character)
365 if len(list) == 0 && len(parts[1]) >= 3 {
366 var mplist []string
367 for _, pname := range packages {
368 pnameparts := strings.Split(pname, "/")
369 if strings.HasPrefix(pnameparts[len(pnameparts)-1], parts[1]) {
370 mplist = append(mplist, pname)
371 }
372 }
373
374 // add to completion list if only a single match is found
375 if len(mplist) == 1 {
376 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, mplist[0])})
377 }
378 }
379
380 // If the component name was expanded but package name match was not found, list will still be empty
381 // We should return entry with just component name auto-completed and package name unchanged.
382 if len(list) == 0 && cname != parts[0] {
383 // Returning 2 entries with <completed-component-name>#<package-part> as prefix
384 // Just 1 entry will auto-complete the argument
385 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s1", cname, parts[1])})
386 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s2", cname, parts[1])})
387 }
388
389 return list
390}
391
392func (ccpn *ConfiguredComponentAndPackageName) Complete(match string) []flags.Completion {
393
394 var list []flags.Completion
395
396 ProcessGlobalOptions()
397
398 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000399 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000400 defer cancel()
401
402 cm, cleanupFunc, err := constructConfigManager(ctx)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000403 if err != nil {
404 return list
405 }
Girish Kumare48ec8a2020-08-18 12:28:51 +0000406 defer cleanupFunc()
Girish Kumarb03dcee2020-04-14 11:48:15 +0000407
408 var componentNames []string
409 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
410
411 // Return nil if no component names could be fetched
412 if err != nil || len(componentNames) == 0 {
413 return nil
414 }
415
416 // Check to see if #was specified, and if so, we know we have
417 // to split component name and package
418 parts := strings.SplitN(match, "#", 2)
419
420 for _, name := range componentNames {
421 if strings.HasPrefix(name, parts[0]) {
422 list = append(list, flags.Completion{Item: name})
423
424 // Handle scenario when one component is exact substring of other e.g. read-write-cor
425 // is substring of read-write-core (last e missing). Such a wrong component name
426 // can get configured during log level set operation
427 // In case of exact match of component name, use it if package part is present
428 if name == parts[0] && len(parts) == 2 {
429 list = []flags.Completion{{Item: name}}
430 break
431 }
432 }
433 }
434
435 // If the possible completions > 1 then we have to stop here
436 // as we can't suggest packages
437 if len(parts) == 1 || len(list) > 1 {
438 return list
439 }
440
441 // Ok, we have a valid, unambiguous component name and there
442 // is a package separator, so lets try to expand the package
443 // and in this case we will replace the list we have so
444 // far with the new list
445 cname := list[0].Item
446 base := []flags.Completion{{Item: fmt.Sprintf("%s#%s", cname, parts[1])}}
447
448 // Get list of packages configured for matching component name
449 logConfig := cm.InitComponentConfig(cname, config.ConfigTypeLogLevel)
450 logLevels, err1 := logConfig.RetrieveAll(ctx)
451 if err1 != nil || len(logLevels) == 0 {
452 return base
453 }
454
455 packages := make([]string, len(logLevels))
456 list = []flags.Completion{}
457 for pname := range logLevels {
458 pname = strings.ReplaceAll(pname, "#", "/")
459 packages = append(packages, pname)
460 if strings.HasPrefix(pname, parts[1]) {
461 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, pname)})
462 }
463 }
464
465 // if package part is present and still no match found based on prefix, user may be using
466 // short-hand notation for package name (last element of package path string e.g. kafka).
467 // Attempt prefix match against last element of package path (after the last / character)
468 if len(list) == 0 && len(parts[1]) >= 3 {
469 var mplist []string
470 for _, pname := range packages {
471 pnameparts := strings.Split(pname, "/")
472 if strings.HasPrefix(pnameparts[len(pnameparts)-1], parts[1]) {
473 mplist = append(mplist, pname)
474 }
475 }
476
477 // add to completion list if only a single match is found
478 if len(mplist) == 1 {
479 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s", cname, mplist[0])})
480 }
481 }
482
483 // If the component name was expanded but package name match was not found, list will still be empty
484 // We should return entry with just component name auto-completed and package name unchanged.
485 if len(list) == 0 && cname != parts[0] {
486 // Returning 2 entries with <completed-component-name>#<package-part> as prefix
487 // Just 1 entry will auto-complete the argument
488 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s1", cname, parts[1])})
489 list = append(list, flags.Completion{Item: fmt.Sprintf("%s#%s2", cname, parts[1])})
490 }
491
492 return list
493}
494
495func (cn *ComponentName) Complete(match string) []flags.Completion {
496
497 componentNames := getVolthaComponentNames()
498
499 // Return nil if no component names could be fetched
500 if len(componentNames) == 0 {
501 return nil
502 }
503
504 var list []flags.Completion
505 for _, name := range componentNames {
506 if strings.HasPrefix(name, match) {
507 list = append(list, flags.Completion{Item: name})
508 }
509 }
510
511 return list
512}
513
514// Return nil if no component names could be fetched
515// processComponentListArgs stores the component name and package names given in command arguments to LogLevel
516// It checks the given argument has # key or not, if # is present then split the argument for # then stores first part as component name
517// and second part as package name
518func processComponentListArgs(Components []string) ([]model.LogLevel, error) {
519
520 var logLevelConfig []model.LogLevel
521
522 if len(Components) == 0 {
523 Components = append(Components, defaultComponentName)
524 }
525
526 for _, component := range Components {
527 logConfig := model.LogLevel{}
528 val := strings.SplitN(component, "#", 2)
529
530 if strings.Contains(val[0], "/") {
531 return nil, errors.New("the component name '" + val[0] + "' contains an invalid character '/'")
532 }
533
Girish Kumarda415372020-04-21 15:37:13 +0000534 // 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 +0000535 if len(val) > 1 {
536 if val[0] == defaultComponentName {
537 return nil, errors.New("global level doesn't support packageName")
538 }
Girish Kumarda415372020-04-21 15:37:13 +0000539
Girish Kumarb03dcee2020-04-14 11:48:15 +0000540 logConfig.ComponentName = val[0]
Girish Kumarda415372020-04-21 15:37:13 +0000541 logConfig.PackageName = val[1]
542 if logConfig.PackageName == "" {
543 logConfig.PackageName = defaultPackageName
544 }
Girish Kumarb03dcee2020-04-14 11:48:15 +0000545 } else {
546 logConfig.ComponentName = component
547 logConfig.PackageName = defaultPackageName
548 }
549 logLevelConfig = append(logLevelConfig, logConfig)
550 }
551 return logLevelConfig, nil
552}
553
554// This method set loglevel for components.
555// For example, using below command loglevel can be set for specific component with default packageName
556// voltctl loglevel set level <componentName>
557// For example, using below command loglevel can be set for specific component with specific packageName
558// voltctl loglevel set level <componentName#packageName>
559// For example, using below command loglevel can be set for more than one component for default package and other component for specific packageName
560// voltctl loglevel set level <componentName1#packageName> <componentName2>
561func (options *SetLogLevelOpts) Execute(args []string) error {
562 var (
563 logLevelConfig []model.LogLevel
564 err error
565 )
566 ProcessGlobalOptions()
567
568 log.SetAllLogLevel(log.FatalLevel)
569
570 if options.Args.Level != "" {
571 if _, err := log.StringToLogLevel(string(options.Args.Level)); err != nil {
572 return fmt.Errorf("Unknown log level '%s'. Allowed values are DEBUG, INFO, WARN, ERROR, FATAL", options.Args.Level)
573 }
574 }
575
576 logLevelConfig, err = processComponentListArgs(toStringArray(options.Args.Component))
577 if err != nil {
578 return fmt.Errorf(err.Error())
579 }
580
David K. Bainbridge9189c632021-03-26 21:52:21 +0000581 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000582 defer cancel()
583
Girish Kumare48ec8a2020-08-18 12:28:51 +0000584 cm, cleanupFunc, err := constructConfigManager(ctx)
585 if err != nil {
586 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
587 }
588 defer cleanupFunc()
589
590 var output []LogLevelOutput
591
Girish Kumarda415372020-04-21 15:37:13 +0000592 validComponents := getVolthaComponentNames()
593
Girish Kumarb03dcee2020-04-14 11:48:15 +0000594 for _, lConfig := range logLevelConfig {
595
596 logConfig := cm.InitComponentConfig(lConfig.ComponentName, config.ConfigTypeLogLevel)
Girish Kumarda415372020-04-21 15:37:13 +0000597 err := logConfig.Save(ctx, strings.ReplaceAll(lConfig.PackageName, "/", "#"), strings.ToUpper(string(options.Args.Level)))
598
Girish Kumarb03dcee2020-04-14 11:48:15 +0000599 if err != nil {
Girish Kumarda415372020-04-21 15:37:13 +0000600 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Failure", Error: err.Error()})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000601 } else {
Girish Kumarda415372020-04-21 15:37:13 +0000602 var outmsg string
603 cvalid := false
604 pvalid := false
605
606 // Validate if component and package name being set are correct. Add a * against the invalid value
607
608 // For global level, only default package is valid
609 if lConfig.ComponentName == defaultComponentName {
610 if lConfig.PackageName != defaultPackageName {
611 lConfig.PackageName = "*" + lConfig.PackageName
612 outmsg = "Only default package is valid for global"
613 }
614 } else {
615
616 for _, cname := range validComponents {
617 if lConfig.ComponentName == cname {
618 cvalid = true
619 break
620 }
621 }
622
623 // If component is valid, fetch and validate entered package name
624 if cvalid {
625 if validPackages, err := getPackageNames(lConfig.ComponentName); err == nil {
626 for _, pname := range validPackages {
627 if lConfig.PackageName == pname {
628 pvalid = true
629 break
630 }
631 }
632 }
633
634 if !pvalid {
635 lConfig.PackageName = "*" + lConfig.PackageName
636 outmsg = "Entered Package Name is not valid"
637 }
638 } else {
639
640 lConfig.ComponentName = "*" + lConfig.ComponentName
641 outmsg = "Entered Component Name is not Currently active in Voltha"
642 }
643 }
644
645 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Success", Error: outmsg})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000646 }
647
648 }
649
650 outputFormat := CharReplacer.Replace(options.Format)
651 if outputFormat == "" {
652 outputFormat = GetCommandOptionWithDefault("log-level-set", "format", DEFAULT_LOG_RESULT_FORMAT)
653 }
654 result := CommandResult{
655 Format: format.Format(outputFormat),
656 OutputAs: toOutputType(options.OutputAs),
657 NameLimit: options.NameLimit,
658 Data: output,
659 }
660
661 GenerateOutput(&result)
662 return nil
663}
664
665// This method list loglevel for components.
666// For example, using below command loglevel can be list for specific component
667// voltctl loglevel list <componentName>
668// For example, using below command loglevel can be list for all the components with all the packageName
669// voltctl loglevel list
670func (options *ListLogLevelsOpts) Execute(args []string) error {
671
672 var (
673 // Initialize to empty as opposed to nil so that -o json will
674 // display empty list and not null VOL-2742
675 data []model.LogLevel = []model.LogLevel{}
676 componentList []string
677 logLevelConfig map[string]string
678 err error
679 )
680 ProcessGlobalOptions()
681
682 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000683 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000684 defer cancel()
685
Girish Kumare48ec8a2020-08-18 12:28:51 +0000686 cm, cleanupFunc, err := constructConfigManager(ctx)
687 if err != nil {
688 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
689 }
690 defer cleanupFunc()
691
Girish Kumarb03dcee2020-04-14 11:48:15 +0000692 if len(options.Args.Component) == 0 {
693 componentList, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
694 if err != nil {
David K. Bainbridge9189c632021-03-26 21:52:21 +0000695 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 +0000696 }
697 } else {
698 componentList = toStringArray(options.Args.Component)
699 }
700
Girish Kumarda415372020-04-21 15:37:13 +0000701 validComponents := getVolthaComponentNames()
702
Girish Kumarb03dcee2020-04-14 11:48:15 +0000703 for _, componentName := range componentList {
704 logConfig := cm.InitComponentConfig(componentName, config.ConfigTypeLogLevel)
705
706 logLevelConfig, err = logConfig.RetrieveAll(ctx)
707 if err != nil {
708 return fmt.Errorf("Unable to retrieve loglevel configuration for component %s : %s", componentName, err)
709 }
710
711 for packageName, level := range logLevelConfig {
712 logLevel := model.LogLevel{}
713 if packageName == "" {
714 continue
715 }
716
Girish Kumarda415372020-04-21 15:37:13 +0000717 cvalid := false
718 pvalid := false
719 outPackageName := strings.ReplaceAll(packageName, "#", "/")
720 outComponentName := componentName
721
722 // Validate retrieved component and package names before printing. Add a * against the invalid value
723 if componentName == defaultComponentName {
724 if packageName != defaultPackageName {
725 outPackageName = "*" + outPackageName
726 }
727 } else {
728 for _, cname := range validComponents {
729 if componentName == cname {
730 cvalid = true
731 break
732 }
733 }
734
735 // For valid component, fetch and verify package name as well
736 if cvalid {
737 if validPackages, err := getPackageNames(componentName); err == nil {
738 for _, pname := range validPackages {
739 if outPackageName == pname {
740 pvalid = true
741 break
742 }
743 }
744 }
745
746 if !pvalid {
747 outPackageName = "*" + outPackageName
748 }
749 } else {
750
751 outComponentName = "*" + componentName
752 }
753 }
754
755 logLevel.PopulateFrom(outComponentName, outPackageName, level)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000756 data = append(data, logLevel)
757 }
758 }
759
760 outputFormat := CharReplacer.Replace(options.Format)
761 if outputFormat == "" {
762 outputFormat = GetCommandOptionWithDefault("log-level-list", "format", DEFAULT_LOG_LEVELS_FORMAT)
763 }
764 orderBy := options.OrderBy
765 if orderBy == "" {
766 orderBy = GetCommandOptionWithDefault("log-level-list", "order", "")
767 }
768
769 result := CommandResult{
770 Format: format.Format(outputFormat),
771 Filter: options.Filter,
772 OrderBy: orderBy,
773 OutputAs: toOutputType(options.OutputAs),
774 NameLimit: options.NameLimit,
775 Data: data,
776 }
777 GenerateOutput(&result)
778 return nil
779}
780
781// This method clear loglevel for components.
782// For example, using below command loglevel can be clear for specific component with default packageName
783// voltctl loglevel clear <componentName>
784// For example, using below command loglevel can be clear for specific component with specific packageName
785// voltctl loglevel clear <componentName#packageName>
786func (options *ClearLogLevelsOpts) Execute(args []string) error {
787
788 var (
789 logLevelConfig []model.LogLevel
790 err error
791 )
792 ProcessGlobalOptions()
793
794 log.SetAllLogLevel(log.FatalLevel)
795
796 logLevelConfig, err = processComponentListArgs(toStringArray(options.Args.Component))
797 if err != nil {
798 return fmt.Errorf("%s", err)
799 }
800
David K. Bainbridge9189c632021-03-26 21:52:21 +0000801 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000802 defer cancel()
803
Girish Kumare48ec8a2020-08-18 12:28:51 +0000804 cm, cleanupFunc, err := constructConfigManager(ctx)
805 if err != nil {
806 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
807 }
808 defer cleanupFunc()
809
810 var output []LogLevelOutput
811
Girish Kumarb03dcee2020-04-14 11:48:15 +0000812 for _, lConfig := range logLevelConfig {
813
814 if lConfig.ComponentName == defaultComponentName {
815 return fmt.Errorf("The global default loglevel cannot be cleared.")
816 }
817
818 logConfig := cm.InitComponentConfig(lConfig.ComponentName, config.ConfigTypeLogLevel)
819
Girish Kumarda415372020-04-21 15:37:13 +0000820 err := logConfig.Delete(ctx, strings.ReplaceAll(lConfig.PackageName, "/", "#"))
Girish Kumarb03dcee2020-04-14 11:48:15 +0000821 if err != nil {
Girish Kumarda415372020-04-21 15:37:13 +0000822 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Failure", Error: err.Error()})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000823 } else {
Girish Kumarda415372020-04-21 15:37:13 +0000824 output = append(output, LogLevelOutput{ComponentName: lConfig.ComponentName, PackageName: lConfig.PackageName, Status: "Success"})
Girish Kumarb03dcee2020-04-14 11:48:15 +0000825 }
826 }
827
828 outputFormat := CharReplacer.Replace(options.Format)
829 if outputFormat == "" {
830 outputFormat = GetCommandOptionWithDefault("log-level-clear", "format", DEFAULT_LOG_RESULT_FORMAT)
831 }
832
833 result := CommandResult{
834 Format: format.Format(outputFormat),
835 OutputAs: toOutputType(options.OutputAs),
836 NameLimit: options.NameLimit,
837 Data: output,
838 }
839
840 GenerateOutput(&result)
841 return nil
842}
843
844// This method lists registered log packages for components.
845// For example, available log packages can be listed for specific component using below command
846// voltctl loglevel listpackage <componentName>
847// For example, available log packages can be listed for all the components using below command (omitting component name)
848// voltctl loglevel listpackage
849func (options *ListLogPackagesOpts) Execute(args []string) error {
850
851 var (
852 // Initialize to empty as opposed to nil so that -o json will
853 // display empty list and not null VOL-2742
854 data []model.LogLevel = []model.LogLevel{}
855 componentList []string
856 err error
857 )
858
859 ProcessGlobalOptions()
860
861 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +0000862 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumarb03dcee2020-04-14 11:48:15 +0000863 defer cancel()
864
Girish Kumare48ec8a2020-08-18 12:28:51 +0000865 cm, cleanupFunc, err := constructConfigManager(ctx)
866 if err != nil {
867 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
868 }
869 defer cleanupFunc()
870
Girish Kumarb03dcee2020-04-14 11:48:15 +0000871 if len(options.Args.Component) == 0 {
872 componentList, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogLevel)
873 if err != nil {
874 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
875 }
876
877 // Include default global package as well when displaying packages for all components
878 logLevel := model.LogLevel{}
879 logLevel.PopulateFrom(defaultComponentName, defaultPackageName, "")
880 data = append(data, logLevel)
881 } else {
882 for _, name := range options.Args.Component {
883 componentList = append(componentList, string(name))
884 }
885 }
886
887 for _, componentName := range componentList {
888 componentMetadata := cm.InitComponentConfig(componentName, config.ConfigTypeMetadata)
889
890 value, err := componentMetadata.Retrieve(ctx, logPackagesListKey)
891 if err != nil || value == "" {
892 // Ignore any error in retrieval for log package list; some components may not store it
893 continue
894 }
895
896 var packageList []string
897 if err = json.Unmarshal([]byte(value), &packageList); err != nil {
898 continue
899 }
900
901 for _, packageName := range packageList {
902 logLevel := model.LogLevel{}
903 logLevel.PopulateFrom(componentName, packageName, "")
904 data = append(data, logLevel)
905 }
906 }
907
908 outputFormat := CharReplacer.Replace(options.Format)
909 if outputFormat == "" {
910 outputFormat = GetCommandOptionWithDefault("log-package-list", "format", DEFAULT_LOG_PACKAGES_FORMAT)
911 }
912 orderBy := options.OrderBy
913 if orderBy == "" {
914 orderBy = GetCommandOptionWithDefault("log-package-list", "order", "ComponentName,PackageName")
915 }
916
917 result := CommandResult{
918 Format: format.Format(outputFormat),
919 Filter: options.Filter,
920 OrderBy: orderBy,
921 OutputAs: toOutputType(options.OutputAs),
922 NameLimit: options.NameLimit,
923 Data: data,
924 }
925 GenerateOutput(&result)
926 return nil
927}
Girish Kumare48ec8a2020-08-18 12:28:51 +0000928
929// This method enables log trace publishing for components.
930// For example, using below command, trace publishing can be enabled for specific component
931// voltctl log tracing enable <componentName>
932// Omitting the component name will enable trace publishing for all the components, as shown in below command.
933// voltctl log tracing enable
934func (options *EnableLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +0000935 ProcessGlobalOptions()
936
937 log.SetAllLogLevel(log.FatalLevel)
938
David K. Bainbridge9189c632021-03-26 21:52:21 +0000939 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +0000940 defer cancel()
941
942 cm, cleanupFunc, err := constructConfigManager(ctx)
943 if err != nil {
944 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
945 }
946 defer cleanupFunc()
947
948 var output []LogLevelOutput
Neha Sharma94313672020-08-25 07:52:28 +0000949 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +0000950
951 if len(options.Args.Component) == 0 {
952 // Apply to all components if no specific component has been indicated
953 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
954 if err != nil {
955 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
956 }
957
958 } else {
959 for _, name := range options.Args.Component {
960 componentNames = append(componentNames, string(name))
961 }
962 }
963
964 validComponents := getVolthaComponentNames()
965
966 for _, component := range componentNames {
967
968 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
969
970 err := config.Save(ctx, logTracingStatusKey, "ENABLED")
971 if err != nil {
972 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
Neha Sharma94313672020-08-25 07:52:28 +0000973 continue
Girish Kumare48ec8a2020-08-18 12:28:51 +0000974 }
Neha Sharma94313672020-08-25 07:52:28 +0000975 outmsg := ""
976 cvalid := false
977 for _, cname := range validComponents {
978 if component == cname {
979 cvalid = true
980 break
981 }
982 }
983
984 // For invalid component, add * against its name to indicate possible mis-configuration
985 if !cvalid {
986 component = "*" + component
987 outmsg = "Entered Component Name is not Currently active in Voltha"
988 }
989 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
Girish Kumare48ec8a2020-08-18 12:28:51 +0000990 }
991
992 outputFormat := CharReplacer.Replace(options.Format)
993 if outputFormat == "" {
994 outputFormat = GetCommandOptionWithDefault("log-tracing-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
995 }
996
997 result := CommandResult{
998 Format: format.Format(outputFormat),
999 OutputAs: toOutputType(options.OutputAs),
1000 NameLimit: options.NameLimit,
1001 Data: output,
1002 }
1003
1004 GenerateOutput(&result)
1005 return nil
1006}
1007
1008// This method disables log trace publishing for components.
1009// For example, using below command, trace publishing can be disabled for specific component
1010// voltctl log tracing disable <componentName>
1011// Omitting the component name will disable trace publishing for all the components, as shown in below command.
1012// voltctl log tracing disable
1013func (options *DisableLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +00001014 ProcessGlobalOptions()
1015
1016 log.SetAllLogLevel(log.FatalLevel)
1017
David K. Bainbridge9189c632021-03-26 21:52:21 +00001018 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +00001019 defer cancel()
1020
1021 cm, cleanupFunc, err := constructConfigManager(ctx)
1022 if err != nil {
1023 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1024 }
1025 defer cleanupFunc()
1026
1027 var output []LogLevelOutput
Neha Sharma94313672020-08-25 07:52:28 +00001028 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +00001029
1030 if len(options.Args.Component) == 0 {
1031 // Apply to all components if no specific component has been indicated
1032 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1033 if err != nil {
1034 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1035 }
1036
1037 } else {
1038 for _, name := range options.Args.Component {
1039 componentNames = append(componentNames, string(name))
1040 }
1041 }
1042
1043 validComponents := getVolthaComponentNames()
1044
1045 for _, component := range componentNames {
1046
1047 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1048
1049 err := config.Save(ctx, logTracingStatusKey, "DISABLED")
1050
1051 if err != nil {
1052 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
Neha Sharma94313672020-08-25 07:52:28 +00001053 continue
Girish Kumare48ec8a2020-08-18 12:28:51 +00001054 }
Neha Sharma94313672020-08-25 07:52:28 +00001055 outmsg := ""
1056 cvalid := false
1057 for _, cname := range validComponents {
1058 if component == cname {
1059 cvalid = true
1060 break
1061 }
1062 }
1063
1064 // For invalid component, add * against its name to indicate possible mis-configuration
1065 if !cvalid {
1066 component = "*" + component
1067 outmsg = "Entered Component Name is not Currently active in Voltha"
1068 }
1069
1070 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
Girish Kumare48ec8a2020-08-18 12:28:51 +00001071 }
1072
1073 outputFormat := CharReplacer.Replace(options.Format)
1074 if outputFormat == "" {
1075 outputFormat = GetCommandOptionWithDefault("log-tracing-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1076 }
1077
1078 result := CommandResult{
1079 Format: format.Format(outputFormat),
1080 OutputAs: toOutputType(options.OutputAs),
1081 NameLimit: options.NameLimit,
1082 Data: output,
1083 }
1084
1085 GenerateOutput(&result)
1086 return nil
1087}
1088
1089// This method lists current status of log trace publishing for components.
1090// For example, using below command, trace publishing can be queried for specific component
1091// voltctl log tracing list <componentName>
1092// Omitting the component name will list trace publishing for all the components, as shown in below command.
1093// voltctl log tracing list
1094func (options *ListLogTracingOpts) Execute(args []string) error {
Girish Kumare48ec8a2020-08-18 12:28:51 +00001095 ProcessGlobalOptions()
1096
1097 log.SetAllLogLevel(log.FatalLevel)
1098
David K. Bainbridge9189c632021-03-26 21:52:21 +00001099 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Girish Kumare48ec8a2020-08-18 12:28:51 +00001100 defer cancel()
1101
1102 cm, cleanupFunc, err := constructConfigManager(ctx)
1103 if err != nil {
1104 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1105 }
1106 defer cleanupFunc()
1107
Neha Sharma94313672020-08-25 07:52:28 +00001108 var componentNames []string
Girish Kumare48ec8a2020-08-18 12:28:51 +00001109 if len(options.Args.Component) == 0 {
1110 // Apply to all components if no specific component has been indicated
1111 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1112 if err != nil {
1113 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1114 }
1115
1116 } else {
1117 for _, name := range options.Args.Component {
1118 componentNames = append(componentNames, string(name))
1119 }
1120 }
1121
1122 validComponents := getVolthaComponentNames()
Neha Sharma94313672020-08-25 07:52:28 +00001123 data := []model.LogFeature{}
Girish Kumare48ec8a2020-08-18 12:28:51 +00001124
1125 for _, component := range componentNames {
1126
1127 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1128
1129 value, err := config.Retrieve(ctx, logTracingStatusKey)
1130 if err != nil || value == "" {
1131 // Ignore any error in retrieval; move to next component
1132 continue
1133 }
1134
1135 cvalid := false
1136 for _, cname := range validComponents {
1137 if component == cname {
1138 cvalid = true
1139 break
1140 }
1141 }
1142
1143 // For invalid component, add * against its name to indicate possible mis-configuration
1144 if !cvalid {
1145 component = "*" + component
1146 }
1147
1148 logTracingStatus := model.LogFeature{}
1149 logTracingStatus.PopulateFrom(component, value)
1150 data = append(data, logTracingStatus)
1151 }
1152
1153 outputFormat := CharReplacer.Replace(options.Format)
1154 if outputFormat == "" {
1155 outputFormat = GetCommandOptionWithDefault("log-tracing-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
1156 }
1157 orderBy := options.OrderBy
1158 if orderBy == "" {
1159 orderBy = GetCommandOptionWithDefault("log-tracing-list", "order", "ComponentName,Status")
1160 }
1161
1162 result := CommandResult{
1163 Format: format.Format(outputFormat),
1164 Filter: options.Filter,
1165 OrderBy: orderBy,
1166 OutputAs: toOutputType(options.OutputAs),
1167 NameLimit: options.NameLimit,
1168 Data: data,
1169 }
1170 GenerateOutput(&result)
1171 return nil
1172}
Neha Sharma94313672020-08-25 07:52:28 +00001173
1174// This method enables log correlation for components.
1175// For example, using below command, log correlation can be enabled for specific component
1176// voltctl log correlation enable <componentName>
1177// Omitting the component name will enable log correlation for all the components, as shown in below command.
1178// voltctl log correlation enable
1179func (options *EnableLogCorrelationOpts) Execute(args []string) error {
1180 ProcessGlobalOptions()
1181
1182 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001183 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001184 defer cancel()
1185
1186 cm, cleanupFunc, err := constructConfigManager(ctx)
1187 if err != nil {
1188 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1189 }
1190 defer cleanupFunc()
1191
1192 var output []LogLevelOutput
1193 var componentNames []string
1194
1195 if len(options.Args.Component) == 0 {
1196 // Apply to all components if no specific component has been indicated
1197 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1198 if err != nil {
1199 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1200 }
1201 } else {
1202 for _, name := range options.Args.Component {
1203 componentNames = append(componentNames, string(name))
1204 }
1205 }
1206
1207 validComponents := getVolthaComponentNames()
1208
1209 for _, component := range componentNames {
1210 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1211 err := config.Save(ctx, logCorrelationStatusKey, "ENABLED")
1212
1213 if err != nil {
1214 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
1215 continue
1216 }
1217
1218 outmsg := ""
1219 cvalid := false
1220 for _, cname := range validComponents {
1221 if component == cname {
1222 cvalid = true
1223 break
1224 }
1225 }
1226 // For invalid component, add * against its name to indicate possible mis-configuration
1227 if !cvalid {
1228 component = "*" + component
1229 outmsg = "Entered Component Name is not Currently active in Voltha"
1230 }
1231 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
1232 }
1233
1234 outputFormat := CharReplacer.Replace(options.Format)
1235 if outputFormat == "" {
1236 outputFormat = GetCommandOptionWithDefault("log-correlation-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1237 }
1238
1239 result := CommandResult{
1240 Format: format.Format(outputFormat),
1241 OutputAs: toOutputType(options.OutputAs),
1242 NameLimit: options.NameLimit,
1243 Data: output,
1244 }
1245 GenerateOutput(&result)
1246 return nil
1247}
1248
1249// This method disables log correlation for components.
1250// For example, using below command, log correlation can be disabled for specific component
1251// voltctl log correlation disable <componentName>
1252// Omitting the component name will disable log correlation for all the components, as shown in below command.
1253// voltctl log correlation disable
1254func (options *DisableLogCorrelationOpts) Execute(args []string) error {
1255 ProcessGlobalOptions()
1256
1257 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001258 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001259 defer cancel()
1260
1261 cm, cleanupFunc, err := constructConfigManager(ctx)
1262 if err != nil {
1263 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1264 }
1265 defer cleanupFunc()
1266
1267 var output []LogLevelOutput
1268 var componentNames []string
1269
1270 if len(options.Args.Component) == 0 {
1271 // Apply to all components if no specific component has been indicated
1272 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1273 if err != nil {
1274 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1275 }
1276 } else {
1277 for _, name := range options.Args.Component {
1278 componentNames = append(componentNames, string(name))
1279 }
1280 }
1281
1282 validComponents := getVolthaComponentNames()
1283
1284 for _, component := range componentNames {
1285
1286 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1287 err := config.Save(ctx, logCorrelationStatusKey, "DISABLED")
1288
1289 if err != nil {
1290 output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
1291 continue
1292 }
1293 outmsg := ""
1294 cvalid := false
1295 for _, cname := range validComponents {
1296 if component == cname {
1297 cvalid = true
1298 break
1299 }
1300 }
1301 // For invalid component, add * against its name to indicate possible mis-configuration
1302 if !cvalid {
1303 component = "*" + component
1304 outmsg = "Entered Component Name is not Currently active in Voltha"
1305 }
1306 output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
1307 }
1308
1309 outputFormat := CharReplacer.Replace(options.Format)
1310 if outputFormat == "" {
1311 outputFormat = GetCommandOptionWithDefault("log-correlation-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
1312 }
1313
1314 result := CommandResult{
1315 Format: format.Format(outputFormat),
1316 OutputAs: toOutputType(options.OutputAs),
1317 NameLimit: options.NameLimit,
1318 Data: output,
1319 }
1320 GenerateOutput(&result)
1321 return nil
1322}
1323
1324// This method lists current status of log correlation for components.
1325// For example, using below command, log correlation can be queried for specific component
1326// voltctl log correlation list <componentName>
1327// Omitting the component name will list log correlation for all the components, as shown in below command.
1328// voltctl log correlation list
1329func (options *ListLogCorrelationOpts) Execute(args []string) error {
1330 ProcessGlobalOptions()
1331 log.SetAllLogLevel(log.FatalLevel)
David K. Bainbridge9189c632021-03-26 21:52:21 +00001332 ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Current().KvStoreConfig.Timeout)
Neha Sharma94313672020-08-25 07:52:28 +00001333 defer cancel()
1334
1335 cm, cleanupFunc, err := constructConfigManager(ctx)
1336 if err != nil {
1337 return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
1338 }
1339 defer cleanupFunc()
1340
1341 var componentNames []string
1342 if len(options.Args.Component) == 0 {
1343 // Apply to all components if no specific component has been indicated
1344 componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
1345 if err != nil {
1346 return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
1347 }
1348 } else {
1349 for _, name := range options.Args.Component {
1350 componentNames = append(componentNames, string(name))
1351 }
1352 }
1353
1354 validComponents := getVolthaComponentNames()
1355 data := []model.LogFeature{}
1356
1357 for _, component := range componentNames {
1358 config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
1359 value, err := config.Retrieve(ctx, logCorrelationStatusKey)
1360 if err != nil || value == "" {
1361 // Ignore any error in retrieval; move to next component
1362 continue
1363 }
1364 cvalid := false
1365
1366 for _, cname := range validComponents {
1367 if component == cname {
1368 cvalid = true
1369 break
1370 }
1371 }
1372 // For invalid component, add * against its name to indicate possible mis-configuration
1373 if !cvalid {
1374 component = "*" + component
1375 }
1376 logCorrelationStatus := model.LogFeature{}
1377 logCorrelationStatus.PopulateFrom(component, value)
1378 data = append(data, logCorrelationStatus)
1379 }
1380
1381 outputFormat := CharReplacer.Replace(options.Format)
1382 if outputFormat == "" {
1383 outputFormat = GetCommandOptionWithDefault("log-correlation-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
1384 }
1385 orderBy := options.OrderBy
1386 if orderBy == "" {
1387 orderBy = GetCommandOptionWithDefault("log-correlation-list", "order", "ComponentName,Status")
1388 }
1389
1390 result := CommandResult{
1391 Format: format.Format(outputFormat),
1392 Filter: options.Filter,
1393 OrderBy: orderBy,
1394 OutputAs: toOutputType(options.OutputAs),
1395 NameLimit: options.NameLimit,
1396 Data: data,
1397 }
1398 GenerateOutput(&result)
1399 return nil
1400}
Joey Armstrong516b3712022-12-16 03:16:58 -05001401
Joey Armstrong9fc4fa82022-12-19 18:38:40 -05001402// NOP-job: 1
1403// [EOF]