VOL-2170 add common method the output GRPC status error

Change-Id: Ibc48cb087cd73f981542bcb137bdb4f213fa7e8d
diff --git a/cmd/voltctl/voltctl.go b/cmd/voltctl/voltctl.go
index 5b70bac..8a342ca 100644
--- a/cmd/voltctl/voltctl.go
+++ b/cmd/voltctl/voltctl.go
@@ -41,7 +41,7 @@
 	compval := os.Getenv("GO_FLAGS_COMPLETION")
 	if len(compval) > 0 {
 		os.Unsetenv("GO_FLAGS_COMPLETION")
-		pp := flags.NewNamedParser(path.Base(os.Args[0]), flags.Default|flags.PassAfterNonOption)
+		pp := flags.NewNamedParser(path.Base(os.Args[0]), flags.HelpFlag|flags.PassDoubleDash|flags.PassAfterNonOption)
 		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())
 		}
@@ -51,10 +51,10 @@
 		os.Setenv("GO_FLAGS_COMPLETION", compval)
 	}
 
-	parser := flags.NewNamedParser(path.Base(os.Args[0]), flags.Default|flags.PassAfterNonOption)
+	parser := flags.NewNamedParser(path.Base(os.Args[0]), flags.HelpFlag|flags.PassDoubleDash|flags.PassAfterNonOption)
 	_, err := parser.AddGroup("Global Options", "", &commands.GlobalOptions)
 	if err != nil {
-		panic(err)
+		commands.Error.Fatalf("Unable to parse global command options: %s", err.Error())
 	}
 	commands.RegisterAdapterCommands(parser)
 	commands.RegisterDeviceCommands(parser)
@@ -75,7 +75,7 @@
 				return
 			}
 		} else {
-			panic(err)
+			commands.Error.Fatal(commands.ErrorToString(err))
 		}
 		os.Exit(1)
 	}
diff --git a/internal/pkg/commands/command.go b/internal/pkg/commands/command.go
index a4e298e..7972b8f 100644
--- a/internal/pkg/commands/command.go
+++ b/internal/pkg/commands/command.go
@@ -73,7 +73,7 @@
 
 	GlobalConfig = GlobalConfigSpec{
 		ApiVersion: "v2",
-		Server:     "localhost",
+		Server:     "localhost:55555",
 		Tls: TlsConfigSpec{
 			UseTls: false,
 		},
@@ -236,38 +236,38 @@
 		if result.Filter != "" {
 			f, err := filter.Parse(result.Filter)
 			if err != nil {
-				panic(err)
+				Error.Fatalf("Unable to parse specified output filter '%s': %s", result.Filter, err.Error())
 			}
 			data, err = f.Process(data)
 			if err != nil {
-				panic(err)
+				Error.Fatalf("Unexpected error while filtering command results: %s", err.Error())
 			}
 		}
 		if result.OrderBy != "" {
 			s, err := order.Parse(result.OrderBy)
 			if err != nil {
-				panic(err)
+				Error.Fatalf("Unable to parse specified sort specification '%s': %s", result.OrderBy, err.Error())
 			}
 			data, err = s.Process(data)
 			if err != nil {
-				panic(err)
+				Error.Fatalf("Unexpected error while sorting command result: %s", err.Error())
 			}
 		}
 		if result.OutputAs == OUTPUT_TABLE {
 			tableFormat := format.Format(result.Format)
 			if err := tableFormat.Execute(os.Stdout, true, result.NameLimit, data); err != nil {
-				Error.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.Error())
 			}
 		} else if result.OutputAs == OUTPUT_JSON {
 			asJson, err := json.Marshal(&data)
 			if err != nil {
-				panic(err)
+				Error.Fatalf("Unexpected error while processing command results to JSON: %s", err.Error())
 			}
 			fmt.Printf("%s", asJson)
 		} else if result.OutputAs == OUTPUT_YAML {
 			asYaml, err := yaml.Marshal(&data)
 			if err != nil {
-				panic(err)
+				Error.Fatalf("Unexpected error while processing command results to YAML: %s", err.Error())
 			}
 			fmt.Printf("%s", asYaml)
 		}
