VOL-1381 statistics manager for openolt adapter
This is inital commit , as PM manger in core is not yet available the changeset
is incomplete and not tested. once PM manager is available this changeset will be
revisited.
Change-Id: I3556722fb0e5fe87d210b65c8b49e2a813b12ffe
diff --git a/adaptercore/statsmanager.go b/adaptercore/statsmanager.go
new file mode 100755
index 0000000..8d7f467
--- /dev/null
+++ b/adaptercore/statsmanager.go
@@ -0,0 +1,317 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+
+ * 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 adaptercore
+
+import (
+ "errors"
+ "fmt"
+
+ "github.com/opencord/voltha-go/common/log"
+ openolt "github.com/opencord/voltha-protos/go/openolt"
+ "github.com/opencord/voltha-protos/go/voltha"
+)
+
+type PonPort struct {
+ /*
+ This is a highly reduced version taken from the adtran pon_port.
+ TODO: Extend for use in the openolt adapter set.
+ */
+ /* MAX_ONUS_SUPPORTED = 256
+ DEFAULT_ENABLED = False
+ MAX_DEPLOYMENT_RANGE = 25000 # Meters (OLT-PB maximum)
+
+ _MCAST_ONU_ID = 253
+ _MCAST_ALLOC_BASE = 0x500
+
+ _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery'] # , 'autoactivate']
+ _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
+ */
+ PONID uint32
+ DeviceID string
+ IntfID uint32
+ PortNum uint32
+ PortID uint32
+ Label string
+ ONUs map[uint32]interface{}
+ ONUsByID map[uint32]interface{}
+
+ RxBytes uint64
+ RxPackets uint64
+ RxMcastPackets uint64
+ RxBcastPackets uint64
+ RxErrorPackets uint64
+ TxBytes uint64
+ TxPackets uint64
+ TxUcastPackets uint64
+ TxMcastPackets uint64
+ TxBcastPackets uint64
+ TxErrorPackets uint64
+}
+
+func NewPONPort(PONID uint32, DeviceID string, IntfID uint32, PortNum uint32) *PonPort {
+
+ var PON PonPort
+
+ PON.PONID = PONID
+ PON.DeviceID = DeviceID
+ PON.IntfID = IntfID
+ PON.PortNum = PortNum
+ PON.PortID = 0
+ PON.Label = fmt.Sprintf("%s,%d", "pon-", PONID)
+
+ PON.ONUs = make(map[uint32]interface{})
+ PON.ONUsByID = make(map[uint32]interface{})
+
+ /*
+ Statistics taken from nni_port
+ self.intf_id = 0 #handled by getter
+ self.port_no = 0 #handled by getter
+ self.port_id = 0 #handled by getter
+
+ Note: In the current implementation of the kpis coming from the BAL the stats are the
+ samne model for NNI and PON.
+
+ TODO: Integrate additional kpis for the PON and other southbound port objecgts.
+
+ */
+
+ PON.RxBytes = 0
+ PON.RxPackets = 0
+ PON.RxMcastPackets = 0
+ PON.RxBcastPackets = 0
+ PON.RxErrorPackets = 0
+ PON.TxBytes = 0
+ PON.TxPackets = 0
+ PON.TxUcastPackets = 0
+ PON.TxMcastPackets = 0
+ PON.TxBcastPackets = 0
+ PON.TxErrorPackets = 0
+
+ /* def __str__(self):
+ return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
+ self._admin_state,
+ self._oper_status,
+ self.olt)
+ */
+ return &PON
+}
+
+type NniPort struct {
+ /*
+ Northbound network port, often Ethernet-based
+
+ This is a highly reduced version taken from the adtran nni_port code set
+ TODO: add functions to allow for port specific values and operations
+ */
+ PortNum uint32
+ Name string
+ LogicalPort uint32
+ IntfID uint32
+
+ RxBytes uint64
+ RxPackets uint64
+ RxMcastPackets uint64
+ RxBcastPackets uint64
+ RxErrorPackets uint64
+ TxBytes uint64
+ TxPackets uint64
+ TxUcastPackets uint64
+ TxMcastPackets uint64
+ TxBcastPackets uint64
+ TxErrorPackets uint64
+}
+
+func NewNniPort(PortNum uint32, IntfID uint32) *NniPort {
+
+ var NNI NniPort
+
+ NNI.PortNum = PortNum
+ NNI.Name = fmt.Sprintf("%s,%d", "nni-", PortNum)
+ NNI.IntfID = IntfID
+
+ NNI.RxBytes = 0
+ NNI.RxPackets = 0
+ NNI.RxMcastPackets = 0
+ NNI.RxBcastPackets = 0
+ NNI.RxErrorPackets = 0
+ NNI.TxBytes = 0
+ NNI.TxPackets = 0
+ NNI.TxUcastPackets = 0
+ NNI.TxMcastPackets = 0
+ NNI.TxBcastPackets = 0
+ NNI.TxErrorPackets = 0
+
+ return &NNI
+}
+
+type OpenOltStatisticsMgr struct {
+ Device *DeviceHandler
+ NorthBoundPort map[uint32]NniPort
+ SouthBoundPort map[uint32]PonPort
+ // TODO PMMetrics Metrics
+}
+
+func NewOpenOltStatsMgr(Dev *DeviceHandler) *OpenOltStatisticsMgr {
+
+ var StatMgr OpenOltStatisticsMgr
+
+ StatMgr.Device = Dev
+ // TODO call metric PMMetric =
+ // Northbound and Southbound ports
+ // added to initialize the pm_metrics
+ var Ports interface{}
+ Ports, _ = InitPorts("nni", Dev.deviceId)
+ StatMgr.NorthBoundPort, _ = Ports.(map[uint32]NniPort)
+ Ports, _ = InitPorts("pon", Dev.deviceId)
+ StatMgr.SouthBoundPort, _ = Ports.(map[uint32]PonPort)
+
+ return &StatMgr
+}
+
+func InitPorts(Intftype string, DeviceID string) (interface{}, error) {
+ /*
+ This method collects the port objects: nni and pon that are updated with the
+ current data from the OLT
+
+ Both the northbound (nni) and southbound ports are indexed by the interface id (intf_id)
+ and NOT the port number. When the port object is instantiated it will contain the intf_id and
+ port_no values
+
+ :param type:
+ :return:
+ */
+ var i uint32
+ if Intftype == "nni" {
+ NniPorts := make(map[uint32]NniPort)
+ for i = 0; i <= 1; i++ {
+ Port := BuildPortObject(i, "nni", DeviceID).(NniPort)
+ NniPorts[Port.IntfID] = Port
+ }
+ return NniPorts, nil
+ } else if Intftype == "pon" {
+ PONPorts := make(map[uint32]PonPort)
+ for i = 0; i <= 16; i++ {
+ PONPort := BuildPortObject(i, "pon", DeviceID).(PonPort)
+ PONPorts[PONPort.IntfID] = PONPort
+ }
+ return PONPorts, nil
+ } else {
+ log.Errorf("Invalid type of interface %s", Intftype)
+ return nil, errors.New("Invalid type of interface ")
+ }
+}
+
+func BuildPortObject(PortNum uint32, IntfType string, DeviceID string) interface{} {
+ /*
+ Seperate method to allow for updating north and southbound ports
+ newly discovered ports and devices
+
+ :param port_num:
+ :param type:
+ :return:
+ */
+
+ //This builds a port object which is added to the
+ //appropriate northbound or southbound values
+ if IntfType == "nni" {
+ IntfID := IntfIdToPortNo(PortNum, voltha.Port_ETHERNET_UNI)
+ return NewNniPort(PortNum, IntfID)
+ } else if IntfType == "pon" {
+ // PON ports require a different configuration
+ // intf_id and pon_id are currently equal.
+ IntfID := IntfIdToPortNo(PortNum, voltha.Port_ETHERNET_NNI)
+ PONID := IntfID
+ return NewPONPort(PONID, DeviceID, IntfID, PortNum)
+ } else {
+ log.Errorf("Invalid type of interface %s", IntfType)
+ return nil
+ }
+}
+
+func (StatMgr *OpenOltStatisticsMgr) PortStatisticsIndication(PortStats *openolt.PortStatistics) {
+ log.Debugf("port-stats-collected %v", PortStats)
+ StatMgr.PortsStatisticsKpis(PortStats)
+ // TODO send stats to core topic to the voltha kafka or a different kafka ?
+}
+
+func FlowStatisticsIndication(self, FlowStats *openolt.FlowStatistics) {
+ log.Debugf("flow-stats-collected %v", FlowStats)
+ //TODO send to kafka ?
+}
+
+func (StatMgr *OpenOltStatisticsMgr) PortsStatisticsKpis(PortStats *openolt.PortStatistics) {
+
+ /*map the port stats values into a dictionary
+ Create a kpoEvent and publish to Kafka
+
+ :param port_stats:
+ :return:
+ */
+ //var err error
+ IntfID := PortStats.IntfId
+
+ if (IntfIdToPortNo(0, voltha.Port_ETHERNET_NNI) < IntfID) &&
+ (IntfID < IntfIdToPortNo(4, voltha.Port_ETHERNET_NNI)) {
+ /*
+ for this release we are only interested in the first NNI for
+ Northbound.
+ we are not using the other 3
+ */
+ return
+ } else {
+
+ PMData := make(map[string]uint64)
+ PMData["rx_bytes"] = PortStats.RxBytes
+ PMData["rx_packets"] = PortStats.RxPackets
+ PMData["rx_ucast_packets"] = PortStats.RxUcastPackets
+ PMData["rx_mcast_packets"] = PortStats.RxMcastPackets
+ PMData["rx_bcast_packets"] = PortStats.RxBcastPackets
+ PMData["rx_error_packets"] = PortStats.RxErrorPackets
+ PMData["tx_bytes"] = PortStats.TxBytes
+ PMData["tx_packets"] = PortStats.TxPackets
+ PMData["tx_ucast_packets"] = PortStats.TxUcastPackets
+ PMData["tx_mcast_packets"] = PortStats.TxMcastPackets
+ PMData["tx_bcast_packets"] = PortStats.TxBcastPackets
+ PMData["tx_error_packets"] = PortStats.TxErrorPackets
+ PMData["rx_crc_errors"] = PortStats.RxCrcErrors
+ PMData["bip_errors"] = PortStats.BipErrors
+
+ PMData["intf_id"] = uint64(PortStats.IntfId)
+
+ /*
+ Based upon the intf_id map to an nni port or a pon port
+ the intf_id is the key to the north or south bound collections
+
+ Based upon the intf_id the port object (nni_port or pon_port) will
+ have its data attr. updated by the current dataset collected.
+
+ For prefixing the rule is currently to use the port number and not the intf_id
+ */
+ //FIXME : Just use first NNI for now
+ /* TODO should the data be marshalled before sending it ?
+ if IntfID == IntfIdToPortNo(0, voltha.Port_ETHERNET_NNI) {
+ //NNI port (just the first one)
+ err = UpdatePortObjectKpiData(StatMgr.NorthBoundPorts[PortStats.IntfID], PMData)
+ } else {
+ //PON ports
+ err = UpdatePortObjectKpiData(SouthboundPorts[PortStats.IntfID], PMData)
+ }
+ if (err != nil) {
+ log.Error("Error publishing statistics data")
+ }
+ */
+ }
+}