Code changes for VOL-3928 Voltctl support to list flow groups

Change-Id: I4e374a0c837511f41bed25937f70c5781133debf
diff --git a/VERSION b/VERSION
index 9f05f9f..ec70f75 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.6.5
+1.6.6
diff --git a/internal/pkg/commands/command.go b/internal/pkg/commands/command.go
index ec9318c..8cefc14 100644
--- a/internal/pkg/commands/command.go
+++ b/internal/pkg/commands/command.go
@@ -114,6 +114,9 @@
 	HexId bool `short:"x" long:"hex-id" description:"Output Ids in hex format"`
 }
 
+type GroupListOptions struct {
+	Bucket bool `short:"b" long:"buckets" description:"Display Buckets"`
+}
 type ListOutputOptions struct {
 	OutputOptions
 	Filter  string `short:"f" long:"filter" default:"" value-name:"FILTER" description:"Only display results that match filter"`
diff --git a/internal/pkg/commands/devices.go b/internal/pkg/commands/devices.go
index 10ebad8..073cd66 100644
--- a/internal/pkg/commands/devices.go
+++ b/internal/pkg/commands/devices.go
@@ -132,6 +132,13 @@
 	} `positional-args:"yes"`
 }
 
+type DeviceFlowGroupList struct {
+	ListOutputOptions
+	GroupListOptions
+	Args struct {
+		Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
+	} `positional-args:"yes"`
+}
 type DevicePortList struct {
 	ListOutputOptions
 	Args struct {
@@ -367,12 +374,13 @@
 }
 
 type DeviceOpts struct {
-	List    DeviceList     `command:"list"`
-	Create  DeviceCreate   `command:"create"`
-	Delete  DeviceDelete   `command:"delete"`
-	Enable  DeviceEnable   `command:"enable"`
-	Disable DeviceDisable  `command:"disable"`
-	Flows   DeviceFlowList `command:"flows"`
+	List    DeviceList          `command:"list"`
+	Create  DeviceCreate        `command:"create"`
+	Delete  DeviceDelete        `command:"delete"`
+	Enable  DeviceEnable        `command:"enable"`
+	Disable DeviceDisable       `command:"disable"`
+	Flows   DeviceFlowList      `command:"flows"`
+	Groups  DeviceFlowGroupList `command:"groups"`
 	Port    struct {
 		List    DevicePortList    `command:"list"`
 		Enable  DevicePortEnable  `command:"enable"`
@@ -890,6 +898,15 @@
 	return fl.Execute(args)
 }
 
+func (options *DeviceFlowGroupList) Execute(args []string) error {
+	grp := &GroupList{}
+	grp.ListOutputOptions = options.ListOutputOptions
+	grp.GroupListOptions = options.GroupListOptions
+	grp.Args.Id = string(options.Args.Id)
+	grp.Method = "device-groups"
+	return grp.Execute(args)
+}
+
 func (options *DeviceInspect) Execute(args []string) error {
 	if len(args) > 0 {
 		return fmt.Errorf("only a single argument 'DEVICE_ID' can be provided")
diff --git a/internal/pkg/commands/groups.go b/internal/pkg/commands/groups.go
new file mode 100644
index 0000000..5e71e8f
--- /dev/null
+++ b/internal/pkg/commands/groups.go
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2021-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"
+	"fmt"
+
+	"github.com/opencord/voltctl/pkg/format"
+	"github.com/opencord/voltha-protos/v4/go/openflow_13"
+	"github.com/opencord/voltha-protos/v4/go/voltha"
+)
+
+const (
+	DEFAULT_DEVICE_GROUPS_FORMAT        = "table{{.GroupId}}\t{{.Type}}"
+	DEFAULT_DEVICE_GROUPS_BUCKET_FORMAT = "table{{.GroupId}}\t{{.Buckets}}\t{{.Type}}"
+)
+
+type GroupList struct {
+	ListOutputOptions
+	GroupListOptions
+	Args struct {
+		Id string `positional-arg-name:"DEVICE_ID" required:"yes"`
+	} `positional-args:"yes"`
+
+	Method string
+}
+
+func (options *GroupList) 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.Current().Grpc.Timeout)
+	defer cancel()
+
+	id := voltha.ID{Id: string(options.Args.Id)}
+
+	var groups *openflow_13.FlowGroups
+	switch options.Method {
+	case "device-groups":
+		groups, err = client.ListDeviceFlowGroups(ctx, &id)
+	case "logical-device-groups":
+		groups, err = client.ListLogicalDeviceFlowGroups(ctx, &id)
+	default:
+		Error.Fatalf("Unknown method name: '%s'", options.Method)
+	}
+
+	var groupList []*openflow_13.OfpGroupDesc
+	if err != nil {
+		return err
+	}
+	if toOutputType(options.OutputAs) == OUTPUT_TABLE && (groups == nil || len(groups.Items) == 0) {
+		fmt.Println("*** NO GROUPS ***")
+		return nil
+	}
+	for _, item := range groups.Items {
+		if item.Desc.Type != openflow_13.OfpGroupType_OFPGT_FF {
+			// Since onos is setting watch port and watch group as max uint32,
+			// for group type other than FF, we need to remove watch port and group for them.
+			for _, bucket := range item.Desc.Buckets {
+				bucket.WatchPort = 0
+				bucket.WatchGroup = 0
+			}
+		}
+		groupList = append(groupList, item.Desc)
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if options.Quiet {
+		outputFormat = "{{.GroupId}}"
+	} else if outputFormat == "" {
+		if options.Bucket {
+			outputFormat = GetCommandOptionWithDefault(options.Method, "format", DEFAULT_DEVICE_GROUPS_BUCKET_FORMAT)
+		} else {
+			outputFormat = GetCommandOptionWithDefault(options.Method, "format", DEFAULT_DEVICE_GROUPS_FORMAT)
+		}
+	}
+
+	orderBy := options.OrderBy
+	if orderBy == "" {
+		orderBy = GetCommandOptionWithDefault(options.Method, "order", "")
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		Filter:    options.Filter,
+		OrderBy:   orderBy,
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      groupList,
+	}
+	GenerateOutput(&result)
+
+	return nil
+}
diff --git a/internal/pkg/commands/logicaldevices.go b/internal/pkg/commands/logicaldevices.go
index 9191385..2f2a1bc 100644
--- a/internal/pkg/commands/logicaldevices.go
+++ b/internal/pkg/commands/logicaldevices.go
@@ -49,6 +49,14 @@
 	} `positional-args:"yes"`
 }
 