diff --git a/internal/pkg/commands/components.go b/internal/pkg/commands/components.go
index 4501f1f..203b64d 100644
--- a/internal/pkg/commands/components.go
+++ b/internal/pkg/commands/components.go
@@ -61,20 +61,20 @@
 	// use the current context in kubeconfig
 	config, err := clientcmd.BuildConfigFromFlags("", GlobalOptions.K8sConfig)
 	if err != nil {
-		panic(err.Error())
+		Error.Fatalf("Unable to resolve Kubernetes configuration options: %s", err.Error())
 	}
 
 	// create the clientset
 	clientset, err := kubernetes.NewForConfig(config)
 	if err != nil {
-		panic(err.Error())
+		Error.Fatalf("Unable to create client context for Kubernetes API connection: %s", err.Error())
 	}
 
 	pods, err := clientset.CoreV1().Pods("").List(metav1.ListOptions{
 		LabelSelector: "app.kubernetes.io/part-of=voltha",
 	})
 	if err != nil {
-		panic(err.Error())
+		Error.Fatalf("Unexpected error while attempting to query PODs from Kubernetes: %s", err.Error())
 	}
 
 	outputFormat := CharReplacer.Replace(options.Format)
diff --git a/internal/pkg/commands/devices.go b/internal/pkg/commands/devices.go
index ac6d508..8b603f8 100644
--- a/internal/pkg/commands/devices.go
+++ b/internal/pkg/commands/devices.go
@@ -303,10 +303,10 @@
 
 		err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
 		if err != nil {
-			fmt.Printf("Error while deleting '%s': %s\n", i, err)
+			Error.Printf("Error while deleting '%s': %s\n", i, err)
 			continue
 		} else if h.Status != nil && h.Status.Err() != nil {
-			fmt.Printf("Error while deleting '%s': %s\n", i, h.Status.Err())
+			Error.Printf("Error while deleting '%s': %s\n", i, ErrorToString(h.Status.Err()))
 			continue
 		}
 		fmt.Printf("%s\n", i)
@@ -336,10 +336,10 @@
 
 		err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
 		if err != nil {
-			fmt.Printf("Error while enabling '%s': %s\n", i, err)
+			Error.Printf("Error while enabling '%s': %s\n", i, err)
 			continue
 		} else if h.Status != nil && h.Status.Err() != nil {
-			fmt.Printf("Error while enabling '%s': %s\n", i, h.Status.Err())
+			Error.Printf("Error while enabling '%s': %s\n", i, ErrorToString(h.Status.Err()))
 			continue
 		}
 		fmt.Printf("%s\n", i)
@@ -369,10 +369,10 @@
 
 		err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
 		if err != nil {
-			fmt.Printf("Error while disabling '%s': %s\n", i, err)
+			Error.Printf("Error while disabling '%s': %s\n", i, err)
 			continue
 		} else if h.Status != nil && h.Status.Err() != nil {
-			fmt.Printf("Error while disabling '%s': %s\n", i, h.Status.Err())
+			Error.Printf("Error while disabling '%s': %s\n", i, ErrorToString(h.Status.Err()))
 			continue
 		}
 		fmt.Printf("%s\n", i)
@@ -402,10 +402,10 @@
 
 		err = grpcurl.InvokeRPC(ctx, descriptor, conn, method, []string{}, h, h.GetParams)
 		if err != nil {
-			fmt.Printf("Error while rebooting '%s': %s\n", i, err)
+			Error.Printf("Error while rebooting '%s': %s\n", i, err)
 			continue
 		} else if h.Status != nil && h.Status.Err() != nil {
-			fmt.Printf("Error while rebooting '%s': %s\n", i, h.Status.Err())
+			Error.Printf("Error while rebooting '%s': %s\n", i, ErrorToString(h.Status.Err()))
 			continue
 		}
 		fmt.Printf("%s\n", i)
