[VOL-3379] Support dynamic enable/disable of Log Correlation Feature

Change-Id: I6e75bb17cc878d917c49de831fd795fc8dee247a
diff --git a/VERSION b/VERSION
index 26aaba0..538ee20 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.2.0
+1.2.1-dev
diff --git a/internal/pkg/commands/log.go b/internal/pkg/commands/log.go
index 036abdc..6f59b9f 100644
--- a/internal/pkg/commands/log.go
+++ b/internal/pkg/commands/log.go
@@ -36,10 +36,11 @@
 )
 
 const (
-	defaultComponentName = "global"
-	defaultPackageName   = "default"
-	logPackagesListKey   = "log_package_list" // kvstore key containing list of allowed log packages
-	logTracingStatusKey  = "trace_publish"
+	defaultComponentName    = "global"
+	defaultPackageName      = "default"
+	logPackagesListKey      = "log_package_list" // kvstore key containing list of allowed log packages
+	logTracingStatusKey     = "trace_publish"
+	logCorrelationStatusKey = "log_correlation"
 )
 
 // Custom Option representing <component-name>#<package-name> format (package is optional)
@@ -121,6 +122,30 @@
 	} `positional-args:"yes" required:"yes"`
 }
 
+// EnableLogCorrelationOpts represent the supported CLI arguments for the log correlation enable command
+type EnableLogCorrelationOpts struct {
+	OutputOptions
+	Args struct {
+		Component []ComponentName
+	} `positional-args:"yes" required:"yes"`
+}
+
+// DisableLogCorrelationOpts represent the supported CLI arguments for the log correlation disable command
+type DisableLogCorrelationOpts struct {
+	OutputOptions
+	Args struct {
+		Component []ComponentName
+	} `positional-args:"yes" required:"yes"`
+}
+
+// ListLogCorrelationOpts represents the supported CLI arguments for the log correlation list command
+type ListLogCorrelationOpts struct {
+	ListOutputOptions
+	Args struct {
+		Component []ComponentName
+	} `positional-args:"yes" required:"yes"`
+}
+
 // LogPackageOpts represents the log package commands
 type LogPackageOpts struct {
 	ListLogPackages ListLogPackagesOpts `command:"list"`
@@ -140,11 +165,19 @@
 	ListLogTracing    ListLogTracingOpts    `command:"list"`
 }
 
+// LogCorrelationOpts represents the log correlation commands
+type LogCorrelationOpts struct {
+	EnableLogCorrelation  EnableLogCorrelationOpts  `command:"enable"`
+	DisableLogCorrelation DisableLogCorrelationOpts `command:"disable"`
+	ListLogCorrelation    ListLogCorrelationOpts    `command:"list"`
+}
+
 // LogOpts represents the log commands
 type LogOpts struct {
-	LogLevel   LogLevelOpts   `command:"level"`
-	LogPackage LogPackageOpts `command:"package"`
-	LogTracing LogTracingOpts `command:"tracing"`
+	LogLevel       LogLevelOpts       `command:"level"`
+	LogPackage     LogPackageOpts     `command:"package"`
+	LogTracing     LogTracingOpts     `command:"tracing"`
+	LogCorrelation LogCorrelationOpts `command:"correlation"`
 }
 
 var logOpts = LogOpts{}
@@ -236,7 +269,6 @@
 	ProcessGlobalOptions()
 
 	log.SetAllLogLevel(log.FatalLevel)
-
 	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
 	defer cancel()
 
@@ -357,7 +389,6 @@
 	ProcessGlobalOptions()
 
 	log.SetAllLogLevel(log.FatalLevel)
-
 	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
 	defer cancel()
 
@@ -642,7 +673,6 @@
 	ProcessGlobalOptions()
 
 	log.SetAllLogLevel(log.FatalLevel)
-
 	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
 	defer cancel()
 
@@ -823,7 +853,6 @@
 	ProcessGlobalOptions()
 
 	log.SetAllLogLevel(log.FatalLevel)
-
 	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
 	defer cancel()
 
