Merge "Extending error message for 'voltctl log level list' command"
diff --git a/internal/pkg/commands/devices.go b/internal/pkg/commands/devices.go
index b6864f6..3068902 100644
--- a/internal/pkg/commands/devices.go
+++ b/internal/pkg/commands/devices.go
@@ -41,7 +41,10 @@
   ADMINSTATE:    {{.AdminState}}
   OPERSTATUS:    {{.OperStatus}}
   CONNECTSTATUS: {{.ConnectStatus}}`
-	DEFAULT_DEVICE_VALUE_GET_FORMAT = "table{{.Name}}\t{{.Result}}"
+	DEFAULT_DEVICE_PM_CONFIG_GET_FORMAT         = "table{{.DefaultFreq}}\t{{.Grouped}}\t{{.FreqOverride}}"
+	DEFAULT_DEVICE_PM_CONFIG_METRIC_LIST_FORMAT = "table{{.Name}}\t{{.Type}}\t{{.Enabled}}\t{{.SampleFreq}}"
+	DEFAULT_DEVICE_PM_CONFIG_GROUP_LIST_FORMAT  = "table{{.GroupName}}\t{{.Enabled}}\t{{.GroupFreq}}"
+	DEFAULT_DEVICE_VALUE_GET_FORMAT             = "table{{.Name}}\t{{.Result}}"
 )
 
 type DeviceList struct {
@@ -57,6 +60,8 @@
 
 type DeviceId string
 
+type MetricName string
+type GroupName string
 type PortNum uint32
 type ValueFlag string
 
@@ -119,6 +124,71 @@
 	} `positional-args:"yes"`
 }
 
+type DevicePmConfigsGet struct {
+	ListOutputOptions
+	Args struct {
+		Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
+	} `positional-args:"yes"`
+}
+
+type DevicePmConfigMetricList struct {
+	ListOutputOptions
+	Args struct {
+		Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
+	} `positional-args:"yes"`
+}
+
+type DevicePmConfigGroupList struct {
+	ListOutputOptions
+	Args struct {
+		Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
+	} `positional-args:"yes"`
+}
+
+type DevicePmConfigGroupMetricList struct {
+	ListOutputOptions
+	Args struct {
+		Id    DeviceId  `positional-arg-name:"DEVICE_ID" required:"yes"`
+		Group GroupName `positional-arg-name:"GROUP_NAME" required:"yes"`
+	} `positional-args:"yes"`
+}
+
+type DevicePmConfigFrequencySet struct {
+	OutputOptions
+	Args struct {
+		Frequency uint32   `positional-arg-name:"FREQUENCY" required:"yes"`
+		Id        DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
+	} `positional-args:"yes"`
+}
+
+type DevicePmConfigMetricEnable struct {
+	Args struct {
+		Id      DeviceId     `positional-arg-name:"DEVICE_ID" required:"yes"`
+		Metrics []MetricName `positional-arg-name:"METRIC_NAME" required:"yes"`
+	} `positional-args:"yes"`
+}
+
+type DevicePmConfigMetricDisable struct {
+	Args struct {
+		Id      DeviceId     `positional-arg-name:"DEVICE_ID" required:"yes"`
+		Metrics []MetricName `positional-arg-name:"METRIC_NAME" required:"yes"`
+	} `positional-args:"yes"`
+}
+
+type DevicePmConfigGroupEnable struct {
+	Args struct {
+		Id     DeviceId    `positional-arg-name:"DEVICE_ID" required:"yes"`
+		Groups []GroupName `positional-arg-name:"GROUP_NAME" required:"yes"`
+	} `positional-args:"yes"`
+}
+
+type DevicePmConfigGroupDisable struct {
+	Args struct {
+		Id     DeviceId    `positional-arg-name:"DEVICE_ID" required:"yes"`
+		Groups []GroupName `positional-arg-name:"GROUP_NAME" required:"yes"`
+	} `positional-args:"yes"`
+}
+
 type DeviceGetExtValue struct {
 	ListOutputOptions
 	Args struct {
@@ -143,6 +213,25 @@
 	Value   struct {
 		Get DeviceGetExtValue `command:"get"`
 	} `command:"value"`
+	PmConfig struct {
+		Get       DevicePmConfigsGet `command:"get"`
+		Frequency struct {
+			Set DevicePmConfigFrequencySet `command:"set"`
+		} `command:"frequency"`
+		Metric struct {
+			List    DevicePmConfigMetricList    `command:"list"`
+			Enable  DevicePmConfigMetricEnable  `command:"enable"`
+			Disable DevicePmConfigMetricDisable `command:"disable"`
+		} `command:"metric"`
+		Group struct {
+			List    DevicePmConfigGroupList    `command:"list"`
+			Enable  DevicePmConfigGroupEnable  `command:"enable"`
+			Disable DevicePmConfigGroupDisable `command:"disable"`
+		} `command:"group"`
+		GroupMetric struct {
+			List DevicePmConfigGroupMetricList `command:"list"`
+		} `command:"groupmetric"`
+	} `command:"pmconfig"`
 }
 
 var deviceOpts = DeviceOpts{}
@@ -153,6 +242,109 @@
 	}
 }
 
+func (i *MetricName) Complete(match string) []flags.Completion {
+	conn, err := NewConnection()
+	if err != nil {
+		return nil
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	var deviceId string
+found:
+	for i := len(os.Args) - 1; i >= 0; i -= 1 {
+		switch os.Args[i] {
+		case "enable":
+			fallthrough
+		case "disable":
+			if len(os.Args) > i+1 {
+				deviceId = os.Args[i+1]
+			} else {
+				return nil
+			}
+			break found
+		default:
+		}
+	}
+
+	if len(deviceId) == 0 {
+		return nil
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(deviceId)}
+
+	pmconfigs, err := client.ListDevicePmConfigs(ctx, &id)
+
+	if err != nil {
+		return nil
+	}
+
+	list := make([]flags.Completion, 0)
+	for _, metrics := range pmconfigs.Metrics {
+		if strings.HasPrefix(metrics.Name, match) {
+			list = append(list, flags.Completion{Item: metrics.Name})
+		}
+	}
+
+	return list
+}
+
+func (i *GroupName) Complete(match string) []flags.Completion {
+	conn, err := NewConnection()
+	if err != nil {
+		return nil
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	var deviceId string
+found:
+	for i := len(os.Args) - 1; i >= 0; i -= 1 {
+		switch os.Args[i] {
+		case "list":
+			fallthrough
+		case "enable":
+			fallthrough
+		case "disable":
+			if len(os.Args) > i+1 {
+				deviceId = os.Args[i+1]
+			} else {
+				return nil
+			}
+			break found
+		default:
+		}
+	}
+
+	if len(deviceId) == 0 {
+		return nil
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(deviceId)}
+
+	pmconfigs, err := client.ListDevicePmConfigs(ctx, &id)
+
+	if err != nil {
+		return nil
+	}
+
+	list := make([]flags.Completion, 0)
+	for _, group := range pmconfigs.Groups {
+		if strings.HasPrefix(group.GroupName, match) {
+			list = append(list, flags.Completion{Item: group.GroupName})
+		}
+	}
+	return list
+}
+
 func (i *PortNum) Complete(match string) []flags.Completion {
 	conn, err := NewConnection()
 	if err != nil {
@@ -473,9 +665,6 @@
 	if outputFormat == "" {
 		outputFormat = GetCommandOptionWithDefault("device-ports", "format", DEFAULT_DEVICE_PORTS_FORMAT)
 	}
-	if options.Quiet {
-		outputFormat = "{{.Id}}"
-	}
 
 	orderBy := options.OrderBy
 	if orderBy == "" {
@@ -592,6 +781,406 @@
 	return nil
 }
 
+func (options *DevicePmConfigsGet) Execute(args []string) error {
+
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("device-pm-configs", "format", DEFAULT_DEVICE_PM_CONFIG_GET_FORMAT)
+	}
+
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("device-pm-configs", "order", "")
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		Filter:    options.Filter,
+		OrderBy:   orderBy,
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      pmConfigs,
+	}
+
+	GenerateOutput(&result)
+	return nil
+
+}
+
+func (options *DevicePmConfigMetricList) Execute(args []string) error {
+
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	if !pmConfigs.Grouped {
+		for _, metric := range pmConfigs.Metrics {
+			if metric.SampleFreq == 0 {
+				metric.SampleFreq = pmConfigs.DefaultFreq
+			}
+		}
+
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("device-pm-configs", "format", DEFAULT_DEVICE_PM_CONFIG_METRIC_LIST_FORMAT)
+	}
+
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("device-pm-configs", "order", "")
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		Filter:    options.Filter,
+		OrderBy:   orderBy,
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      pmConfigs.Metrics,
+	}
+
+	GenerateOutput(&result)
+	return nil
+
+}
+
+func (options *DevicePmConfigMetricEnable) Execute(args []string) error {
+
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	if !pmConfigs.Grouped {
+		for _, metric := range pmConfigs.Metrics {
+			for _, mName := range options.Args.Metrics {
+				if string(mName) == metric.Name && !metric.Enabled {
+					metric.Enabled = true
+					_, err := client.UpdateDevicePmConfigs(ctx, pmConfigs)
+					if err != nil {
+						return err
+					}
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (options *DevicePmConfigMetricDisable) Execute(args []string) error {
+
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	if !pmConfigs.Grouped {
+		for _, metric := range pmConfigs.Metrics {
+			for _, mName := range options.Args.Metrics {
+				if string(mName) == metric.Name && metric.Enabled {
+					metric.Enabled = false
+					_, err := client.UpdateDevicePmConfigs(ctx, pmConfigs)
+					if err != nil {
+						return err
+					}
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (options *DevicePmConfigGroupEnable) Execute(args []string) error {
+
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	if pmConfigs.Grouped {
+		for _, group := range pmConfigs.Groups {
+			for _, gName := range options.Args.Groups {
+				if string(gName) == group.GroupName && !group.Enabled {
+					group.Enabled = true
+					_, err := client.UpdateDevicePmConfigs(ctx, pmConfigs)
+					if err != nil {
+						return err
+					}
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (options *DevicePmConfigGroupDisable) Execute(args []string) error {
+
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	if pmConfigs.Grouped {
+		for _, group := range pmConfigs.Groups {
+			for _, gName := range options.Args.Groups {
+				if string(gName) == group.GroupName && group.Enabled {
+					group.Enabled = false
+					_, err := client.UpdateDevicePmConfigs(ctx, pmConfigs)
+					if err != nil {
+						return err
+					}
+				}
+			}
+		}
+	}
+	return nil
+}
+
+func (options *DevicePmConfigGroupList) Execute(args []string) error {
+
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	if pmConfigs.Grouped {
+		for _, group := range pmConfigs.Groups {
+			if group.GroupFreq == 0 {
+				group.GroupFreq = pmConfigs.DefaultFreq
+			}
+		}
+
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("device-pm-configs", "format", DEFAULT_DEVICE_PM_CONFIG_GROUP_LIST_FORMAT)
+	}
+
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("device-pm-configs", "order", "")
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		Filter:    options.Filter,
+		OrderBy:   orderBy,
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      pmConfigs.Groups,
+	}
+
+	GenerateOutput(&result)
+	return nil
+
+}
+
+func (options *DevicePmConfigGroupMetricList) Execute(args []string) error {
+
+	var metrics []*voltha.PmConfig
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	for _, groups := range pmConfigs.Groups {
+
+		if string(options.Args.Group) == groups.GroupName {
+			for _, metric := range groups.Metrics {
+				if metric.SampleFreq == 0 && groups.GroupFreq == 0 {
+					metric.SampleFreq = pmConfigs.DefaultFreq
+				} else {
+					metric.SampleFreq = groups.GroupFreq
+				}
+			}
+			metrics = groups.Metrics
+		}
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("device-pm-configs", "format", DEFAULT_DEVICE_PM_CONFIG_METRIC_LIST_FORMAT)
+	}
+
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("device-pm-configs", "order", "")
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		Filter:    options.Filter,
+		OrderBy:   orderBy,
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      metrics,
+	}
+
+	GenerateOutput(&result)
+	return nil
+
+}
+
+func (options *DevicePmConfigFrequencySet) Execute(args []string) error {
+
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	client := voltha.NewVolthaServiceClient(conn)
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	pmConfigs, err := client.ListDevicePmConfigs(ctx, &id)
+	if err != nil {
+		return err
+	}
+
+	pmConfigs.DefaultFreq = options.Args.Frequency
+
+	_, err = client.UpdateDevicePmConfigs(ctx, pmConfigs)
+	if err != nil {
+		return err
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("device-pm-configs", "format", DEFAULT_DEVICE_PM_CONFIG_GET_FORMAT)
+	}
+	if options.Quiet {
+		outputFormat = "{{.Id}}"
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      pmConfigs,
+	}
+
+	GenerateOutput(&result)
+	return nil
+
+}
+
 type ReturnValueRow struct {
 	Name   string      `json:"name"`
 	Result interface{} `json:"result"`