blob: 6a67f152c2a4eca21e81235f8450f1cb8fe8960d [file] [log] [blame]
Abhilash S.L765ad002019-04-24 16:40:57 +05301/*
2 * Copyright 2019-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Girish Gowdru6a80bbd2019-07-02 07:36:09 -070016
17//Package adaptercore provides the utility for olt devices, flows and statistics
Abhilash S.L765ad002019-04-24 16:40:57 +053018package adaptercore
19
20import (
21 "errors"
22 "fmt"
Naga Manjunath7615e552019-10-11 22:35:47 +053023 "sync"
24 "time"
Abhilash S.L765ad002019-04-24 16:40:57 +053025
Scott Baker51290152019-10-24 14:23:20 -070026 "github.com/opencord/voltha-lib-go/v2/pkg/log"
Girish Gowdru6a80bbd2019-07-02 07:36:09 -070027 "github.com/opencord/voltha-protos/go/openolt"
Abhilash S.L765ad002019-04-24 16:40:57 +053028 "github.com/opencord/voltha-protos/go/voltha"
29)
30
Naga Manjunath7615e552019-10-11 22:35:47 +053031var mutex = &sync.Mutex{}
32
Girish Gowdru6a80bbd2019-07-02 07:36:09 -070033// PonPort representation
Abhilash S.L765ad002019-04-24 16:40:57 +053034type PonPort struct {
35 /*
36 This is a highly reduced version taken from the adtran pon_port.
37 TODO: Extend for use in the openolt adapter set.
38 */
39 /* MAX_ONUS_SUPPORTED = 256
40 DEFAULT_ENABLED = False
41 MAX_DEPLOYMENT_RANGE = 25000 # Meters (OLT-PB maximum)
42
43 _MCAST_ONU_ID = 253
44 _MCAST_ALLOC_BASE = 0x500
45
46 _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery'] # , 'autoactivate']
47 _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
48 */
49 PONID uint32
50 DeviceID string
51 IntfID uint32
52 PortNum uint32
53 PortID uint32
54 Label string
55 ONUs map[uint32]interface{}
56 ONUsByID map[uint32]interface{}
57
58 RxBytes uint64
59 RxPackets uint64
60 RxMcastPackets uint64
61 RxBcastPackets uint64
62 RxErrorPackets uint64
63 TxBytes uint64
64 TxPackets uint64
65 TxUcastPackets uint64
66 TxMcastPackets uint64
67 TxBcastPackets uint64
68 TxErrorPackets uint64
69}
70
Girish Gowdru6a80bbd2019-07-02 07:36:09 -070071// NewPONPort returns a new instance of PonPort initialized with given PONID, DeviceID, IntfID and PortNum
Abhilash S.L765ad002019-04-24 16:40:57 +053072func NewPONPort(PONID uint32, DeviceID string, IntfID uint32, PortNum uint32) *PonPort {
73
74 var PON PonPort
75
76 PON.PONID = PONID
77 PON.DeviceID = DeviceID
78 PON.IntfID = IntfID
79 PON.PortNum = PortNum
80 PON.PortID = 0
Naga Manjunath7615e552019-10-11 22:35:47 +053081 PON.Label = fmt.Sprintf("%s%d", "pon-", PONID)
Abhilash S.L765ad002019-04-24 16:40:57 +053082
83 PON.ONUs = make(map[uint32]interface{})
84 PON.ONUsByID = make(map[uint32]interface{})
85
86 /*
87 Statistics taken from nni_port
88 self.intf_id = 0 #handled by getter
89 self.port_no = 0 #handled by getter
90 self.port_id = 0 #handled by getter
91
92 Note: In the current implementation of the kpis coming from the BAL the stats are the
93 samne model for NNI and PON.
94
95 TODO: Integrate additional kpis for the PON and other southbound port objecgts.
96
97 */
98
99 PON.RxBytes = 0
100 PON.RxPackets = 0
101 PON.RxMcastPackets = 0
102 PON.RxBcastPackets = 0
103 PON.RxErrorPackets = 0
104 PON.TxBytes = 0
105 PON.TxPackets = 0
106 PON.TxUcastPackets = 0
107 PON.TxMcastPackets = 0
108 PON.TxBcastPackets = 0
109 PON.TxErrorPackets = 0
110
111 /* def __str__(self):
112 return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
113 self._admin_state,
114 self._oper_status,
115 self.olt)
116 */
117 return &PON
118}
119
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700120// NniPort representation
Abhilash S.L765ad002019-04-24 16:40:57 +0530121type NniPort struct {
122 /*
123 Northbound network port, often Ethernet-based
124
125 This is a highly reduced version taken from the adtran nni_port code set
126 TODO: add functions to allow for port specific values and operations
127 */
128 PortNum uint32
129 Name string
130 LogicalPort uint32
131 IntfID uint32
132
133 RxBytes uint64
134 RxPackets uint64
135 RxMcastPackets uint64
136 RxBcastPackets uint64
137 RxErrorPackets uint64
138 TxBytes uint64
139 TxPackets uint64
140 TxUcastPackets uint64
141 TxMcastPackets uint64
142 TxBcastPackets uint64
143 TxErrorPackets uint64
144}
145
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700146// NewNniPort returns a new instance of NniPort initialized with the given PortNum and IntfID
Abhilash S.L765ad002019-04-24 16:40:57 +0530147func NewNniPort(PortNum uint32, IntfID uint32) *NniPort {
148
149 var NNI NniPort
150
151 NNI.PortNum = PortNum
Naga Manjunath7615e552019-10-11 22:35:47 +0530152 NNI.Name = fmt.Sprintf("%s%d", "nni-", PortNum)
Abhilash S.L765ad002019-04-24 16:40:57 +0530153 NNI.IntfID = IntfID
154
155 NNI.RxBytes = 0
156 NNI.RxPackets = 0
157 NNI.RxMcastPackets = 0
158 NNI.RxBcastPackets = 0
159 NNI.RxErrorPackets = 0
160 NNI.TxBytes = 0
161 NNI.TxPackets = 0
162 NNI.TxUcastPackets = 0
163 NNI.TxMcastPackets = 0
164 NNI.TxBcastPackets = 0
165 NNI.TxErrorPackets = 0
166
167 return &NNI
168}
169
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700170// OpenOltStatisticsMgr structure
Abhilash S.L765ad002019-04-24 16:40:57 +0530171type OpenOltStatisticsMgr struct {
172 Device *DeviceHandler
Naga Manjunath7615e552019-10-11 22:35:47 +0530173 NorthBoundPort map[uint32]*NniPort
174 SouthBoundPort map[uint32]*PonPort
Abhilash S.L765ad002019-04-24 16:40:57 +0530175 // TODO PMMetrics Metrics
176}
177
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700178// NewOpenOltStatsMgr returns a new instance of the OpenOltStatisticsMgr
Abhilash S.L765ad002019-04-24 16:40:57 +0530179func NewOpenOltStatsMgr(Dev *DeviceHandler) *OpenOltStatisticsMgr {
180
181 var StatMgr OpenOltStatisticsMgr
182
183 StatMgr.Device = Dev
184 // TODO call metric PMMetric =
185 // Northbound and Southbound ports
186 // added to initialize the pm_metrics
187 var Ports interface{}
Naga Manjunath7615e552019-10-11 22:35:47 +0530188 Ports, _ = InitPorts("nni", Dev.deviceID, 1)
189 StatMgr.NorthBoundPort, _ = Ports.(map[uint32]*NniPort)
190 NumPonPorts := Dev.resourceMgr.DevInfo.GetPonPorts()
191 Ports, _ = InitPorts("pon", Dev.deviceID, NumPonPorts)
192 StatMgr.SouthBoundPort, _ = Ports.(map[uint32]*PonPort)
Abhilash S.L765ad002019-04-24 16:40:57 +0530193 return &StatMgr
194}
195
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700196// InitPorts collects the port objects: nni and pon that are updated with the current data from the OLT
Naga Manjunath7615e552019-10-11 22:35:47 +0530197func InitPorts(Intftype string, DeviceID string, numOfPorts uint32) (interface{}, error) {
Abhilash S.L765ad002019-04-24 16:40:57 +0530198 /*
199 This method collects the port objects: nni and pon that are updated with the
200 current data from the OLT
201
202 Both the northbound (nni) and southbound ports are indexed by the interface id (intf_id)
203 and NOT the port number. When the port object is instantiated it will contain the intf_id and
204 port_no values
205
206 :param type:
207 :return:
208 */
209 var i uint32
210 if Intftype == "nni" {
Naga Manjunath7615e552019-10-11 22:35:47 +0530211 NniPorts := make(map[uint32]*NniPort)
212 for i = 0; i < numOfPorts; i++ {
kdarapu768708d2019-09-16 23:19:15 +0530213 Port := BuildPortObject(i, "nni", DeviceID).(*NniPort)
Naga Manjunath7615e552019-10-11 22:35:47 +0530214 NniPorts[Port.IntfID] = Port
Abhilash S.L765ad002019-04-24 16:40:57 +0530215 }
216 return NniPorts, nil
217 } else if Intftype == "pon" {
Naga Manjunath7615e552019-10-11 22:35:47 +0530218 PONPorts := make(map[uint32]*PonPort)
219 for i = 0; i < numOfPorts; i++ {
kdarapu768708d2019-09-16 23:19:15 +0530220 PONPort := BuildPortObject(i, "pon", DeviceID).(*PonPort)
Naga Manjunath7615e552019-10-11 22:35:47 +0530221 PONPorts[PortNoToIntfID(PONPort.IntfID, voltha.Port_PON_OLT)] = PONPort
Abhilash S.L765ad002019-04-24 16:40:57 +0530222 }
223 return PONPorts, nil
224 } else {
225 log.Errorf("Invalid type of interface %s", Intftype)
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700226 return nil, errors.New("invalid type of interface ")
Abhilash S.L765ad002019-04-24 16:40:57 +0530227 }
228}
229
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700230// BuildPortObject allows for updating north and southbound ports, newly discovered ports, and devices
Abhilash S.L765ad002019-04-24 16:40:57 +0530231func BuildPortObject(PortNum uint32, IntfType string, DeviceID string) interface{} {
232 /*
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700233 Separate method to allow for updating north and southbound ports
Abhilash S.L765ad002019-04-24 16:40:57 +0530234 newly discovered ports and devices
235
236 :param port_num:
237 :param type:
238 :return:
239 */
240
241 //This builds a port object which is added to the
242 //appropriate northbound or southbound values
243 if IntfType == "nni" {
Naga Manjunath7615e552019-10-11 22:35:47 +0530244 IntfID := IntfIDToPortNo(PortNum, voltha.Port_ETHERNET_NNI)
245 nniID := PortNoToIntfID(IntfID, voltha.Port_ETHERNET_NNI)
246 log.Debugf("NniID %v", nniID)
247 return NewNniPort(PortNum, nniID)
Abhilash S.L765ad002019-04-24 16:40:57 +0530248 } else if IntfType == "pon" {
249 // PON ports require a different configuration
250 // intf_id and pon_id are currently equal.
Naga Manjunath7615e552019-10-11 22:35:47 +0530251 IntfID := IntfIDToPortNo(PortNum, voltha.Port_PON_OLT)
252 PONID := PortNoToIntfID(IntfID, voltha.Port_PON_OLT)
253 log.Debugf("PonID %v", PONID)
Abhilash S.L765ad002019-04-24 16:40:57 +0530254 return NewPONPort(PONID, DeviceID, IntfID, PortNum)
255 } else {
256 log.Errorf("Invalid type of interface %s", IntfType)
257 return nil
258 }
259}
260
Naga Manjunath7615e552019-10-11 22:35:47 +0530261// collectNNIMetrics will collect the nni port metrics
262func (StatMgr *OpenOltStatisticsMgr) collectNNIMetrics(nniID uint32) map[string]float32 {
263
264 nnival := make(map[string]float32)
265 mutex.Lock()
266 cm := StatMgr.Device.portStats.NorthBoundPort[nniID]
267 mutex.Unlock()
268 metricName := StatMgr.Device.metrics.GetSubscriberMetrics()
269
270 if metricName != nil && len(metricName) > 0 {
271 for mName := range metricName {
272 switch mName {
273 case "rx_bytes":
274 nnival["RxBytes"] = float32(cm.RxBytes)
275 case "rx_packets":
276 nnival["RxPackets"] = float32(cm.RxPackets)
277 case "rx_mcast_packets":
278 nnival["RxMcastPackets"] = float32(cm.RxMcastPackets)
279 case "rx_bcast_packets":
280 nnival["RxBcastPackets"] = float32(cm.RxBcastPackets)
281 case "tx_bytes":
282 nnival["TxBytes"] = float32(cm.TxBytes)
283 case "tx_packets":
284 nnival["TxPackets"] = float32(cm.TxPackets)
285 case "tx_mcast_packets":
286 nnival["TxMcastPackets"] = float32(cm.TxMcastPackets)
287 case "tx_bcast_packets":
288 nnival["TxBcastPackets"] = float32(cm.TxBcastPackets)
289 }
290 }
291 }
292 return nnival
293}
294
295// collectPONMetrics will collect the pon port metrics
296func (StatMgr *OpenOltStatisticsMgr) collectPONMetrics(pID uint32) map[string]float32 {
297
298 ponval := make(map[string]float32)
299 mutex.Lock()
300 cm := StatMgr.Device.portStats.SouthBoundPort[pID]
301 mutex.Unlock()
302 metricName := StatMgr.Device.metrics.GetSubscriberMetrics()
303
304 if metricName != nil && len(metricName) > 0 {
305 for mName := range metricName {
306 switch mName {
307 case "rx_bytes":
308 ponval["RxBytes"] = float32(cm.RxBytes)
309 case "rx_packets":
310 ponval["RxPackets"] = float32(cm.RxPackets)
311 case "rx_mcast_packets":
312 ponval["RxMcastPackets"] = float32(cm.RxMcastPackets)
313 case "rx_bcast_packets":
314 ponval["RxBcastPackets"] = float32(cm.RxBcastPackets)
315 case "tx_bytes":
316 ponval["TxBytes"] = float32(cm.TxBytes)
317 case "tx_packets":
318 ponval["TxPackets"] = float32(cm.TxPackets)
319 case "tx_mcast_packets":
320 ponval["TxMcastPackets"] = float32(cm.TxMcastPackets)
321 case "tx_bcast_packets":
322 ponval["TxBcastPackets"] = float32(cm.TxBcastPackets)
323 }
324 }
325 }
326 return ponval
327}
328
329// publishMatrics will publish the pon port metrics
330func (StatMgr OpenOltStatisticsMgr) publishMetrics(portType string, val map[string]float32, portnum uint32, context map[string]string, devID string) {
331 log.Debugf("Post-%v %v", portType, val)
332
333 var metricInfo voltha.MetricInformation
334 var ke voltha.KpiEvent2
335 var volthaEventSubCatgry voltha.EventSubCategory_EventSubCategory
336
337 if portType == "NNIStats" {
338 volthaEventSubCatgry = voltha.EventSubCategory_NNI
339 } else {
340 volthaEventSubCatgry = voltha.EventSubCategory_PON
341 }
342
343 raisedTs := time.Now().UnixNano()
344 mmd := voltha.MetricMetaData{
345 Title: portType,
346 Ts: float64(raisedTs),
347 Context: context,
348 DeviceId: devID,
349 }
350
351 metricInfo.Metadata = &mmd
352 metricInfo.Metrics = val
353
354 ke.SliceData = []*voltha.MetricInformation{&metricInfo}
355 ke.Type = voltha.KpiEventType_slice
356 ke.Ts = float64(time.Now().UnixNano())
357
358 if err := StatMgr.Device.EventProxy.SendKpiEvent("STATS_EVENT", &ke, voltha.EventCategory_EQUIPMENT, volthaEventSubCatgry, raisedTs); err != nil {
359 log.Errorw("Failed to send Pon stats", log.Fields{"err": err})
360 }
361
362}
363
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700364// PortStatisticsIndication handles the port statistics indication
Naga Manjunath7615e552019-10-11 22:35:47 +0530365func (StatMgr *OpenOltStatisticsMgr) PortStatisticsIndication(PortStats *openolt.PortStatistics, NumPonPorts uint32) {
Abhilash S.L765ad002019-04-24 16:40:57 +0530366 log.Debugf("port-stats-collected %v", PortStats)
Naga Manjunath7615e552019-10-11 22:35:47 +0530367 StatMgr.PortsStatisticsKpis(PortStats, NumPonPorts)
368 log.Infow("Received port stats indication", log.Fields{"PortStats": PortStats})
Abhilash S.L765ad002019-04-24 16:40:57 +0530369 // TODO send stats to core topic to the voltha kafka or a different kafka ?
370}
371
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700372// FlowStatisticsIndication to be implemented
Abhilash S.L765ad002019-04-24 16:40:57 +0530373func FlowStatisticsIndication(self, FlowStats *openolt.FlowStatistics) {
374 log.Debugf("flow-stats-collected %v", FlowStats)
375 //TODO send to kafka ?
376}
377
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700378// PortsStatisticsKpis map the port stats values into a dictionary, creates the kpiEvent and then publish to Kafka
Naga Manjunath7615e552019-10-11 22:35:47 +0530379func (StatMgr *OpenOltStatisticsMgr) PortsStatisticsKpis(PortStats *openolt.PortStatistics, NumPonPorts uint32) {
Abhilash S.L765ad002019-04-24 16:40:57 +0530380
381 /*map the port stats values into a dictionary
382 Create a kpoEvent and publish to Kafka
383
384 :param port_stats:
385 :return:
386 */
387 //var err error
388 IntfID := PortStats.IntfId
389
Naga Manjunath7615e552019-10-11 22:35:47 +0530390 if (IntfIDToPortNo(1, voltha.Port_ETHERNET_NNI) < IntfID) &&
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700391 (IntfID < IntfIDToPortNo(4, voltha.Port_ETHERNET_NNI)) {
Abhilash S.L765ad002019-04-24 16:40:57 +0530392 /*
393 for this release we are only interested in the first NNI for
394 Northbound.
395 we are not using the other 3
396 */
397 return
Naga Manjunath7615e552019-10-11 22:35:47 +0530398 } else if IntfIDToPortNo(0, voltha.Port_ETHERNET_NNI) == IntfID {
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700399
Naga Manjunath7615e552019-10-11 22:35:47 +0530400 var portNNIStat NniPort
401 portNNIStat.IntfID = IntfID
402 portNNIStat.PortNum = uint32(0)
403 portNNIStat.RxBytes = PortStats.RxBytes
404 portNNIStat.RxPackets = PortStats.RxPackets
405 portNNIStat.RxMcastPackets = PortStats.RxMcastPackets
406 portNNIStat.RxBcastPackets = PortStats.RxBcastPackets
407 portNNIStat.TxBytes = PortStats.TxBytes
408 portNNIStat.TxPackets = PortStats.TxPackets
409 portNNIStat.TxMcastPackets = PortStats.TxMcastPackets
410 portNNIStat.TxBcastPackets = PortStats.TxBcastPackets
411 mutex.Lock()
412 StatMgr.NorthBoundPort[0] = &portNNIStat
413 mutex.Unlock()
414 log.Debugf("Received-NNI-Stats: %v", StatMgr.NorthBoundPort)
415 }
416 for i := uint32(0); i < NumPonPorts; i++ {
417
418 if IntfIDToPortNo(i, voltha.Port_PON_OLT) == IntfID {
419 var portPonStat PonPort
420 portPonStat.IntfID = IntfID
421 portPonStat.PortNum = i
422 portPonStat.PONID = i
423 portPonStat.RxBytes = PortStats.RxBytes
424 portPonStat.RxPackets = PortStats.RxPackets
425 portPonStat.RxMcastPackets = PortStats.RxMcastPackets
426 portPonStat.RxBcastPackets = PortStats.RxBcastPackets
427 portPonStat.TxBytes = PortStats.TxBytes
428 portPonStat.TxPackets = PortStats.TxPackets
429 portPonStat.TxMcastPackets = PortStats.TxMcastPackets
430 portPonStat.TxBcastPackets = PortStats.TxBcastPackets
431 mutex.Lock()
432 StatMgr.SouthBoundPort[i] = &portPonStat
433 mutex.Unlock()
434 log.Debugf("Received-PON-Stats-for-Port %v : %v", i, StatMgr.SouthBoundPort[i])
435 }
436 }
Girish Gowdru6a80bbd2019-07-02 07:36:09 -0700437
438 /*
439 Based upon the intf_id map to an nni port or a pon port
440 the intf_id is the key to the north or south bound collections
441
442 Based upon the intf_id the port object (nni_port or pon_port) will
443 have its data attr. updated by the current dataset collected.
444
445 For prefixing the rule is currently to use the port number and not the intf_id
446 */
447 //FIXME : Just use first NNI for now
448 /* TODO should the data be marshaled before sending it ?
449 if IntfID == IntfIdToPortNo(0, voltha.Port_ETHERNET_NNI) {
450 //NNI port (just the first one)
451 err = UpdatePortObjectKpiData(StatMgr.NorthBoundPorts[PortStats.IntfID], PMData)
452 } else {
453 //PON ports
454 err = UpdatePortObjectKpiData(SouthboundPorts[PortStats.IntfID], PMData)
455 }
456 if (err != nil) {
457 log.Error("Error publishing statistics data")
458 }
459 */
460
Abhilash S.L765ad002019-04-24 16:40:57 +0530461}