VOL-2168 - add the ability to specify default formats and ordering

Change-Id: Ie867bbd8d055e34f26eb43466a92f1d1954e35d4
diff --git a/Makefile b/Makefile
index 62332d7..fc0ea52 100644
--- a/Makefile
+++ b/Makefile
@@ -155,7 +155,7 @@
 endif
 	@rm -rf ./sca-report
 	@mkdir -p ./sca-report
-	$(GOLANGCI_LINT_TOOL) run --out-format $(GOLANGCI_LINT_OUT_FORMAT) ./... 2>&1 | tee ./sca-report/sca-report.xml
+	$(GOLANGCI_LINT_TOOL) run --deadline 20m --out-format $(GOLANGCI_LINT_OUT_FORMAT) ./... 2>&1 | tee ./sca-report/sca-report.xml
 
 test:
 	@mkdir -p ./tests/results
diff --git a/README.md b/README.md
index 1966af9..8e2ea55 100644
--- a/README.md
+++ b/README.md
@@ -75,6 +75,15 @@
 `--outputas` or `-o` command line option. Valid values for this options are
 `table`, `json`, or `yaml`.
 
+## Overriding Default Command Format and Order
+The default format and ordering of commands can be overriden (specified) by
+the command line options, but they can also be set via a configuration file so
+that the overrides don't have to be specified on each invocation. By default
+the file `~/.volt/command_options` is loaded, but the file used can also be
+specified by the environment variable `VOLTCTL_COMMAND_OPTIONS` or via
+the command line arguments. A samle of this file is include in the 
+repository as `voltctl_command_options.config`.
+
 ### Examples
 ```
 voltctl adapter list
diff --git a/cmd/voltctl/voltctl.go b/cmd/voltctl/voltctl.go
index caa0f40..5b70bac 100644
--- a/cmd/voltctl/voltctl.go
+++ b/cmd/voltctl/voltctl.go
@@ -42,11 +42,12 @@
 	if len(compval) > 0 {
 		os.Unsetenv("GO_FLAGS_COMPLETION")
 		pp := flags.NewNamedParser(path.Base(os.Args[0]), flags.Default|flags.PassAfterNonOption)
-		_, err := pp.AddGroup("Global Options", "", &commands.GlobalOptions)
-		if err != nil {
-			panic(err)
+		if _, err := pp.AddGroup("Global Options", "", &commands.GlobalOptions); err != nil {
+			commands.Error.Fatalf("Unable to set up global options for command completion: %s", err.Error())
 		}
-		pp.Parse()
+		if _, err := pp.Parse(); err != nil {
+			commands.Error.Fatalf("Unable to parse command line options for command completion: %s", err.Error())
+		}
 		os.Setenv("GO_FLAGS_COMPLETION", compval)
 	}
 
diff --git a/internal/pkg/commands/adapter.go b/internal/pkg/commands/adapter.go
index 067b64c..b777889 100644
--- a/internal/pkg/commands/adapter.go
+++ b/internal/pkg/commands/adapter.go
@@ -22,7 +22,6 @@
 	"github.com/jhump/protoreflect/dynamic"
 	"github.com/opencord/voltctl/pkg/format"
 	"github.com/opencord/voltctl/pkg/model"
-	"log"
 )
 
 const (
@@ -41,7 +40,7 @@
 
 func RegisterAdapterCommands(parent *flags.Parser) {
 	if _, err := parent.AddCommand("adapter", "adapter commands", "Commands to query and manipulate VOLTHA adapters", &adapterOpts); err != nil {
-		log.Fatalf("Unexpected error while attempting to register adapter commands : %s", err)
+		Error.Fatalf("Unexpected error while attempting to register adapter commands : %s", err)
 	}
 }
 
@@ -81,12 +80,15 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_OUTPUT_FORMAT
+		outputFormat = GetCommandOptionWithDefault("adapter-list", "format", DEFAULT_OUTPUT_FORMAT)
 	}
-
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
 	}
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("adapter-list", "order", "")
+	}
 
 	data := make([]model.Adapter, len(items.([]interface{})))
 	for i, item := range items.([]interface{}) {
@@ -96,7 +98,7 @@
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
diff --git a/internal/pkg/commands/command.go b/internal/pkg/commands/command.go
index 2331163..a4e298e 100644
--- a/internal/pkg/commands/command.go
+++ b/internal/pkg/commands/command.go
@@ -82,19 +82,27 @@
 		},
 	}
 
+	GlobalCommandOptions = make(map[string]map[string]string)
+
 	GlobalOptions struct {
 		Config string `short:"c" long:"config" env:"VOLTCONFIG" value-name:"FILE" default:"" description:"Location of client config file"`
 		Server string `short:"s" long:"server" default:"" value-name:"SERVER:PORT" description:"IP/Host and port of VOLTHA"`
 		// Do not set the default for the API version here, else it will override the value read in the config
-		ApiVersion string `short:"a" long:"apiversion" description:"API version" value-name:"VERSION" choice:"v1" choice:"v2"`
-		Debug      bool   `short:"d" long:"debug" description:"Enable debug mode"`
-		UseTLS     bool   `long:"tls" description:"Use TLS"`
-		CACert     string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
-		Cert       string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
-		Key        string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
-		Verify     bool   `long:"tlsverify" description:"Use TLS and verify the remote"`
-		K8sConfig  string `short:"8" long:"k8sconfig" env:"KUBECONFIG" value-name:"FILE" default:"" description:"Location of Kubernetes config file"`
+		ApiVersion     string `short:"a" long:"apiversion" description:"API version" value-name:"VERSION" choice:"v1" choice:"v2"`
+		Debug          bool   `short:"d" long:"debug" description:"Enable debug mode"`
+		UseTLS         bool   `long:"tls" description:"Use TLS"`
+		CACert         string `long:"tlscacert" value-name:"CA_CERT_FILE" description:"Trust certs signed only by this CA"`
+		Cert           string `long:"tlscert" value-name:"CERT_FILE" description:"Path to TLS vertificate file"`
+		Key            string `long:"tlskey" value-name:"KEY_FILE" description:"Path to TLS key file"`
+		Verify         bool   `long:"tlsverify" description:"Use TLS and verify the remote"`
+		K8sConfig      string `short:"8" long:"k8sconfig" env:"KUBECONFIG" value-name:"FILE" default:"" description:"Location of Kubernetes config file"`
+		CommandOptions string `short:"o" long:"command-options" env:"VOLTCTL_COMMAND_OPTIONS" value-name:"FILE" default:"" description:"Location of command options default configuration file"`
 	}
+
+	Debug = log.New(os.Stdout, "DEBUG: ", 0)
+	Info  = log.New(os.Stdout, "INFO: ", 0)
+	Warn  = log.New(os.Stderr, "WARN: ", 0)
+	Error = log.New(os.Stderr, "ERROR: ", 0)
 )
 
 type OutputOptions struct {
@@ -145,25 +153,34 @@
 	Data      interface{}
 }
 
+func GetCommandOptionWithDefault(name, option, defaultValue string) string {
+	if cmd, ok := GlobalCommandOptions[name]; ok {
+		if val, ok := cmd[option]; ok {
+			return CharReplacer.Replace(val)
+		}
+	}
+	return defaultValue
+}
+
 func ProcessGlobalOptions() {
 	if len(GlobalOptions.Config) == 0 {
 		home, err := os.UserHomeDir()
 		if err != nil {
-			log.Printf("Unable to discover they users home directory: %s\n", err)
+			Warn.Printf("Unable to discover the user's home directory: %s", err)
 			home = "~"
 		}
 		GlobalOptions.Config = filepath.Join(home, ".volt", "config")
 	}
 
-	info, err := os.Stat(GlobalOptions.Config)
-	if err == nil && !info.IsDir() {
+	if info, err := os.Stat(GlobalOptions.Config); err == nil && !info.IsDir() {
 		configFile, err := ioutil.ReadFile(GlobalOptions.Config)
 		if err != nil {
-			log.Printf("configFile.Get err   #%v ", err)
+			Error.Fatalf("Unable to read the configuration file '%s': %s",
+				GlobalOptions.Config, err.Error())
 		}
-		err = yaml.Unmarshal(configFile, &GlobalConfig)
-		if err != nil {
-			log.Fatalf("Unmarshal: %v", err)
+		if err = yaml.Unmarshal(configFile, &GlobalConfig); err != nil {
+			Error.Fatalf("Unable to parse the configuration file '%s': %s",
+				GlobalOptions.Config, err.Error())
 		}
 	}
 
@@ -180,11 +197,32 @@
 	if len(GlobalOptions.K8sConfig) == 0 {
 		home, err := os.UserHomeDir()
 		if err != nil {
-			log.Printf("Unable to discover the user's home directory: %s\n", err)
+			Warn.Printf("Unable to discover the user's home directory: %s", err)
 			home = "~"
 		}
 		GlobalOptions.K8sConfig = filepath.Join(home, ".kube", "config")
 	}
+
+	if len(GlobalOptions.CommandOptions) == 0 {
+		home, err := os.UserHomeDir()
+		if err != nil {
+			Warn.Printf("Unable to discover the user's home directory: %s", err)
+			home = "~"
+		}
+		GlobalOptions.CommandOptions = filepath.Join(home, ".volt", "command_options")
+	}
+
+	if info, err := os.Stat(GlobalOptions.CommandOptions); err == nil && !info.IsDir() {
+		optionsFile, err := ioutil.ReadFile(GlobalOptions.CommandOptions)
+		if err != nil {
+			Error.Fatalf("Unable to read command options configuration file '%s' : %s",
+				GlobalOptions.CommandOptions, err.Error())
+		}
+		if err = yaml.Unmarshal(optionsFile, &GlobalCommandOptions); err != nil {
+			Error.Fatalf("Unable to parse the command line options configuration file '%s': %s",
+				GlobalOptions.CommandOptions, err.Error())
+		}
+	}
 }
 
 func NewConnection() (*grpc.ClientConn, error) {
@@ -218,7 +256,7 @@
 		if result.OutputAs == OUTPUT_TABLE {
 			tableFormat := format.Format(result.Format)
 			if err := tableFormat.Execute(os.Stdout, true, result.NameLimit, data); err != nil {
-				log.Fatalf("Unexpected error while attempting to format results as table : %s", err)
+				Error.Fatalf("Unexpected error while attempting to format results as table : %s", err)
 			}
 		} else if result.OutputAs == OUTPUT_JSON {
 			asJson, err := json.Marshal(&data)
diff --git a/internal/pkg/commands/completion.go b/internal/pkg/commands/completion.go
index 17de063..0052a10 100644
--- a/internal/pkg/commands/completion.go
+++ b/internal/pkg/commands/completion.go
@@ -19,7 +19,6 @@
 	"fmt"
 	flags "github.com/jessevdk/go-flags"
 	"github.com/opencord/voltctl/internal/pkg/completion"
-	"log"
 )
 
 type BashOptions struct{}
@@ -30,7 +29,7 @@
 
 func RegisterCompletionCommands(parent *flags.Parser) {
 	if _, err := parent.AddCommand("completion", "generate shell compleition", "Commands to generate shell compleition information", &CompletionOptions{}); err != nil {
-		log.Fatalf("Unexpected error while attempting to register completion commands : %s", err)
+		Error.Fatalf("Unexpected error while attempting to register completion commands : %s", err)
 	}
 }
 
diff --git a/internal/pkg/commands/components.go b/internal/pkg/commands/components.go
index 95cbd80..4501f1f 100644
--- a/internal/pkg/commands/components.go
+++ b/internal/pkg/commands/components.go
@@ -23,7 +23,6 @@
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/tools/clientcmd"
-	"log"
 )
 
 const (
@@ -44,7 +43,7 @@
 
 func RegisterComponentCommands(parser *flags.Parser) {
 	if _, err := parser.AddCommand("component", "component instance commands", "Commands to query and manipulate VOLTHA component instances", &componentOpts); err != nil {
-		log.Fatalf("Unexpected error while attempting to register component commands : %s", err)
+		Error.Fatalf("Unexpected error while attempting to register component commands : %s", err)
 	}
 }
 
@@ -80,11 +79,16 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_COMPONENT_FORMAT
+		outputFormat = GetCommandOptionWithDefault("component-list", "format", DEFAULT_COMPONENT_FORMAT)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Metadata.Name}}"
 	}
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("component-list", "order", "")
+	}
+
 	data := make([]model.ComponentInstance, len(pods.Items))
 	for i, item := range pods.Items {
 		data[i].PopulateFrom(item)
@@ -93,7 +97,7 @@
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
diff --git a/internal/pkg/commands/config.go b/internal/pkg/commands/config.go
index aadc9d2..3ba4118 100644
--- a/internal/pkg/commands/config.go
+++ b/internal/pkg/commands/config.go
@@ -19,7 +19,6 @@
 	"fmt"
 	flags "github.com/jessevdk/go-flags"
 	"gopkg.in/yaml.v2"
-	"log"
 )
 
 const copyrightNotice = `
