VOL-3735 Retrieve port counters using the GetExtValue(SingleGetValue) rpc from the openolt adapter

Change-Id: I6bce9eb07396fb84e6733beeb81341080243e7a5
diff --git a/internal/pkg/commands/devices.go b/internal/pkg/commands/devices.go
index 416e1c5..f04ab4e 100644
--- a/internal/pkg/commands/devices.go
+++ b/internal/pkg/commands/devices.go
@@ -25,8 +25,9 @@
 	"github.com/golang/protobuf/ptypes/empty"
 	flags "github.com/jessevdk/go-flags"
 	"github.com/opencord/voltctl/pkg/format"
-	"github.com/opencord/voltha-protos/v3/go/common"
-	"github.com/opencord/voltha-protos/v3/go/voltha"
+	"github.com/opencord/voltha-protos/v4/go/common"
+	"github.com/opencord/voltha-protos/v4/go/extension"
+	"github.com/opencord/voltha-protos/v4/go/voltha"
 )
 
 const (
@@ -46,6 +47,19 @@
 	DEFAULT_DEVICE_PM_CONFIG_GROUP_LIST_FORMAT  = "table{{.GroupName}}\t{{.Enabled}}\t{{.GroupFreq}}"
 	DEFAULT_DEVICE_VALUE_GET_FORMAT             = "table{{.Name}}\t{{.Result}}"
 	DEFAULT_DEVICE_IMAGE_LIST_GET_FORMAT        = "table{{.Name}}\t{{.Url}}\t{{.Crc}}\t{{.DownloadState}}\t{{.ImageVersion}}\t{{.LocalDir}}\t{{.ImageState}}\t{{.FileSize}}"
+	DEFAULT_DEVICE_GET_PORT_STATUS_FORMAT       = `
+  TXBYTES:		{{.TxBytes}}
+  TXPACKETS:		{{.TxPackets}}
+  TXERRPACKETS:		{{.TxErrorPackets}}
+  TXBCASTPACKETS:	{{.TxBcastPackets}}
+  TXUCASTPACKETS:	{{.TxUcastPackets}}
+  TXMCASTPACKETS:	{{.TxMcastPackets}}
+  RXBYTES:		{{.RxBytes}}
+  RXPACKETS:		{{.RxPackets}}
+  RXERRPACKETS:		{{.RxErrorPackets}}
+  RXBCASTPACKETS:	{{.RxBcastPackets}}
+  RXUCASTPACKETS:	{{.RxUcastPackets}}
+  RXMCASTPACKETS:	{{.RxMcastPackets}}`
 )
 
 type DeviceList struct {
@@ -232,6 +246,14 @@
 		ImageVersion string   `positional-arg-name:"IMAGE_VERSION" required:"yes"`
 		SaveConfig   bool     `positional-arg-name:"SAVE_EXISTING_CONFIG"`
 		LocalDir     string   `positional-arg-name:"IMAGE_LOCAL_DIRECTORY"`
+	}
+}
+type DeviceGetPortStats struct {
+	ListOutputOptions
+	Args struct {
+		Id       DeviceId `positional-arg-name:"DEVICE_ID" required:"yes"`
+		PortNo   uint32   `positional-arg-name:"PORT_NO" required:"yes"`
+		PortType string   `positional-arg-name:"PORT_TYPE" required:"yes"`
 	} `positional-args:"yes"`
 }
 
@@ -274,12 +296,14 @@
 			List DevicePmConfigGroupMetricList `command:"list"`
 		} `command:"groupmetric"`
 	} `command:"pmconfig"`
-
 	Image struct {
 		Get      DeviceOnuListImages          `command:"list"`
 		Download DeviceOnuDownloadImage       `command:"download"`
 		Activate DeviceOnuActivateImageUpdate `command:"activate"`
 	} `command:"image"`
+	GetExtVal struct {
+		Stats DeviceGetPortStats `command:"portstats"`
+	} `command:"getextval"`
 }
 
 var deviceOpts = DeviceOpts{}
@@ -1416,6 +1440,63 @@
 	Result interface{} `json:"result"`
 }
 
+func (options *DeviceGetPortStats) Execute(args []string) error {
+	conn, err := NewConnection()
+	if err != nil {
+		return err
+	}
+	defer conn.Close()
+	client := extension.NewExtensionClient(conn)
+	var portType extension.GetOltPortCounters_PortType
+
+	if options.Args.PortType == "pon" {
+		portType = extension.GetOltPortCounters_Port_PON_OLT
+	} else if options.Args.PortType == "nni" {
+
+		portType = extension.GetOltPortCounters_Port_ETHERNET_NNI
+	} else {
+		return fmt.Errorf("expected interface type pon/nni, provided %s", options.Args.PortType)
+	}
+
+	singleGetValReq := extension.SingleGetValueRequest{
+		TargetId: string(options.Args.Id),
+		Request: &extension.GetValueRequest{
+			Request: &extension.GetValueRequest_OltPortInfo{
+				OltPortInfo: &extension.GetOltPortCounters{
+					PortNo:   options.Args.PortNo,
+					PortType: portType,
+				},
+			},
+		},
+	}
+
+	ctx, cancel := context.WithTimeout(context.Background(), GlobalConfig.Grpc.Timeout)
+	defer cancel()
+	rv, err := client.GetExtValue(ctx, &singleGetValReq)
+	if err != nil {
+		Error.Printf("Error getting value on device Id %s,err=%s\n", options.Args.Id, ErrorToString(err))
+		return err
+	}
+
+	if rv.Response.Status != extension.GetValueResponse_OK {
+		return fmt.Errorf("failed to get port stats %v", rv.Response.ErrReason.String())
+	}
+
+	outputFormat := CharReplacer.Replace(options.Format)
+	if outputFormat == "" {
+		outputFormat = GetCommandOptionWithDefault("device-get-port-status", "format", DEFAULT_DEVICE_GET_PORT_STATUS_FORMAT)
+	}
+
+	result := CommandResult{
+		Format:    format.Format(outputFormat),
+		OutputAs:  toOutputType(options.OutputAs),
+		NameLimit: options.NameLimit,
+		Data:      rv.GetResponse().GetPortCoutners(),
+	}
+	GenerateOutput(&result)
+	return nil
+}
+
 /*Device  get Onu Distance */
 func (options *DeviceGetExtValue) Execute(args []string) error {
 	conn, err := NewConnection()