+type LogicalDeviceFlowGroupList struct {
+	ListOutputOptions
+	GroupListOptions
+	Args struct {
+		Id DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
+	} `positional-args:"yes"`
+}
+
 type LogicalDevicePortList struct {
 	ListOutputOptions
 	Args struct {
@@ -64,9 +72,10 @@
 }
 
 type LogicalDeviceOpts struct {
-	List  LogicalDeviceList     `command:"list"`
-	Flows LogicalDeviceFlowList `command:"flows"`
-	Port  struct {
+	List   LogicalDeviceList          `command:"list"`
+	Flows  LogicalDeviceFlowList      `command:"flows"`
+	Groups LogicalDeviceFlowGroupList `command:"groups"`
+	Port   struct {
 		List LogicalDevicePortList `command:"list"`
 	} `command:"port"`
 	Inspect LogicalDeviceInspect `command:"inspect"`
@@ -219,6 +228,16 @@
 	return fl.Execute(args)
 }
 
+func (options *LogicalDeviceFlowGroupList) Execute(args []string) error {
+	grp := &GroupList{}
+	grp.ListOutputOptions = options.ListOutputOptions
+	grp.GroupListOptions = options.GroupListOptions
+	grp.Args.Id = string(options.Args.Id)
+	grp.Method = "logical-device-groups"
+	return grp.Execute(args)
+
+}
+
 func (options *LogicalDeviceInspect) Execute(args []string) error {
 	if len(args) > 0 {
 		return fmt.Errorf("only a single argument 'DEVICE_ID' can be provided")