@@ -39,12 +38,17 @@
 #
 `
 
+type CommandOptionsDump struct{}
+
 type ConfigOptions struct {
+	Commands CommandOptionsDump `command:"commands"`
 }
 
 func RegisterConfigCommands(parent *flags.Parser) {
-	if _, err := parent.AddCommand("config", "generate voltctl configuration", "Commands to generate voltctl configuration", &ConfigOptions{}); err != nil {
-		log.Fatalf("Unexpected error while attempting to register config commands : %s", err)
+	if command, err := parent.AddCommand("config", "generate voltctl configuration", "Commands to generate voltctl configuration", &ConfigOptions{}); err != nil {
+		Error.Fatalf("Unexpected error while attempting to register config commands : %s", err)
+	} else {
+		command.SubcommandsOptional = true
 	}
 }
 
@@ -59,3 +63,14 @@
 	fmt.Println(string(b))
 	return nil
 }
+
+func (commands *CommandOptionsDump) Execute(args []string) error {
+	ProcessGlobalOptions()
+	b, err := yaml.Marshal(GlobalCommandOptions)
+	if err != nil {
+		return err
+	}
+	fmt.Println(copyrightNotice)
+	fmt.Println(string(b))
+	return nil
+}
diff --git a/internal/pkg/commands/devicegroups.go b/internal/pkg/commands/devicegroups.go
index 7314b0a..4d206c7 100644
--- a/internal/pkg/commands/devicegroups.go
+++ b/internal/pkg/commands/devicegroups.go
@@ -22,7 +22,6 @@
 	"github.com/jhump/protoreflect/dynamic"
 	"github.com/opencord/voltctl/pkg/format"
 	"github.com/opencord/voltctl/pkg/model"
-	"log"
 )
 
 const (
@@ -42,7 +41,7 @@
 func RegisterDeviceGroupCommands(parser *flags.Parser) {
 	if _, err := parser.AddCommand("devicegroup", "device group commands", "Commands to query and manipulate VOLTHA device groups",
 		&deviceGroupOpts); err != nil {
-		log.Fatalf("Unexpected error while attempting to register device group commands : %s", err)
+		Error.Fatalf("Unexpected error while attempting to register device group commands : %s", err)
 	}
 }
 
@@ -54,7 +53,7 @@
 	}
 	defer conn.Close()
 
-	descriptor, method, err := GetMethod("devicegroup-list")
+	descriptor, method, err := GetMethod("device-group-list")
 	if err != nil {
 		return err
 	}
@@ -84,11 +83,15 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_DEVICE_GROUP_FORMAT
+		outputFormat = GetCommandOptionWithDefault("device-group-list", "format", DEFAULT_DEVICE_GROUP_FORMAT)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
 	}
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("device-group-list", "order", "")
+	}
 
 	data := make([]model.DeviceGroup, len(items.([]interface{})))
 	for i, item := range items.([]interface{}) {
@@ -99,7 +102,7 @@
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
diff --git a/internal/pkg/commands/devices.go b/internal/pkg/commands/devices.go
index 78d1aca..ac6d508 100644
--- a/internal/pkg/commands/devices.go
+++ b/internal/pkg/commands/devices.go
@@ -23,7 +23,6 @@
 	"github.com/jhump/protoreflect/dynamic"
 	"github.com/opencord/voltctl/pkg/format"
 	"github.com/opencord/voltctl/pkg/model"
-	"log"
 	"strings"
 )
 
@@ -115,7 +114,7 @@
 
 func RegisterDeviceCommands(parser *flags.Parser) {
 	if _, err := parser.AddCommand("device", "device commands", "Commands to query and manipulate VOLTHA devices", &deviceOpts); err != nil {
-		log.Fatalf("Unexpected error while attempting to register device commands : %s", err)
+		Error.Fatalf("Unexpected error while attempting to register device commands : %s", err)
 	}
 }
 
@@ -204,12 +203,17 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_DEVICE_FORMAT
+		outputFormat = GetCommandOptionWithDefault("device-list", "format", DEFAULT_DEVICE_FORMAT)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
 	}
 
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("device-list", "order", "")
+	}
+
 	data := make([]model.Device, len(items.([]interface{})))
 	for i, item := range items.([]interface{}) {
 		val := item.(*dynamic.Message)
@@ -219,7 +223,7 @@
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
@@ -450,12 +454,17 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_DEVICE_PORTS_FORMAT
+		outputFormat = GetCommandOptionWithDefault("device-ports", "format", DEFAULT_DEVICE_PORTS_FORMAT)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
 	}
 
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("device-ports", "order", "")
+	}
+
 	data := make([]model.DevicePort, len(items.([]interface{})))
 	for i, item := range items.([]interface{}) {
 		data[i].PopulateFrom(item.(*dynamic.Message))
@@ -464,7 +473,7 @@
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
@@ -478,7 +487,7 @@
 	fl := &FlowList{}
 	fl.ListOutputOptions = options.ListOutputOptions
 	fl.Args.Id = string(options.Args.Id)
-	fl.Method = "device-flow-list"
+	fl.Method = "device-flows"
 	return fl.Execute(args)
 }
 
@@ -521,7 +530,7 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_DEVICE_INSPECT_FORMAT
+		outputFormat = GetCommandOptionWithDefault("device-inspect", "format", DEFAULT_DEVICE_INSPECT_FORMAT)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
diff --git a/internal/pkg/commands/flows.go b/internal/pkg/commands/flows.go
index d16abab..bfb82dc 100644
--- a/internal/pkg/commands/flows.go
+++ b/internal/pkg/commands/flows.go
@@ -114,8 +114,8 @@
 	defer conn.Close()
 
 	switch options.Method {
-	case "device-flow-list":
-	case "logical-device-flow-list":
+	case "device-flows":
+	case "logical-device-flows":
 	default:
 		panic(fmt.Errorf("Unknown method name: '%s'", options.Method))
 	}
@@ -165,13 +165,18 @@
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
 	} else if outputFormat == "" {
-		outputFormat = buildOutputFormat(fieldset, model.FLOW_FIELD_STATS)
+		outputFormat = GetCommandOptionWithDefault(options.Method, "format", buildOutputFormat(fieldset, model.FLOW_FIELD_STATS))
+	}
+
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault(options.Method, "order", "")
 	}
 
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
diff --git a/internal/pkg/commands/funcmap.go b/internal/pkg/commands/funcmap.go
index 7f479df..f60d297 100644
--- a/internal/pkg/commands/funcmap.go
+++ b/internal/pkg/commands/funcmap.go
@@ -102,7 +102,7 @@
 		"v1": "voltha.VolthaGlobalService/GetDevice",
 		"v2": "voltha.VolthaService/GetDevice",
 	},
-	"device-flow-list": {
+	"device-flows": {
 		"v1": "voltha.VolthaGlobalService/ListDeviceFlows",
 		"v2": "voltha.VolthaService/ListDeviceFlows",
 	},
@@ -114,7 +114,7 @@
 		"v1": "voltha.VolthaGlobalService/ListLogicalDevicePorts",
 		"v2": "voltha.VolthaService/ListLogicalDevicePorts",
 	},
-	"logical-device-flow-list": {
+	"logical-device-flows": {
 		"v1": "voltha.VolthaGlobalService/ListLogicalDeviceFlows",
 		"v2": "voltha.VolthaService/ListLogicalDeviceFlows",
 	},
@@ -122,7 +122,7 @@
 		"v1": "voltha.VolthaGlobalService/GetLogicalDevice",
 		"v2": "voltha.VolthaService/GetLogicalDevice",
 	},
-	"devicegroup-list": {
+	"device-group-list": {
 		"v1": "voltha.VolthaGlobalService/ListDeviceGroups",
 		"v2": "voltha.VolthaService/ListDeviceGroups",
 	},
diff --git a/internal/pkg/commands/logicaldevices.go b/internal/pkg/commands/logicaldevices.go
index 16cd48a..cecd5ec 100644
--- a/internal/pkg/commands/logicaldevices.go
+++ b/internal/pkg/commands/logicaldevices.go
@@ -23,7 +23,6 @@
 	"github.com/jhump/protoreflect/dynamic"
 	"github.com/opencord/voltctl/pkg/format"
 	"github.com/opencord/voltctl/pkg/model"
-	"log"
 	"strings"
 )
 
@@ -74,7 +73,7 @@
 
 func RegisterLogicalDeviceCommands(parser *flags.Parser) {
 	if _, err := parser.AddCommand("logicaldevice", "logical device commands", "Commands to query and manipulate VOLTHA logical devices", &logicalDeviceOpts); err != nil {
-		log.Fatalf("Unexpected error while attempting to register logical device commands : %s", err)
+		Error.Fatalf("Unexpected error while attempting to register logical device commands : %s", err)
 	}
 }
 
@@ -163,11 +162,15 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_LOGICAL_DEVICE_FORMAT
+		outputFormat = GetCommandOptionWithDefault("logical-device-list", "format", DEFAULT_LOGICAL_DEVICE_FORMAT)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
 	}
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("local-device-list", "order", "")
+	}
 
 	data := make([]model.LogicalDevice, len(items.([]interface{})))
 	for i, item := range items.([]interface{}) {
@@ -177,7 +180,7 @@
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
@@ -227,11 +230,15 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_LOGICAL_DEVICE_PORT_FORMAT
+		outputFormat = GetCommandOptionWithDefault("logical-device-ports", "format", DEFAULT_LOGICAL_DEVICE_PORT_FORMAT)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
 	}
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault("logical-device-ports", "order", "")
+	}
 
 	data := make([]model.LogicalPort, len(items.([]interface{})))
 	for i, item := range items.([]interface{}) {
@@ -241,7 +248,7 @@
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
@@ -255,7 +262,7 @@
 	fl := &FlowList{}
 	fl.ListOutputOptions = options.ListOutputOptions
 	fl.Args.Id = string(options.Args.Id)
-	fl.Method = "logical-device-flow-list"
+	fl.Method = "logical-device-flows"
 	return fl.Execute(args)
 }
 
@@ -298,7 +305,7 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_LOGICAL_DEVICE_INSPECT_FORMAT
+		outputFormat = GetCommandOptionWithDefault("logical-device-inspect", "format", DEFAULT_LOGICAL_DEVICE_INSPECT_FORMAT)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Id}}"
diff --git a/internal/pkg/commands/loglevel.go b/internal/pkg/commands/loglevel.go
index 265ab8f..3bb7798 100644
--- a/internal/pkg/commands/loglevel.go
+++ b/internal/pkg/commands/loglevel.go
@@ -27,7 +27,6 @@
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 	"k8s.io/client-go/kubernetes"
 	"k8s.io/client-go/tools/clientcmd"
-	"log"
 	"strings"
 )
 
@@ -198,12 +197,12 @@
 		case "rw-core":
 			affinity_group, ok := pod.Labels["affinity-group"]
 			if !ok {
-				log.Printf("rwcore %s lacks affinity-group label", pod.Name)
+				Warn.Printf("rwcore %s lacks affinity-group label", pod.Name)
 				continue
 			}
 			affinity_group_core_id, ok := pod.Labels["affinity-group-core-id"]
 			if !ok {
-				log.Printf("rwcore %s lacks affinity-group-core-id label", pod.Name)
+				Warn.Printf("rwcore %s lacks affinity-group-core-id label", pod.Name)
 				continue
 			}
 			arouter_name = "vcore" + affinity_group + ".vcore" + affinity_group + affinity_group_core_id
@@ -315,7 +314,7 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_SETLOGLEVEL_FORMAT
+		outputFormat = GetCommandOptionWithDefault("loglevel-set", "format", DEFAULT_SETLOGLEVEL_FORMAT)
 	}
 
 	result := CommandResult{
@@ -329,7 +328,7 @@
 	return nil
 }
 
-func (options *GetLogLevelsOpts) Execute(args []string) error {
+func (options *GetLogLevelsOpts) getLogLevels(methodName string, args []string) error {
 	if len(options.Args.Component) == 0 {
 		return fmt.Errorf("Please specify at least one component")
 	}
@@ -409,13 +408,17 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DEFAULT_LOGLEVELS_FORMAT
+		outputFormat = GetCommandOptionWithDefault(methodName, "format", DEFAULT_LOGLEVELS_FORMAT)
+	}
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault(methodName, "order", "")
 	}
 
 	result := CommandResult{
 		Format:    format.Format(outputFormat),
 		Filter:    options.Filter,
-		OrderBy:   options.OrderBy,
+		OrderBy:   orderBy,
 		OutputAs:  toOutputType(options.OutputAs),
 		NameLimit: options.NameLimit,
 		Data:      data,
@@ -424,6 +427,10 @@
 	return nil
 }
 
+func (options *GetLogLevelsOpts) Execute(args []string) error {
+	return options.getLogLevels("loglevel-get", args)
+}
+
 func (options *ListLogLevelsOpts) Execute(args []string) error {
 	var getOptions GetLogLevelsOpts
 	var podNames []string
@@ -442,5 +449,5 @@
 	getOptions.ListOutputOptions = options.ListOutputOptions
 	getOptions.Args.Component = podNames
 
-	return getOptions.Execute(args)
+	return getOptions.getLogLevels("loglevel-list", args)
 }
diff --git a/internal/pkg/commands/version.go b/internal/pkg/commands/version.go
index 1ca0fbf..d68b633 100644
--- a/internal/pkg/commands/version.go
+++ b/internal/pkg/commands/version.go
@@ -105,7 +105,7 @@
 func (options *VersionOpts) clientOnlyVersion(args []string) error {
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = ClientOnlyFormat
+		outputFormat = GetCommandOptionWithDefault("version", "format", ClientOnlyFormat)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Version}}"
@@ -210,7 +210,7 @@
 
 	outputFormat := CharReplacer.Replace(options.Format)
 	if outputFormat == "" {
-		outputFormat = DefaultFormat
+		outputFormat = GetCommandOptionWithDefault("version", "format", DefaultFormat)
 	}
 	if options.Quiet {
 		outputFormat = "{{.Client.Version}}"
diff --git a/voltctl.config b/voltctl.config
index 15c8a41..b52590f 100644
--- a/voltctl.config
+++ b/voltctl.config
@@ -1,5 +1,4 @@
-apiVersion: v1
+apiVersion: v2
 server: voltha.voltha.svc.cluster.local:50555
 grpc:
   timeout: 10s
-
diff --git a/voltctl_command_options.config b/voltctl_command_options.config
new file mode 100644
index 0000000..6732777
--- /dev/null
+++ b/voltctl_command_options.config
@@ -0,0 +1,30 @@
+device-list:
+  format: table{{.Id}}\t{{.Type}}\t{{.Root}}\t{{.ParentId}}\t{{.SerialNumber}}\t{{.Vlan}}\t{{.AdminState}}\t{{.OperStatus}}\t{{.ConnectStatus}}\t{{.Reason}}
+  order: -Root,SerialNumber
+
+device-ports:
+  order: PortNo
+
+device-flows:
+  order: Priority,EthType
+
+logical-device-list:
+  order: RootDeviceId,DataPathId
+
+logical-device-ports:
+  order: Id
+
+logical-device-flows:
+  order: Priority,EthType
+
+adapter-list:
+  order: Id
+
+component-list:
+  order: Component,Name,Id
+
+loglevel-get:
+  order: ComponentName,PackageName,Level
+
+loglevel-list:
+  order: ComponentName,PackageName,Level