blob: d8fa663ac952303f04c6ab2ef88a9240e45f6cdc [file] [log] [blame]
Takahiro Suzukid7bf8202020-12-17 20:21:59 +09001/*
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 */
16
17//Package core provides the utility for olt devices, flows and statistics
18package core
19
20import (
21 "context"
22 "fmt"
23 "strconv"
24 "sync"
25 "time"
26
27 "github.com/opencord/voltha-lib-go/v3/pkg/log"
28 "github.com/opencord/voltha-openolt-adapter/internal/pkg/olterrors"
29 "github.com/opencord/voltha-protos/v3/go/openolt"
30 "github.com/opencord/voltha-protos/v3/go/voltha"
31)
32
33var mutex = &sync.Mutex{}
34
35// PonPort representation
36type PonPort struct {
37 /*
38 This is a highly reduced version taken from the adtran pon_port.
39 TODO: Extend for use in the openolt adapter set.
40 */
41 /* MAX_ONUS_SUPPORTED = 256
42 DEFAULT_ENABLED = False
43 MAX_DEPLOYMENT_RANGE = 25000 # Meters (OLT-PB maximum)
44
45 _MCAST_ONU_ID = 253
46 _MCAST_ALLOC_BASE = 0x500
47
48 _SUPPORTED_ACTIVATION_METHODS = ['autodiscovery'] # , 'autoactivate']
49 _SUPPORTED_AUTHENTICATION_METHODS = ['serial-number']
50 */
51 PONID uint32
52 DeviceID string
53 IntfID uint32
54 PortNum uint32
55 PortID uint32
56 Label string
57 ONUs map[uint32]interface{}
58 ONUsByID map[uint32]interface{}
59
60 RxBytes uint64
61 RxPackets uint64
62 RxUcastPackets uint64
63 RxMcastPackets uint64
64 RxBcastPackets uint64
65 RxErrorPackets uint64
66 TxBytes uint64
67 TxPackets uint64
68 TxUcastPackets uint64
69 TxMcastPackets uint64
70 TxBcastPackets uint64
71 TxErrorPackets uint64
72 RxCrcErrors uint64
73 BipErrors uint64
74}
75
76// NewPONPort returns a new instance of PonPort initialized with given PONID, DeviceID, IntfID and PortNum
77func NewPONPort(PONID uint32, DeviceID string, IntfID uint32, PortNum uint32) *PonPort {
78
79 var PON PonPort
80
81 PON.PONID = PONID
82 PON.DeviceID = DeviceID
83 PON.IntfID = IntfID
84 PON.PortNum = PortNum
85 PON.PortID = 0
86 PON.Label = fmt.Sprintf("%s%d", "pon-", PONID)
87
88 PON.ONUs = make(map[uint32]interface{})
89 PON.ONUsByID = make(map[uint32]interface{})
90
91 /*
92 Statistics taken from nni_port
93 self.intf_id = 0 #handled by getter
94 self.port_no = 0 #handled by getter
95 self.port_id = 0 #handled by getter
96
97 Note: In the current implementation of the kpis coming from the BAL the stats are the
98 samne model for NNI and PON.
99
100 TODO: Integrate additional kpis for the PON and other southbound port objecgts.
101
102 */
103
104 PON.RxBytes = 0
105 PON.RxPackets = 0
106 PON.RxUcastPackets = 0
107 PON.RxMcastPackets = 0
108 PON.RxBcastPackets = 0
109 PON.RxErrorPackets = 0
110 PON.TxBytes = 0
111 PON.TxPackets = 0
112 PON.TxUcastPackets = 0
113 PON.TxMcastPackets = 0
114 PON.TxBcastPackets = 0
115 PON.TxErrorPackets = 0
116 PON.RxCrcErrors = 0
117 PON.BipErrors = 0
118
119 /* def __str__(self):
120 return "PonPort-{}: Admin: {}, Oper: {}, OLT: {}".format(self._label,
121 self._admin_state,
122 self._oper_status,
123 self.olt)
124 */
125 return &PON
126}
127
128// NniPort representation
129type NniPort struct {
130 /*
131 Northbound network port, often Ethernet-based
132
133 This is a highly reduced version taken from the adtran nni_port code set
134 TODO: add functions to allow for port specific values and operations
135 */
136 PortNum uint32
137 Name string
138 LogicalPort uint32
139 IntfID uint32
140
141 RxBytes uint64
142 RxPackets uint64
143 RxUcastPackets uint64
144 RxMcastPackets uint64
145 RxBcastPackets uint64
146 RxErrorPackets uint64
147 TxBytes uint64
148 TxPackets uint64
149 TxUcastPackets uint64
150 TxMcastPackets uint64
151 TxBcastPackets uint64
152 TxErrorPackets uint64
153 RxCrcErrors uint64
154 BipErrors uint64
155}
156
157// NewNniPort returns a new instance of NniPort initialized with the given PortNum and IntfID
158func NewNniPort(PortNum uint32, IntfID uint32) *NniPort {
159
160 var NNI NniPort
161
162 NNI.PortNum = PortNum
163 NNI.Name = fmt.Sprintf("%s%d", "nni-", PortNum)
164 NNI.IntfID = IntfID
165
166 NNI.RxBytes = 0
167 NNI.RxPackets = 0
168 NNI.RxUcastPackets = 0
169 NNI.RxMcastPackets = 0
170 NNI.RxBcastPackets = 0
171 NNI.RxErrorPackets = 0
172 NNI.TxBytes = 0
173 NNI.TxPackets = 0
174 NNI.TxUcastPackets = 0
175 NNI.TxMcastPackets = 0
176 NNI.TxBcastPackets = 0
177 NNI.TxErrorPackets = 0
178 NNI.RxCrcErrors = 0
179 NNI.BipErrors = 0
180
181 return &NNI
182}
183
184// OpenOltStatisticsMgr structure
185type OpenOltStatisticsMgr struct {
186 Device *DeviceHandler
187 NorthBoundPort map[uint32]*NniPort
188 SouthBoundPort map[uint32]*PonPort
189}
190
191// NewOpenOltStatsMgr returns a new instance of the OpenOltStatisticsMgr
192func NewOpenOltStatsMgr(ctx context.Context, Dev *DeviceHandler) *OpenOltStatisticsMgr {
193
194 var StatMgr OpenOltStatisticsMgr
195
196 StatMgr.Device = Dev
197 var Ports interface{}
198 Ports, _ = InitPorts(ctx, "nni", Dev.device.Id, 1)
199 StatMgr.NorthBoundPort, _ = Ports.(map[uint32]*NniPort)
200 NumPonPorts := Dev.resourceMgr.DevInfo.GetPonPorts()
201 Ports, _ = InitPorts(ctx, "pon", Dev.device.Id, NumPonPorts)
202 StatMgr.SouthBoundPort, _ = Ports.(map[uint32]*PonPort)
203 return &StatMgr
204}
205
206// InitPorts collects the port objects: nni and pon that are updated with the current data from the OLT
207func InitPorts(ctx context.Context, Intftype string, DeviceID string, numOfPorts uint32) (interface{}, error) {
208 /*
209 This method collects the port objects: nni and pon that are updated with the
210 current data from the OLT
211
212 Both the northbound (nni) and southbound ports are indexed by the interface id (intf_id)
213 and NOT the port number. When the port object is instantiated it will contain the intf_id and
214 port_no values
215
216 :param type:
217 :return:
218 */
219 var i uint32
220 if Intftype == "nni" {
221 NniPorts := make(map[uint32]*NniPort)
222 for i = 0; i < numOfPorts; i++ {
223 Port := BuildPortObject(ctx, i, "nni", DeviceID).(*NniPort)
224 NniPorts[Port.IntfID] = Port
225 }
226 return NniPorts, nil
227 } else if Intftype == "pon" {
228 PONPorts := make(map[uint32]*PonPort)
229 for i = 0; i < numOfPorts; i++ {
230 PONPort := BuildPortObject(ctx, i, "pon", DeviceID).(*PonPort)
231 PONPorts[PortNoToIntfID(PONPort.IntfID, voltha.Port_PON_OLT)] = PONPort
232 }
233 return PONPorts, nil
234 } else {
235 logger.Errorw(ctx, "invalid-type-of-interface", log.Fields{"interface-type": Intftype})
236 return nil, olterrors.NewErrInvalidValue(log.Fields{"interface-type": Intftype}, nil)
237 }
238}
239
240// BuildPortObject allows for updating north and southbound ports, newly discovered ports, and devices
241func BuildPortObject(ctx context.Context, PortNum uint32, IntfType string, DeviceID string) interface{} {
242 /*
243 Separate method to allow for updating north and southbound ports
244 newly discovered ports and devices
245
246 :param port_num:
247 :param type:
248 :return:
249 */
250
251 if IntfType == "nni" {
252 IntfID := IntfIDToPortNo(PortNum, voltha.Port_ETHERNET_NNI)
253 nniID := PortNoToIntfID(IntfID, voltha.Port_ETHERNET_NNI)
254 logger.Debugw(ctx, "interface-type-nni",
255 log.Fields{
256 "nni-id": nniID,
257 "intf-type": IntfType})
258 return NewNniPort(PortNum, nniID)
259 } else if IntfType == "pon" {
260 // PON ports require a different configuration
261 // intf_id and pon_id are currently equal.
262 IntfID := IntfIDToPortNo(PortNum, voltha.Port_PON_OLT)
263 PONID := PortNoToIntfID(IntfID, voltha.Port_PON_OLT)
264 logger.Debugw(ctx, "interface-type-pon",
265 log.Fields{
266 "pon-id": PONID,
267 "intf-type": IntfType})
268 return NewPONPort(PONID, DeviceID, IntfID, PortNum)
269 } else {
270 logger.Errorw(ctx, "invalid-type-of-interface", log.Fields{"intf-type": IntfType})
271 return nil
272 }
273}
274
275// collectNNIMetrics will collect the nni port metrics
276func (StatMgr *OpenOltStatisticsMgr) collectNNIMetrics(nniID uint32) map[string]float32 {
277
278 nnival := make(map[string]float32)
279 mutex.Lock()
280 cm := StatMgr.Device.portStats.NorthBoundPort[nniID]
281 mutex.Unlock()
282 metricNames := StatMgr.Device.metrics.GetSubscriberMetrics()
283
284 var metrics []string
285 for metric := range metricNames {
286 if metricNames[metric].Enabled {
287 metrics = append(metrics, metric)
288 }
289 }
290
291 for _, mName := range metrics {
292 switch mName {
293 case "rx_bytes":
294 nnival["RxBytes"] = float32(cm.RxBytes)
295 case "rx_packets":
296 nnival["RxPackets"] = float32(cm.RxPackets)
297 case "rx_ucast_packets":
298 nnival["RxUcastPackets"] = float32(cm.RxUcastPackets)
299 case "rx_mcast_packets":
300 nnival["RxMcastPackets"] = float32(cm.RxMcastPackets)
301 case "rx_bcast_packets":
302 nnival["RxBcastPackets"] = float32(cm.RxBcastPackets)
303 case "tx_bytes":
304 nnival["TxBytes"] = float32(cm.TxBytes)
305 case "tx_packets":
306 nnival["TxPackets"] = float32(cm.TxPackets)
307 case "tx_mcast_packets":
308 nnival["TxMcastPackets"] = float32(cm.TxMcastPackets)
309 case "tx_bcast_packets":
310 nnival["TxBcastPackets"] = float32(cm.TxBcastPackets)
311 }
312 }
313 return nnival
314}
315
316// collectPONMetrics will collect the pon port metrics
317func (StatMgr *OpenOltStatisticsMgr) collectPONMetrics(pID uint32) map[string]float32 {
318
319 ponval := make(map[string]float32)
320 mutex.Lock()
321 cm := StatMgr.Device.portStats.SouthBoundPort[pID]
322 mutex.Unlock()
323 metricNames := StatMgr.Device.metrics.GetSubscriberMetrics()
324
325 var metrics []string
326 for metric := range metricNames {
327 if metricNames[metric].Enabled {
328 metrics = append(metrics, metric)
329 }
330 }
331
332 for _, mName := range metrics {
333 switch mName {
334 case "rx_bytes":
335 ponval["RxBytes"] = float32(cm.RxBytes)
336 case "rx_packets":
337 ponval["RxPackets"] = float32(cm.RxPackets)
338 case "rx_ucast_packets":
339 ponval["RxUcastPackets"] = float32(cm.RxUcastPackets)
340 case "rx_mcast_packets":
341 ponval["RxMcastPackets"] = float32(cm.RxMcastPackets)
342 case "rx_bcast_packets":
343 ponval["RxBcastPackets"] = float32(cm.RxBcastPackets)
344 case "tx_bytes":
345 ponval["TxBytes"] = float32(cm.TxBytes)
346 case "tx_packets":
347 ponval["TxPackets"] = float32(cm.TxPackets)
348 case "tx_mcast_packets":
349 ponval["TxMcastPackets"] = float32(cm.TxMcastPackets)
350 case "tx_bcast_packets":
351 ponval["TxBcastPackets"] = float32(cm.TxBcastPackets)
352 }
353 }
354
355 return ponval
356}
357
358// publishMatrics will publish the pon port metrics
359func (StatMgr OpenOltStatisticsMgr) publishMetrics(ctx context.Context, val map[string]float32,
360 port *voltha.Port, devID string, devType string) {
361 logger.Debugw(ctx, "publish-metrics",
362 log.Fields{
363 "port": port.Label,
364 "metrics": val})
365
366 var metricInfo voltha.MetricInformation
367 var ke voltha.KpiEvent2
368 var volthaEventSubCatgry voltha.EventSubCategory_Types
369 metricsContext := make(map[string]string)
370 metricsContext["oltid"] = devID
371 metricsContext["devicetype"] = devType
372 metricsContext["portlabel"] = port.Label
373 metricsContext["portno"] = strconv.Itoa(int(port.PortNo))
374
375 if port.Type == voltha.Port_ETHERNET_NNI {
376 volthaEventSubCatgry = voltha.EventSubCategory_NNI
377 } else {
378 volthaEventSubCatgry = voltha.EventSubCategory_PON
379 }
380
381 raisedTs := time.Now().UnixNano()
382 mmd := voltha.MetricMetaData{
383 Title: port.Type.String(),
384 Ts: float64(raisedTs),
385 Context: metricsContext,
386 DeviceId: devID,
387 }
388
389 metricInfo.Metadata = &mmd
390 metricInfo.Metrics = val
391
392 ke.SliceData = []*voltha.MetricInformation{&metricInfo}
393 ke.Type = voltha.KpiEventType_slice
394 ke.Ts = float64(time.Now().UnixNano())
395
396 if err := StatMgr.Device.EventProxy.SendKpiEvent(ctx, "STATS_EVENT", &ke, voltha.EventCategory_EQUIPMENT, volthaEventSubCatgry, raisedTs); err != nil {
397 logger.Errorw(ctx, "failed-to-send-pon-stats", log.Fields{"err": err})
398 }
399}
400
401// PortStatisticsIndication handles the port statistics indication
402func (StatMgr *OpenOltStatisticsMgr) PortStatisticsIndication(ctx context.Context, PortStats *openolt.PortStatistics, NumPonPorts uint32) {
403 StatMgr.PortsStatisticsKpis(ctx, PortStats, NumPonPorts)
404 logger.Debugw(ctx, "received-port-stats-indication", log.Fields{"port-stats": PortStats})
405}
406
407// FlowStatisticsIndication to be implemented
408func FlowStatisticsIndication(ctx context.Context, self, FlowStats *openolt.FlowStatistics) {
409 logger.Debugw(ctx, "flow-stats-collected", log.Fields{"flow-stats": FlowStats})
410}
411
412// PortsStatisticsKpis map the port stats values into a dictionary, creates the kpiEvent and then publish to Kafka
413func (StatMgr *OpenOltStatisticsMgr) PortsStatisticsKpis(ctx context.Context, PortStats *openolt.PortStatistics, NumPonPorts uint32) {
414
415 /*map the port stats values into a dictionary
416 Create a kpoEvent and publish to Kafka
417
418 :param port_stats:
419 :return:
420 */
421 IntfID := PortStats.IntfId
422
423 if (IntfIDToPortNo(1, voltha.Port_ETHERNET_NNI) < IntfID) &&
424 (IntfID < IntfIDToPortNo(4, voltha.Port_ETHERNET_NNI)) {
425 /*
426 for this release we are only interested in the first NNI for
427 Northbound.
428 we are not using the other 3
429 */
430 return
431 } else if IntfIDToPortNo(0, voltha.Port_ETHERNET_NNI) == IntfID {
432
433 var portNNIStat NniPort
434 portNNIStat.IntfID = IntfID
435 portNNIStat.PortNum = uint32(0)
436 portNNIStat.RxBytes = PortStats.RxBytes
437 portNNIStat.RxPackets = PortStats.RxPackets
438 portNNIStat.RxUcastPackets = PortStats.RxUcastPackets
439 portNNIStat.RxMcastPackets = PortStats.RxMcastPackets
440 portNNIStat.RxBcastPackets = PortStats.RxBcastPackets
441 portNNIStat.TxBytes = PortStats.TxBytes
442 portNNIStat.TxPackets = PortStats.TxPackets
443 portNNIStat.TxUcastPackets = PortStats.TxUcastPackets
444 portNNIStat.TxMcastPackets = PortStats.TxMcastPackets
445 portNNIStat.TxBcastPackets = PortStats.TxBcastPackets
446 mutex.Lock()
447 StatMgr.NorthBoundPort[0] = &portNNIStat
448 mutex.Unlock()
449 logger.Debugw(ctx, "received-nni-stats", log.Fields{"nni-stats": StatMgr.NorthBoundPort})
450 }
451 for i := uint32(0); i < NumPonPorts; i++ {
452
453 if IntfIDToPortNo(i, voltha.Port_PON_OLT) == IntfID {
454 var portPonStat PonPort
455 portPonStat.IntfID = IntfID
456 portPonStat.PortNum = i
457 portPonStat.PONID = i
458 portPonStat.RxBytes = PortStats.RxBytes
459 portPonStat.RxPackets = PortStats.RxPackets
460 portPonStat.RxUcastPackets = PortStats.RxUcastPackets
461 portPonStat.RxMcastPackets = PortStats.RxMcastPackets
462 portPonStat.RxBcastPackets = PortStats.RxBcastPackets
463 portPonStat.TxBytes = PortStats.TxBytes
464 portPonStat.TxPackets = PortStats.TxPackets
465 portPonStat.TxUcastPackets = PortStats.TxUcastPackets
466 portPonStat.TxMcastPackets = PortStats.TxMcastPackets
467 portPonStat.TxBcastPackets = PortStats.TxBcastPackets
468 mutex.Lock()
469 StatMgr.SouthBoundPort[i] = &portPonStat
470 mutex.Unlock()
471 logger.Debugw(ctx, "received-pon-stats-for-port", log.Fields{"port-pon-stats": portPonStat})
472 }
473 }
474
475 /*
476 Based upon the intf_id map to an nni port or a pon port
477 the intf_id is the key to the north or south bound collections
478
479 Based upon the intf_id the port object (nni_port or pon_port) will
480 have its data attr. updated by the current dataset collected.
481
482 For prefixing the rule is currently to use the port number and not the intf_id
483 */
484 /* TODO should the data be marshaled before sending it ?
485 if IntfID == IntfIdToPortNo(0, voltha.Port_ETHERNET_NNI) {
486 //NNI port (just the first one)
487 err = UpdatePortObjectKpiData(StatMgr.NorthBoundPorts[PortStats.IntfID], PMData)
488 } else {
489 //PON ports
490 err = UpdatePortObjectKpiData(SouthboundPorts[PortStats.IntfID], PMData)
491 }
492 if (err != nil) {
493 logger.Error(ctx, "Error publishing statistics data")
494 }
495 */
496
497}