@@ -897,12 +926,6 @@
 // Omitting the component name will enable trace publishing for all the components, as shown in below command.
 // voltctl log tracing enable
 func (options *EnableLogTracingOpts) Execute(args []string) error {
-
-	var (
-		componentNames []string
-		err            error
-	)
-
 	ProcessGlobalOptions()
 
 	log.SetAllLogLevel(log.FatalLevel)
@@ -917,6 +940,7 @@
 	defer cleanupFunc()
 
 	var output []LogLevelOutput
+	var componentNames []string
 
 	if len(options.Args.Component) == 0 {
 		// Apply to all components if no specific component has been indicated
@@ -940,24 +964,23 @@
 		err := config.Save(ctx, logTracingStatusKey, "ENABLED")
 		if err != nil {
 			output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
-		} else {
-			outmsg := ""
-			cvalid := false
-			for _, cname := range validComponents {
-				if component == cname {
-					cvalid = true
-					break
-				}
-			}
-
-			// For invalid component, add * against its name to indicate possible mis-configuration
-			if !cvalid {
-				component = "*" + component
-				outmsg = "Entered Component Name is not Currently active in Voltha"
-			}
-
-			output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
+			continue
 		}
+		outmsg := ""
+		cvalid := false
+		for _, cname := range validComponents {
+			if component == cname {
+				cvalid = true
+				break
+			}
+		}
+
+		// For invalid component, add * against its name to indicate possible mis-configuration
+		if !cvalid {
+			component = "*" + component
+			outmsg = "Entered Component Name is not Currently active in Voltha"
+		}
+		output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
 	}
 
 	outputFormat := CharReplacer.Replace(options.Format)
@@ -982,12 +1005,6 @@
 // Omitting the component name will disable trace publishing for all the components, as shown in below command.
 // voltctl log tracing disable
 func (options *DisableLogTracingOpts) Execute(args []string) error {
-
-	var (
-		componentNames []string
-		err            error
-	)
-
 	ProcessGlobalOptions()
 
 	log.SetAllLogLevel(log.FatalLevel)
@@ -1002,6 +1019,7 @@
 	defer cleanupFunc()
 
 	var output []LogLevelOutput
+	var componentNames []string
 
 	if len(options.Args.Component) == 0 {
 		// Apply to all components if no specific component has been indicated
@@ -1026,24 +1044,24 @@
 
 		if err != nil {
 			output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
-		} else {
-			outmsg := ""
-			cvalid := false
-			for _, cname := range validComponents {
-				if component == cname {
-					cvalid = true
-					break
-				}
-			}
-
-			// For invalid component, add * against its name to indicate possible mis-configuration
-			if !cvalid {
-				component = "*" + component
-				outmsg = "Entered Component Name is not Currently active in Voltha"
-			}
-
-			output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
+			continue
 		}
+		outmsg := ""
+		cvalid := false
+		for _, cname := range validComponents {
+			if component == cname {
+				cvalid = true
+				break
+			}
+		}
+
+		// For invalid component, add * against its name to indicate possible mis-configuration
+		if !cvalid {
+			component = "*" + component
+			outmsg = "Entered Component Name is not Currently active in Voltha"
+		}
+
+		output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
 	}
 
 	outputFormat := CharReplacer.Replace(options.Format)
@@ -1068,13 +1086,6 @@
 // Omitting the component name will list trace publishing for all the components, as shown in below command.
 // voltctl log tracing list
 func (options *ListLogTracingOpts) Execute(args []string) error {
-
-	var (
-		data           []model.LogFeature = []model.LogFeature{}
-		componentNames []string
-		err            error
-	)
-
 	ProcessGlobalOptions()
 
 	log.SetAllLogLevel(log.FatalLevel)
@@ -1088,6 +1099,7 @@
 	}
 	defer cleanupFunc()
 
+	var componentNames []string
 	if len(options.Args.Component) == 0 {
 		// Apply to all components if no specific component has been indicated
 		componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
@@ -1102,6 +1114,7 @@
 	}
 
 	validComponents := getVolthaComponentNames()
+	data := []model.LogFeature{}
 
 	for _, component := range componentNames {
 
@@ -1151,3 +1164,231 @@
 	GenerateOutput(&result)
 	return nil
 }
+
+// This method enables log correlation for components.
+// For example, using below command, log correlation can be enabled for specific component
+// voltctl log correlation enable <componentName>
+// Omitting the component name will enable log correlation for all the components, as shown in below command.
+// voltctl log correlation enable
+func (options *EnableLogCorrelationOpts) Execute(args []string) error {
+	ProcessGlobalOptions()
+
+	log.SetAllLogLevel(log.FatalLevel)
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
+	defer cancel()
+
+	cm, cleanupFunc, err := constructConfigManager(ctx)
+	if err != nil {
+		return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
+	}
+	defer cleanupFunc()
+
+	var output []LogLevelOutput
+	var componentNames []string
+
+	if len(options.Args.Component) == 0 {
+		// Apply to all components if no specific component has been indicated
+		componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
+		if err != nil {
+			return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
+		}
+	} else {
+		for _, name := range options.Args.Component {
+			componentNames = append(componentNames, string(name))
+		}
+	}
+
+	validComponents := getVolthaComponentNames()
+
+	for _, component := range componentNames {
+		config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
+		err := config.Save(ctx, logCorrelationStatusKey, "ENABLED")
+
+		if err != nil {
+			output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
+			continue
+		}
+
+		outmsg := ""
+		cvalid := false
+		for _, cname := range validComponents {
+			if component == cname {
+				cvalid = true
+				break
+			}
+		}
+		// For invalid component, add * against its name to indicate possible mis-configuration
+		if !cvalid {
+			component = "*" + component
+			outmsg = "Entered Component Name is not Currently active in Voltha"
+		}
+		output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("log-correlation-enable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      output,
+	}
+	GenerateOutput(&result)
+	return nil
+}
+
+// This method disables log correlation for components.
+// For example, using below command, log correlation can be disabled for specific component
+// voltctl log correlation disable <componentName>
+// Omitting the component name will disable log correlation for all the components, as shown in below command.
+// voltctl log correlation disable
+func (options *DisableLogCorrelationOpts) Execute(args []string) error {
+	ProcessGlobalOptions()
+
+	log.SetAllLogLevel(log.FatalLevel)
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
+	defer cancel()
+
+	cm, cleanupFunc, err := constructConfigManager(ctx)
+	if err != nil {
+		return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
+	}
+	defer cleanupFunc()
+
+	var output []LogLevelOutput
+	var componentNames []string
+
+	if len(options.Args.Component) == 0 {
+		// Apply to all components if no specific component has been indicated
+		componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
+		if err != nil {
+			return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
+		}
+	} else {
+		for _, name := range options.Args.Component {
+			componentNames = append(componentNames, string(name))
+		}
+	}
+
+	validComponents := getVolthaComponentNames()
+
+	for _, component := range componentNames {
+
+		config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
+		err := config.Save(ctx, logCorrelationStatusKey, "DISABLED")
+
+		if err != nil {
+			output = append(output, LogLevelOutput{ComponentName: component, Status: "Failure", Error: err.Error()})
+			continue
+		}
+		outmsg := ""
+		cvalid := false
+		for _, cname := range validComponents {
+			if component == cname {
+				cvalid = true
+				break
+			}
+		}
+		// For invalid component, add * against its name to indicate possible mis-configuration
+		if !cvalid {
+			component = "*" + component
+			outmsg = "Entered Component Name is not Currently active in Voltha"
+		}
+		output = append(output, LogLevelOutput{ComponentName: component, Status: "Success", Error: outmsg})
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("log-correlation-disable", "format", DEFAULT_LOG_FEATURE_RESULT_FORMAT)
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      output,
+	}
+	GenerateOutput(&result)
+	return nil
+}
+
+// This method lists current status of log correlation for components.
+// For example, using below command, log correlation can be queried for specific component
+// voltctl log correlation list <componentName>
+// Omitting the component name will list log correlation for all the components, as shown in below command.
+// voltctl log correlation list
+func (options *ListLogCorrelationOpts) Execute(args []string) error {
+	ProcessGlobalOptions()
+	log.SetAllLogLevel(log.FatalLevel)
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.KvStoreConfig.Timeout)
+	defer cancel()
+
+	cm, cleanupFunc, err := constructConfigManager(ctx)
+	if err != nil {
+		return fmt.Errorf("Error while constructing ConfigManager instance : %s", err)
+	}
+	defer cleanupFunc()
+
+	var componentNames []string
+	if len(options.Args.Component) == 0 {
+		// Apply to all components if no specific component has been indicated
+		componentNames, err = cm.RetrieveComponentList(ctx, config.ConfigTypeLogFeatures)
+		if err != nil {
+			return fmt.Errorf("Unable to retrieve list of voltha components : %s ", err)
+		}
+	} else {
+		for _, name := range options.Args.Component {
+			componentNames = append(componentNames, string(name))
+		}
+	}
+
+	validComponents := getVolthaComponentNames()
+	data := []model.LogFeature{}
+
+	for _, component := range componentNames {
+		config := cm.InitComponentConfig(component, config.ConfigTypeLogFeatures)
+		value, err := config.Retrieve(ctx, logCorrelationStatusKey)
+		if err != nil || value == "" {
+			// Ignore any error in retrieval; move to next component
+			continue
+		}
+		cvalid := false
+
+		for _, cname := range validComponents {
+			if component == cname {
+				cvalid = true
+				break
+			}
+		}
+		// For invalid component, add * against its name to indicate possible mis-configuration
+		if !cvalid {
+			component = "*" + component
+		}
+		logCorrelationStatus := model.LogFeature{}
+		logCorrelationStatus.PopulateFrom(component, value)
+		data = append(data, logCorrelationStatus)
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("log-correlation-list", "format", DEFAULT_LOG_FEATURE_STATUS_FORMAT)
+	}
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("log-correlation-list", "order", "ComponentName,Status")
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		Filter:    options.Filter,
+		OrderBy:   orderBy,
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      data,
+	}
+	GenerateOutput(&result)
+	return nil
+}
diff --git a/voltctl_command_options.config b/voltctl_command_options.config
index fb20790..bdca081 100644
--- a/voltctl_command_options.config
+++ b/voltctl_command_options.config
@@ -32,3 +32,6 @@
 
 log-tracing-list:
   order: ComponentName,Status
+
+log-correlation-list:
+  order: ComponentName,Status