SEBA-38 implement status command

Change-Id: I23a513d8190a4b83ffdf35c91b50aa6955c35b8d
diff --git a/commands/status.go b/commands/status.go
new file mode 100644
index 0000000..215d1bb
--- /dev/null
+++ b/commands/status.go
@@ -0,0 +1,90 @@
+/*
+ * 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 (
+	"context"
+	"github.com/fullstorydev/grpcurl"
+	flags "github.com/jessevdk/go-flags"
+	"github.com/jhump/protoreflect/dynamic"
+	"strings"
+)
+
+const StatusListFormat = "table{{ .Component }}\t{{ .Name }}\t{{ .Version }}\t{{ .Connection }}\t{{ .Status }}"
+
+type StatusListOpts struct {
+	ListOutputOptions
+	Filter string `short:"f" long:"filter" description:"Comma-separated list of filters"`
+}
+
+type StatusOpts struct {
+	List StatusListOpts `command:"list"`
+}
+
+var statusOpts = StatusOpts{}
+
+func RegisterStatusCommands(parser *flags.Parser) {
+	parser.AddCommand("status", "status commands", "Commands to query status of various subsystems", &statusOpts)
+}
+
+func (options *StatusListOpts) Execute(args []string) error {
+	conn, descriptor, err := InitReflectionClient()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	headers := GenerateHeaders()
+
+	var components []map[string]string
+
+	// TODO(smbaker): Consider using David's client-side filtering can be used so we get filtering
+	// by other fields (status, etc) for free.
+
+	if options.Filter == "" || strings.Contains(strings.ToLower(options.Filter), "database") {
+		h := &RpcEventHandler{}
+		err = grpcurl.InvokeRPC(ctx, descriptor, conn, "xos.utility.GetDatabaseInfo", headers, h, h.GetParams)
+		if err != nil {
+			return err
+		}
+
+		if h.Status != nil && h.Status.Err() != nil {
+			return h.Status.Err()
+		}
+
+		d, err := dynamic.AsDynamicMessage(h.Response)
+		if err != nil {
+			return err
+		}
+
+		db_map := make(map[string]string)
+		db_map["Component"] = "Database"
+		db_map["Name"] = d.GetFieldByName("name").(string)
+		db_map["Version"] = d.GetFieldByName("version").(string)
+		db_map["Connection"] = d.GetFieldByName("connection").(string)
+		db_map["Status"] = GetEnumValue(d, "status")
+
+		components = append(components, db_map)
+	}
+
+	FormatAndGenerateListOutput(&options.ListOutputOptions, StatusListFormat, StatusListFormat, components)
+
+	return nil
+}