WIP - Suggesting changes (take2)

    This is not yet completed, still working on things. Eventually the plan
    is to provide the following changes

    - restructure repo to be more aligned with https://github.com/golang-standards/project-layout
    - add k8s probes
    - modifications (golang range loops, etc) to follow some golang
    practices

Change-Id: I6922cbc00b5ef17ceab183aba00a7fc59ab46480
diff --git a/internal/pkg/openflow/stats.go b/internal/pkg/openflow/stats.go
new file mode 100644
index 0000000..68d2625
--- /dev/null
+++ b/internal/pkg/openflow/stats.go
@@ -0,0 +1,619 @@
+/*
+   Copyright 2020 the original author or authors.
+
+   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 openflow
+
+import (
+	"context"
+	"encoding/json"
+	"github.com/donNewtonAlpha/goloxi"
+	ofp "github.com/donNewtonAlpha/goloxi/of13"
+	"github.com/opencord/voltha-lib-go/v2/pkg/log"
+	"github.com/opencord/voltha-protos/v2/go/common"
+	"github.com/opencord/voltha-protos/v2/go/openflow_13"
+	"net"
+	"unsafe"
+)
+
+func (ofc *OFClient) handleStatsRequest(request ofp.IHeader, statType uint16) error {
+	if logger.V(log.DebugLevel) {
+		js, _ := json.Marshal(request)
+		logger.Debugw("handleStatsRequest called",
+			log.Fields{
+				"device-id": ofc.DeviceID,
+				"stat-type": statType,
+				"request":   js})
+	}
+
+	switch statType {
+	case ofp.OFPSTDesc:
+		statsReq := request.(*ofp.DescStatsRequest)
+		response, err := ofc.handleDescStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTFlow:
+		statsReq := request.(*ofp.FlowStatsRequest)
+		response, err := ofc.handleFlowStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		response.Length = uint16(unsafe.Sizeof(*response))
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+
+	case ofp.OFPSTAggregate:
+		statsReq := request.(*ofp.AggregateStatsRequest)
+		response, err := ofc.handleAggregateStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTTable:
+		statsReq := request.(*ofp.TableStatsRequest)
+		response, e := ofc.handleTableStatsRequest(statsReq)
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		if e != nil {
+			return e
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTPort:
+		statsReq := request.(*ofp.PortStatsRequest)
+		response, err := ofc.handlePortStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTQueue:
+		statsReq := request.(*ofp.QueueStatsRequest)
+		response, err := ofc.handleQueueStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTGroup:
+		statsReq := request.(*ofp.GroupStatsRequest)
+		response, err := ofc.handleGroupStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		ofc.SendMessage(response)
+	case ofp.OFPSTGroupDesc:
+		statsReq := request.(*ofp.GroupDescStatsRequest)
+		response, err := ofc.handleGroupStatsDescRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+
+	case ofp.OFPSTGroupFeatures:
+		statsReq := request.(*ofp.GroupFeaturesStatsRequest)
+		response, err := ofc.handleGroupFeatureStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTMeter:
+		statsReq := request.(*ofp.MeterStatsRequest)
+		response, err := ofc.handleMeterStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTMeterConfig:
+		statsReq := request.(*ofp.MeterConfigStatsRequest)
+		response, err := ofc.handleMeterConfigStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTMeterFeatures:
+		statsReq := request.(*ofp.MeterFeaturesStatsRequest)
+		response, err := ofc.handleMeterFeatureStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTTableFeatures:
+		statsReq := request.(*ofp.TableFeaturesStatsRequest)
+		response, err := ofc.handleTableFeaturesStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	case ofp.OFPSTPortDesc:
+		statsReq := request.(*ofp.PortDescStatsRequest)
+		response, err := ofc.handlePortDescStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+
+	case ofp.OFPSTExperimenter:
+		statsReq := request.(*ofp.ExperimenterStatsRequest)
+		response, err := ofc.handleExperimenterStatsRequest(statsReq)
+		if err != nil {
+			return err
+		}
+		if logger.V(log.DebugLevel) {
+			reqJs, _ := json.Marshal(statsReq)
+			resJs, _ := json.Marshal(response)
+			logger.Debugw("handle-stats-request",
+				log.Fields{
+					"device-id": ofc.DeviceID,
+					"request":   reqJs,
+					"response":  resJs})
+		}
+		return ofc.SendMessage(response)
+	}
+	return nil
+}
+
+func (ofc *OFClient) handleDescStatsRequest(request *ofp.DescStatsRequest) (*ofp.DescStatsReply, error) {
+	response := ofp.NewDescStatsReply()
+	response.SetXid(request.GetXid())
+	response.SetVersion(request.GetVersion())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+
+	resp, err := ofc.VolthaClient.GetLogicalDevice(context.Background(),
+		&common.ID{Id: ofc.DeviceID})
+	if err != nil {
+		return nil, err
+	}
+	desc := resp.GetDesc()
+
+	response.SetMfrDesc(PadString(desc.GetMfrDesc(), 256))
+	response.SetHwDesc(PadString(desc.GetHwDesc(), 256))
+	response.SetSwDesc(PadString(desc.GetSwDesc(), 256))
+	response.SetSerialNum(PadString(desc.GetSerialNum(), 32))
+	response.SetDpDesc(PadString(desc.GetDpDesc(), 256))
+	return response, nil
+}
+
+func (ofc *OFClient) handleFlowStatsRequest(request *ofp.FlowStatsRequest) (*ofp.FlowStatsReply, error) {
+	response := ofp.NewFlowStatsReply()
+	response.SetXid(request.GetXid())
+	response.SetVersion(4)
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	resp, err := ofc.VolthaClient.ListLogicalDeviceFlows(context.Background(),
+		&common.ID{Id: ofc.DeviceID})
+	if err != nil {
+		return nil, err
+	}
+	var flow []*ofp.FlowStatsEntry
+	for _, item := range resp.GetItems() {
+		entry := ofp.NewFlowStatsEntry()
+		entry.SetTableId(uint8(item.GetTableId()))
+		entry.SetDurationSec(item.GetDurationSec())
+		entry.SetDurationNsec(item.GetDurationNsec())
+		entry.SetPriority(uint16(item.GetPriority()))
+		entry.SetIdleTimeout(uint16(item.GetIdleTimeout()))
+		entry.SetHardTimeout(uint16(item.GetHardTimeout()))
+		entry.SetFlags(ofp.FlowModFlags(item.GetFlags()))
+		entry.SetCookie(item.GetCookie())
+		entry.SetPacketCount(item.GetPacketCount())
+		entry.SetByteCount(item.GetByteCount())
+		entrySize := uint16(48)
+		match := ofp.NewMatchV3()
+		pbMatch := item.GetMatch()
+		match.SetType(uint16(pbMatch.GetType()))
+		size := uint16(4)
+		var fields []goloxi.IOxm
+		for _, oxmField := range pbMatch.GetOxmFields() {
+			field := oxmField.GetField()
+			ofbField := field.(*openflow_13.OfpOxmField_OfbField).OfbField
+			iOxm, oxmSize := parseOxm(ofbField, ofc.DeviceID)
+			fields = append(fields, iOxm)
+			if oxmSize > 0 {
+				size += 4 //header for oxm
+			}
+			size += oxmSize
+		}
+
+		match.OxmList = fields
+		match.Length = uint16(size)
+		//account for 8 byte alignment
+		if size%8 != 0 {
+			size = ((size / 8) + 1) * 8
+		}
+		entrySize += size
+		entry.SetMatch(*match)
+		var instructions []ofp.IInstruction
+		for _, ofpInstruction := range item.Instructions {
+			instruction, size := parseInstructions(ofpInstruction, ofc.DeviceID)
+			instructions = append(instructions, instruction)
+			entrySize += size
+		}
+		entry.Instructions = instructions
+		entry.Length = entrySize
+		entrySize = 0
+		flow = append(flow, entry)
+	}
+	response.SetEntries(flow)
+	return response, nil
+}
+
+func (ofc *OFClient) handleAggregateStatsRequest(request *ofp.AggregateStatsRequest) (*ofp.AggregateStatsReply, error) {
+	response := ofp.NewAggregateStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	response.SetFlowCount(0)
+	//TODO wire this to voltha core when it implements
+	return response, nil
+}
+
+func (ofc *OFClient) handleGroupStatsRequest(request *ofp.GroupStatsRequest) (*ofp.GroupStatsReply, error) {
+	response := ofp.NewGroupStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	reply, err := ofc.VolthaClient.ListLogicalDeviceFlowGroups(context.Background(),
+		&common.ID{Id: ofc.DeviceID})
+	if err != nil {
+		return nil, err
+	}
+
+	var groupStatsEntries []*ofp.GroupStatsEntry
+	for _, item := range reply.GetItems() {
+		stats := item.GetStats()
+		var entry ofp.GroupStatsEntry
+		entry.SetByteCount(stats.GetByteCount())
+		entry.SetPacketCount(stats.GetPacketCount())
+		entry.SetDurationNsec(stats.GetDurationNsec())
+		entry.SetDurationSec(stats.GetDurationSec())
+		entry.SetRefCount(stats.GetRefCount())
+		entry.SetGroupId(stats.GetGroupId())
+		var bucketStatsList []*ofp.BucketCounter
+		for _, bucketStat := range stats.GetBucketStats() {
+			bucketCounter := ofp.BucketCounter{}
+			bucketCounter.SetPacketCount(bucketStat.GetPacketCount())
+			bucketCounter.SetByteCount(bucketStat.GetByteCount())
+			bucketStatsList = append(bucketStatsList, &bucketCounter)
+		}
+		entry.SetBucketStats(bucketStatsList)
+		groupStatsEntries = append(groupStatsEntries, &entry)
+	}
+	response.SetEntries(groupStatsEntries)
+	return response, nil
+}
+
+func (ofc *OFClient) handleGroupStatsDescRequest(request *ofp.GroupDescStatsRequest) (*ofp.GroupDescStatsReply, error) {
+	response := ofp.NewGroupDescStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	reply, err := ofc.VolthaClient.ListLogicalDeviceFlowGroups(context.Background(),
+		&common.ID{Id: ofc.DeviceID})
+	if err != nil {
+		return nil, err
+	}
+	var groupDescStatsEntries []*ofp.GroupDescStatsEntry
+	for _, item := range reply.GetItems() {
+		stats := item.GetStats()
+		var groupDesc ofp.GroupDescStatsEntry
+		groupDesc.SetGroupId(stats.GetGroupId())
+		/*
+			buckets := item.g
+			var bucketList []*ofp.Bucket
+			for j:=0;j<len(buckets);j++{
+
+			}
+
+			groupDesc.SetBuckets(bucketList)
+		*/
+		groupDescStatsEntries = append(groupDescStatsEntries, &groupDesc)
+	}
+	response.SetEntries(groupDescStatsEntries)
+	return response, nil
+}
+
+func (ofc *OFClient) handleGroupFeatureStatsRequest(request *ofp.GroupFeaturesStatsRequest) (*ofp.GroupFeaturesStatsReply, error) {
+	response := ofp.NewGroupFeaturesStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	//TODO wire this to voltha core when it implements
+	return response, nil
+}
+
+func (ofc *OFClient) handleMeterStatsRequest(request *ofp.MeterStatsRequest) (*ofp.MeterStatsReply, error) {
+	response := ofp.NewMeterStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	resp, err := ofc.VolthaClient.ListLogicalDeviceMeters(context.Background(),
+		&common.ID{Id: ofc.DeviceID})
+	if err != nil {
+		return nil, err
+	}
+	size := uint16(40)
+	var meterStats []*ofp.MeterStats
+	for _, item := range resp.Items {
+		meterStat := ofp.NewMeterStats()
+		stats := item.Stats
+		meterStat.DurationNsec = stats.DurationNsec
+		meterStat.DurationSec = stats.DurationSec
+		meterStat.ByteInCount = stats.ByteInCount
+		meterStat.FlowCount = stats.FlowCount
+		meterStat.MeterId = stats.MeterId
+		var bandStats []*ofp.MeterBandStats
+		for _, bStat := range stats.BandStats {
+			bandStat := ofp.NewMeterBandStats()
+			bandStat.ByteBandCount = bStat.ByteBandCount
+			bandStat.PacketBandCount = bStat.PacketBandCount
+			bandStats = append(bandStats, bandStat)
+			size += 16
+		}
+		meterStat.SetBandStats(bandStats)
+		meterStat.Len = size
+		meterStats = append(meterStats, meterStat)
+	}
+	response.SetEntries(meterStats)
+	return response, nil
+}
+
+func (ofc *OFClient) handleMeterConfigStatsRequest(request *ofp.MeterConfigStatsRequest) (*ofp.MeterConfigStatsReply, error) {
+	response := ofp.NewMeterConfigStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	//TODO wire this to voltha core when it implements
+	return response, nil
+}
+
+func (ofc *OFClient) handleTableFeaturesStatsRequest(request *ofp.TableFeaturesStatsRequest) (*ofp.TableFeaturesStatsReply, error) {
+	response := ofp.NewTableFeaturesStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	//TODO wire this to voltha core when it implements
+	return response, nil
+}
+
+func (ofc *OFClient) handleTableStatsRequest(request *ofp.TableStatsRequest) (*ofp.TableStatsReply, error) {
+	var response = ofp.NewTableStatsReply()
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	return response, nil
+}
+
+func (ofc *OFClient) handleQueueStatsRequest(request *ofp.QueueStatsRequest) (*ofp.QueueStatsReply, error) {
+	response := ofp.NewQueueStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	//TODO wire this to voltha core when it implements
+	return response, nil
+}
+
+func (ofc *OFClient) handlePortStatsRequest(request *ofp.PortStatsRequest) (*ofp.PortStatsReply, error) {
+	response := ofp.NewPortStatsReply()
+	response.SetXid(request.GetXid())
+	response.SetVersion(request.GetVersion())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	reply, err := ofc.VolthaClient.ListLogicalDevicePorts(context.Background(),
+		&common.ID{Id: ofc.DeviceID})
+	if err != nil {
+		return nil, err
+	}
+	var entries []*ofp.PortStatsEntry
+	if request.GetPortNo() == 0xffffffff { //all ports
+		for _, port := range reply.GetItems() {
+			entries = append(entries, parsePortStats(port))
+		}
+	} else { //find right port that is requested
+		for _, port := range reply.GetItems() {
+			if port.GetOfpPortStats().GetPortNo() == uint32(request.GetPortNo()) {
+				entries = append(entries, parsePortStats(port))
+			}
+		}
+	}
+	response.SetEntries(entries)
+	return response, nil
+}
+
+func (ofc *OFClient) handlePortDescStatsRequest(request *ofp.PortDescStatsRequest) (*ofp.PortDescStatsReply, error) {
+	response := ofp.NewPortDescStatsReply()
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	logicalDevice, err := ofc.VolthaClient.GetLogicalDevice(context.Background(),
+		&common.ID{Id: ofc.DeviceID})
+	if err != nil {
+		return nil, err
+	}
+	var entries []*ofp.PortDesc
+	for _, port := range logicalDevice.GetPorts() {
+		ofpPort := port.GetOfpPort()
+		var entry ofp.PortDesc
+		entry.SetPortNo(ofp.Port(ofpPort.GetPortNo()))
+
+		var octets []byte
+		for _, val := range ofpPort.GetHwAddr() {
+			octets = append(octets, byte(val))
+		}
+		hwAddr := net.HardwareAddr(octets)
+		entry.SetHwAddr(hwAddr)
+		entry.SetName(PadString(ofpPort.GetName(), 16))
+		entry.SetConfig(ofp.PortConfig(ofpPort.GetConfig()))
+		entry.SetState(ofp.PortState(ofpPort.GetState()))
+		entry.SetCurr(ofp.PortFeatures(ofpPort.GetCurr()))
+		entry.SetAdvertised(ofp.PortFeatures(ofpPort.GetAdvertised()))
+		entry.SetSupported(ofp.PortFeatures(ofpPort.GetSupported()))
+		entry.SetPeer(ofp.PortFeatures(ofpPort.GetPeer()))
+		entry.SetCurrSpeed(ofpPort.GetCurrSpeed())
+		entry.SetMaxSpeed(ofpPort.GetMaxSpeed())
+
+		entries = append(entries, &entry)
+	}
+
+	response.SetEntries(entries)
+	//TODO call voltha and get port descriptions etc
+	return response, nil
+
+}
+
+func (ofc *OFClient) handleMeterFeatureStatsRequest(request *ofp.MeterFeaturesStatsRequest) (*ofp.MeterFeaturesStatsReply, error) {
+	response := ofp.NewMeterFeaturesStatsReply()
+	response.SetXid(request.GetXid())
+	response.SetVersion(request.GetVersion())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	meterFeatures := ofp.NewMeterFeatures()
+	meterFeatures.Capabilities = ofp.OFPMFKbps
+	meterFeatures.BandTypes = ofp.OFPMBTDrop
+	meterFeatures.MaxMeter = 0xffffffff
+	meterFeatures.MaxBands = 0xff
+	meterFeatures.MaxColor = 0xff
+	response.Features = *meterFeatures
+	return response, nil
+}
+
+func (ofc *OFClient) handleExperimenterStatsRequest(request *ofp.ExperimenterStatsRequest) (*ofp.ExperimenterStatsReply, error) {
+	response := ofp.NewExperimenterStatsReply(request.GetExperimenter())
+	response.SetVersion(request.GetVersion())
+	response.SetXid(request.GetXid())
+	response.SetFlags(ofp.StatsReplyFlags(request.GetFlags()))
+	//TODO wire this to voltha core when it implements
+	return response, nil
+}