diff --git a/internal/pkg/commands/error.go b/internal/pkg/commands/error.go
new file mode 100644
index 0000000..ff641d3
--- /dev/null
+++ b/internal/pkg/commands/error.go
@@ -0,0 +1,51 @@
+/*
+ * Portions copyright 2019-present Open Networking Foundation
+ * Original copyright 2019-present Ciena Corporation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package commands
+
+import (
+	"fmt"
+	"google.golang.org/grpc/status"
+	"regexp"
+	"strings"
+)
+
+var (
+	descRE = regexp.MustCompile(`desc = "(.*)"`)
+	try2RE = regexp.MustCompile(`all SubConns are in TransientFailure, latest connection error: (.*)`)
+	try3RE = regexp.MustCompile(`all SubConns are in TransientFailure, (.*)`)
+)
+
+func ErrorToString(err error) string {
+	if err == nil {
+		return ""
+	}
+
+	if st, ok := status.FromError(err); ok {
+		msg := st.Message()
+		if match := descRE.FindAllStringSubmatch(msg, 1); match != nil {
+			msg = match[0][1]
+		} else if match = try2RE.FindAllStringSubmatch(msg, 1); match != nil {
+			msg = match[0][1]
+		} else if match = try3RE.FindAllStringSubmatch(msg, 1); match != nil {
+			msg = match[0][1]
+		}
+
+		return fmt.Sprintf("%s: %s", strings.ToUpper(st.Code().String()), msg)
+	}
+	return err.Error()
+}
diff --git a/internal/pkg/commands/flows.go b/internal/pkg/commands/flows.go
index bfb82dc..b8c2eb6 100644
--- a/internal/pkg/commands/flows.go
+++ b/internal/pkg/commands/flows.go
@@ -117,7 +117,7 @@
 	case "device-flows":
 	case "logical-device-flows":
 	default:
-		panic(fmt.Errorf("Unknown method name: '%s'", options.Method))
+		Error.Fatalf("Unknown method name: '%s'", options.Method)
 	}
 
 	descriptor, method, err := GetMethod(options.Method)
diff --git a/internal/pkg/commands/handler.go b/internal/pkg/commands/handler.go
index 03e599e..0c6d32b 100644
--- a/internal/pkg/commands/handler.go
+++ b/internal/pkg/commands/handler.go
@@ -16,7 +16,6 @@
 package commands
 
 import (
-	"fmt"
 	"github.com/golang/protobuf/proto"
 	"github.com/jhump/protoreflect/desc"
 	"github.com/jhump/protoreflect/dynamic"
@@ -67,8 +66,7 @@
 	for k, v := range fields {
 		err := dmsg.TrySetFieldByName(k, v)
 		if err != nil {
-			fmt.Printf("Failed to set field %s in proto %s, err %v\n", k, dmsg.XXX_MessageName(), err)
-			panic("GetParams failure")
+			Error.Fatalf("Failed to set field %s in proto %s, err %v\n", k, dmsg.XXX_MessageName(), err.Error())
 		}
 	}
 	delete(h.Fields, dmsg.XXX_MessageName())
diff --git a/internal/pkg/commands/loglevel.go b/internal/pkg/commands/loglevel.go
index e7723fd..5da4e12 100644
--- a/internal/pkg/commands/loglevel.go
+++ b/internal/pkg/commands/loglevel.go
@@ -72,7 +72,7 @@
 func RegisterLogLevelCommands(parent *flags.Parser) {
 	_, err := parent.AddCommand("loglevel", "loglevel commands", "Get and set log levels", &logLevelOpts)
 	if err != nil {
-		panic(err)
+		Error.Fatalf("Unable to register log level commands with voltctl command parser: %s", err.Error())
 	}
 }
 
diff --git a/internal/pkg/commands/version.go b/internal/pkg/commands/version.go
index d68b633..05ddcc7 100644
--- a/internal/pkg/commands/version.go
+++ b/internal/pkg/commands/version.go
@@ -72,7 +72,7 @@
 func RegisterVersionCommands(parent *flags.Parser) {
 	_, err := parent.AddCommand("version", "display version", "Display client and server version", &versionOpts)
 	if err != nil {
-		panic(err)
+		Error.Fatalf("Unable to register version command: %s", err.Error())
 	}
 }