blob: 623e26146b48315ae10a5ef8398547e104b40584 [file] [log] [blame]
Elia Battistone1cecb22022-03-21 10:05:25 +01001/*
2* Copyright 2022-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
17package core
18
19import (
20 "fmt"
Elia Battistona1333642022-07-27 12:17:24 +000021 "strconv"
Elia Battiston4750d3c2022-07-14 13:24:56 +000022 "time"
Elia Battistone1cecb22022-03-21 10:05:25 +010023
Elia Battistona1333642022-07-27 12:17:24 +000024 "github.com/opencord/voltha-northbound-bbf-adapter/internal/clients"
Elia Battiston589addb2022-04-04 16:40:01 +020025 "github.com/opencord/voltha-protos/v5/go/common"
Elia Battistone1cecb22022-03-21 10:05:25 +010026 "github.com/opencord/voltha-protos/v5/go/voltha"
27)
28
29const (
Elia Battiston4750d3c2022-07-14 13:24:56 +000030 DeviceAggregationModule = "bbf-device-aggregation"
31 DevicesPath = "/" + DeviceAggregationModule + ":devices"
Elia Battiston589addb2022-04-04 16:40:01 +020032
Elia Battistona1333642022-07-27 12:17:24 +000033 ServiceProfileModule = "bbf-nt-service-profile"
34 ServiceProfilesPath = "/" + ServiceProfileModule + ":service-profiles"
35
36 VlansModule = "bbf-l2-access-attributes"
37 VlansPath = "/" + VlansModule + ":vlan-translation-profiles"
38
39 BandwidthProfileModule = "bbf-nt-line-profile"
40 BandwidthProfilesPath = "/" + BandwidthProfileModule + ":line-bandwidth-profiles"
41
Elia Battiston589addb2022-04-04 16:40:01 +020042 //Device types
43 DeviceTypeOlt = "bbf-device-types:olt"
44 DeviceTypeOnu = "bbf-device-types:onu"
45
46 //Admin states
47 ietfAdminStateUnknown = "unknown"
48 ietfAdminStateLocked = "locked"
49 ietfAdminStateUnlocked = "unlocked"
50
51 //Oper states
52 ietfOperStateUnknown = "unknown"
53 ietfOperStateDisabled = "disabled"
54 ietfOperStateEnabled = "enabled"
55 ietfOperStateTesting = "testing"
Elia Battiston4750d3c2022-07-14 13:24:56 +000056 ietfOperStateUp = "up"
57 ietfOperStateDown = "down"
58
59 //Keys of useful values in device events
60 eventContextKeyPonId = "pon-id"
61 eventContextKeyOnuSn = "serial-number"
62 eventContextKeyOltSn = "olt-serial-number"
Elia Battistona1333642022-07-27 12:17:24 +000063
64 //Values to allow any VLAN ID
65 YangVlanIdAny = "any"
66 VolthaVlanIdAny = 4096
Elia Battistone1cecb22022-03-21 10:05:25 +010067)
68
69type YangItem struct {
70 Path string
71 Value string
72}
73
74//getDevicePath returns the yang path to the root of the device with a specific ID
75func getDevicePath(id string) string {
76 return fmt.Sprintf("%s/device[name='%s']", DevicesPath, id)
77}
78
Elia Battiston589addb2022-04-04 16:40:01 +020079//getDevicePath returns the yang path to the root of the device's hardware module in its data mountpoint
80func getDeviceHardwarePath(id string) string {
Elia Battiston4750d3c2022-07-14 13:24:56 +000081 return fmt.Sprintf("%s/device[name='%s']/data/ietf-hardware:hardware/component[name='%s']", DevicesPath, id, id)
Elia Battiston589addb2022-04-04 16:40:01 +020082}
83
Elia Battistona1333642022-07-27 12:17:24 +000084//GetServicePortPath returns the yang path to a service's port node
85func GetServicePortPath(serviceName string, portName string) string {
86 return fmt.Sprintf("%s/service-profile[name='%s']/ports/port[name='%s']", ServiceProfilesPath, serviceName, portName)
87}
88
89//GetVlansPath returns the yang path to a vlan translation profile's root node
90func GetVlansPath(serviceName string) string {
91 return fmt.Sprintf("%s/vlan-translation-profile[name='%s']", VlansPath, serviceName)
92}
93
Elia Battiston589addb2022-04-04 16:40:01 +020094//ietfHardwareAdminState returns the string that represents the ietf-hardware admin state
95//enum value corresponding to the one of VOLTHA
96func ietfHardwareAdminState(volthaAdminState voltha.AdminState_Types) string {
97 //TODO: verify this mapping is correct
98 switch volthaAdminState {
99 case common.AdminState_UNKNOWN:
100 return ietfAdminStateUnknown
101 case common.AdminState_PREPROVISIONED:
102 case common.AdminState_DOWNLOADING_IMAGE:
103 case common.AdminState_ENABLED:
104 return ietfAdminStateUnlocked
105 case common.AdminState_DISABLED:
106 return ietfAdminStateLocked
107 }
108
109 //TODO: does something map to "shutting-down" ?
110
111 return ietfAdminStateUnknown
112}
113
114//ietfHardwareOperState returns the string that represents the ietf-hardware oper state
115//enum value corresponding to the one of VOLTHA
116func ietfHardwareOperState(volthaOperState voltha.OperStatus_Types) string {
117 //TODO: verify this mapping is correct
118 switch volthaOperState {
119 case common.OperStatus_UNKNOWN:
120 return ietfOperStateUnknown
121 case common.OperStatus_TESTING:
122 return ietfOperStateTesting
123 case common.OperStatus_ACTIVE:
124 return ietfOperStateEnabled
125 case common.OperStatus_DISCOVERED:
126 case common.OperStatus_ACTIVATING:
127 case common.OperStatus_FAILED:
128 case common.OperStatus_RECONCILING:
129 case common.OperStatus_RECONCILING_FAILED:
130 return ietfOperStateDisabled
131 }
132
133 return ietfOperStateUnknown
134}
135
Elia Battiston4750d3c2022-07-14 13:24:56 +0000136//ietfHardwareOperState returns the string that represents the ietf-interfaces oper state
137//enum value corresponding to the one of VOLTHA
138func ietfInterfacesOperState(volthaOperState voltha.OperStatus_Types) string {
139 //TODO: verify this mapping is correct
140 switch volthaOperState {
141 case common.OperStatus_UNKNOWN:
142 return ietfOperStateUnknown
143 case common.OperStatus_TESTING:
144 return ietfOperStateTesting
145 case common.OperStatus_ACTIVE:
146 return ietfOperStateUp
147 case common.OperStatus_DISCOVERED:
148 case common.OperStatus_ACTIVATING:
149 case common.OperStatus_FAILED:
150 case common.OperStatus_RECONCILING:
151 case common.OperStatus_RECONCILING_FAILED:
152 return ietfOperStateDown
153 }
154
155 return ietfOperStateUnknown
156}
157
Elia Battistone1cecb22022-03-21 10:05:25 +0100158//translateDevice returns a slice of yang items that represent a voltha device
Elia Battiston4750d3c2022-07-14 13:24:56 +0000159func translateDevice(device *voltha.Device) []YangItem {
Elia Battistone1cecb22022-03-21 10:05:25 +0100160 devicePath := getDevicePath(device.Id)
Elia Battiston589addb2022-04-04 16:40:01 +0200161 hardwarePath := getDeviceHardwarePath(device.Id)
Elia Battistone1cecb22022-03-21 10:05:25 +0100162
Elia Battiston589addb2022-04-04 16:40:01 +0200163 result := []YangItem{}
Elia Battistone1cecb22022-03-21 10:05:25 +0100164
Elia Battiston589addb2022-04-04 16:40:01 +0200165 //Device type
Elia Battistone1cecb22022-03-21 10:05:25 +0100166 if device.Root {
Elia Battiston4750d3c2022-07-14 13:24:56 +0000167 //OLT
Elia Battiston589addb2022-04-04 16:40:01 +0200168 result = append(result, YangItem{
Elia Battiston4750d3c2022-07-14 13:24:56 +0000169 Path: devicePath + "/type",
Elia Battiston589addb2022-04-04 16:40:01 +0200170 Value: DeviceTypeOlt,
171 })
Elia Battistone1cecb22022-03-21 10:05:25 +0100172 } else {
Elia Battiston4750d3c2022-07-14 13:24:56 +0000173 //ONU
Elia Battistona1333642022-07-27 12:17:24 +0000174 result = append(result, []YangItem{
175 {
176 Path: devicePath + "/type",
177 Value: DeviceTypeOnu,
178 },
179 {
180 Path: hardwarePath + "/parent",
181 Value: device.ParentId,
182 },
183 {
184 Path: hardwarePath + "/parent-rel-pos",
185 Value: strconv.FormatUint(uint64(device.ParentPortNo), 10),
186 },
187 }...)
Elia Battistone1cecb22022-03-21 10:05:25 +0100188 }
189
Elia Battiston589addb2022-04-04 16:40:01 +0200190 //Vendor name
191 result = append(result, YangItem{
Elia Battiston4750d3c2022-07-14 13:24:56 +0000192 Path: hardwarePath + "/mfg-name",
Elia Battiston589addb2022-04-04 16:40:01 +0200193 Value: device.Vendor,
194 })
195
196 //Model
197 result = append(result, YangItem{
Elia Battiston4750d3c2022-07-14 13:24:56 +0000198 Path: hardwarePath + "/model-name",
Elia Battiston589addb2022-04-04 16:40:01 +0200199 Value: device.Model,
200 })
201
202 //Hardware version
203 result = append(result, YangItem{
Elia Battiston4750d3c2022-07-14 13:24:56 +0000204 Path: hardwarePath + "/hardware-rev",
Elia Battiston589addb2022-04-04 16:40:01 +0200205 Value: device.HardwareVersion,
206 })
207
208 //Firmware version
209 result = append(result, YangItem{
Elia Battiston4750d3c2022-07-14 13:24:56 +0000210 Path: hardwarePath + "/firmware-rev",
Elia Battiston589addb2022-04-04 16:40:01 +0200211 Value: device.FirmwareVersion,
212 })
213
214 //Serial number
215 result = append(result, YangItem{
Elia Battiston4750d3c2022-07-14 13:24:56 +0000216 Path: hardwarePath + "/serial-num",
Elia Battiston589addb2022-04-04 16:40:01 +0200217 Value: device.SerialNumber,
218 })
219
220 //Administrative state
221 //Translates VOLTHA admin state enum to ietf-hardware enum
222 result = append(result, YangItem{
Elia Battiston4750d3c2022-07-14 13:24:56 +0000223 Path: hardwarePath + "/state/admin-state",
Elia Battiston589addb2022-04-04 16:40:01 +0200224 Value: ietfHardwareAdminState(device.AdminState),
225 })
226
227 //Operative state
228 result = append(result, YangItem{
Elia Battiston4750d3c2022-07-14 13:24:56 +0000229 Path: hardwarePath + "/state/oper-state",
Elia Battiston589addb2022-04-04 16:40:01 +0200230 Value: ietfHardwareOperState(device.OperStatus),
231 })
232
233 return result
Elia Battistone1cecb22022-03-21 10:05:25 +0100234}
235
Elia Battiston4750d3c2022-07-14 13:24:56 +0000236//translateOnuPorts returns a slice of yang items that represent the UNIs of an ONU
237func translateOnuPorts(deviceId string, ports *voltha.Ports) ([]YangItem, error) {
238 interfacesPath := getDevicePath(deviceId) + "/data/ietf-interfaces:interfaces"
Elia Battistone1cecb22022-03-21 10:05:25 +0100239 result := []YangItem{}
240
Elia Battiston4750d3c2022-07-14 13:24:56 +0000241 for _, port := range ports.Items {
242 if port.Type == voltha.Port_ETHERNET_UNI {
243 if port.OfpPort == nil {
244 return nil, fmt.Errorf("no-ofp-port-in-uni: %s %d", deviceId, port.PortNo)
245 }
246
247 interfacePath := fmt.Sprintf("%s/interface[name='%s']", interfacesPath, port.OfpPort.Name)
248
249 result = append(result, []YangItem{
250 {
251 Path: interfacePath + "/type",
252 Value: "bbf-xpon-if-type:onu-v-vrefpoint",
253 },
254 {
255 Path: interfacePath + "/oper-status",
256 Value: ietfInterfacesOperState(port.OperStatus),
257 },
258 }...)
259 }
Elia Battistone1cecb22022-03-21 10:05:25 +0100260 }
261
Elia Battiston4750d3c2022-07-14 13:24:56 +0000262 return result, nil
263}
264
265//TranslateOnuActivatedEvent returns a slice of yang items and the name of the channel termination to populate
266//an ONU discovery notification with data from ONU_ACTIVATED_RAISE_EVENT coming from the Kafka bus
267func TranslateOnuActivatedEvent(eventHeader *voltha.EventHeader, deviceEvent *voltha.DeviceEvent) (notification []YangItem, channelTermination []YangItem, err error) {
268
269 //TODO: the use of this notification, which requires the creation of a dummy channel termination node,
270 //is temporary, and will be substituted with a more fitting one as soon as it will be defined
271
272 //Check if the needed information is present
273 ponId, ok := deviceEvent.Context[eventContextKeyPonId]
274 if !ok {
275 return nil, nil, fmt.Errorf("missing-key-from-event-context: %s", eventContextKeyPonId)
276 }
277 oltId, ok := deviceEvent.Context[eventContextKeyOltSn]
278 if !ok {
279 return nil, nil, fmt.Errorf("missing-key-from-event-context: %s", eventContextKeyPonId)
280 }
281 ponName := oltId + "-pon-" + ponId
282
283 onuSn, ok := deviceEvent.Context[eventContextKeyOnuSn]
284 if !ok {
285 return nil, nil, fmt.Errorf("missing-key-from-event-context: %s", eventContextKeyOnuSn)
286 }
287
288 notificationPath := "/bbf-xpon-onu-states:onu-state-change"
289
290 notification = []YangItem{
291 {
292 Path: notificationPath + "/detected-serial-number",
293 Value: onuSn,
294 },
295 {
296 Path: notificationPath + "/channel-termination-ref",
297 Value: ponName,
298 },
299 {
300 Path: notificationPath + "/onu-state-last-change",
301 Value: eventHeader.RaisedTs.AsTime().Format(time.RFC3339),
302 },
303 {
304 Path: notificationPath + "/onu-state",
305 Value: "bbf-xpon-onu-types:onu-present",
306 },
307 {
308 Path: notificationPath + "/detected-registration-id",
309 Value: deviceEvent.ResourceId,
310 },
311 }
312
313 channelTermination = []YangItem{
314 {
315 Path: fmt.Sprintf("/ietf-interfaces:interfaces/interface[name='%s']/type", ponName),
316 Value: "bbf-if-type:vlan-sub-interface",
317 },
318 }
319
320 return notification, channelTermination, nil
Elia Battistone1cecb22022-03-21 10:05:25 +0100321}
Elia Battistona1333642022-07-27 12:17:24 +0000322
323//translateServices returns a slice of yang items that represent the currently programmed services
324func translateServices(subscribers []clients.ProgrammedSubscriber, ports []clients.OnosPort) ([]YangItem, error) {
325 //Create a map of port IDs to port names
326 //e.g. of:00000a0a0a0a0a0a/256 to BBSM000a0001-1
327 portNames := map[string]string{}
328
329 for _, port := range ports {
330 portId := fmt.Sprintf("%s/%s", port.Element, port.Port)
331 name, ok := port.Annotations["portName"]
332 if ok {
333 portNames[portId] = name
334 }
335 }
336
337 result := []YangItem{}
338
339 for _, subscriber := range subscribers {
340 portName, ok := portNames[subscriber.Location]
341 if !ok {
342 return nil, fmt.Errorf("no-port-name-for-location: %s", subscriber.Location)
343 }
344
345 serviceName := fmt.Sprintf("%s-%s", portName, subscriber.TagInfo.ServiceName)
346
347 portPath := GetServicePortPath(serviceName, portName)
348
349 if subscriber.TagInfo.ConfiguredMacAddress != "" {
350 result = append(result, YangItem{
351 Path: portPath + "/bbf-nt-service-profile-voltha:configured-mac-address",
352 Value: subscriber.TagInfo.ConfiguredMacAddress,
353 })
354 }
355
356 result = append(result, []YangItem{
357 {
358 Path: fmt.Sprintf("%s/port-vlans/port-vlan[name='%s']", portPath, serviceName),
359 Value: "",
360 },
361 {
362 Path: portPath + "/bbf-nt-service-profile-voltha:technology-profile-id",
363 Value: strconv.Itoa(subscriber.TagInfo.TechnologyProfileID),
364 },
365 {
366 Path: portPath + "/bbf-nt-service-profile-voltha:downstream-subscriber-bp-name",
367 Value: subscriber.TagInfo.DownstreamBandwidthProfile,
368 },
369 {
370 Path: portPath + "/bbf-nt-service-profile-voltha:upstream-subscriber-bp-name",
371 Value: subscriber.TagInfo.UpstreamBandwidthProfile,
372 },
373 {
374 Path: portPath + "/bbf-nt-service-profile-voltha:mac-learning-enabled",
375 Value: strconv.FormatBool(subscriber.TagInfo.EnableMacLearning),
376 },
377 {
378 Path: portPath + "/bbf-nt-service-profile-voltha:dhcp-required",
379 Value: strconv.FormatBool(subscriber.TagInfo.IsDhcpRequired),
380 },
381 {
382 Path: portPath + "/bbf-nt-service-profile-voltha:igmp-required",
383 Value: strconv.FormatBool(subscriber.TagInfo.IsIgmpRequired),
384 },
385 {
386 Path: portPath + "/bbf-nt-service-profile-voltha:pppoe-required",
387 Value: strconv.FormatBool(subscriber.TagInfo.IsPPPoERequired),
388 },
389 }...)
390
391 if subscriber.TagInfo.UpstreamOltBandwidthProfile != "" {
392 result = append(result, YangItem{
393 Path: portPath + "/bbf-nt-service-profile-voltha:upstream-olt-bp-name",
394 Value: subscriber.TagInfo.UpstreamOltBandwidthProfile,
395 })
396 }
397
398 if subscriber.TagInfo.DownstreamOltBandwidthProfile != "" {
399 result = append(result, YangItem{
400 Path: portPath + "/bbf-nt-service-profile-voltha:downstream-olt-bp-name",
401 Value: subscriber.TagInfo.UpstreamOltBandwidthProfile,
402 })
403 }
404 }
405
406 return result, nil
407}
408
409//translateVlans returns a slice of yang items that represent the vlans used by programmed services
410func translateVlans(subscribers []clients.ProgrammedSubscriber, ports []clients.OnosPort) ([]YangItem, error) {
411 //Create a map of port IDs to port names
412 //e.g. of:00000a0a0a0a0a0a/256 to BBSM000a0001-1
413 portNames := map[string]string{}
414
415 for _, port := range ports {
416 portId := fmt.Sprintf("%s/%s", port.Element, port.Port)
417 name, ok := port.Annotations["portName"]
418 if ok {
419 portNames[portId] = name
420 }
421 }
422
423 result := []YangItem{}
424
425 for _, subscriber := range subscribers {
426 portName, ok := portNames[subscriber.Location]
427 if !ok {
428 return nil, fmt.Errorf("no-port-name-for-location: %s", subscriber.Location)
429 }
430
431 serviceName := fmt.Sprintf("%s-%s", portName, subscriber.TagInfo.ServiceName)
432
433 vlansPath := GetVlansPath(serviceName)
434
435 uniTagMatch := YangVlanIdAny
436 sTag := YangVlanIdAny
437 cTag := YangVlanIdAny
438
439 if subscriber.TagInfo.UniTagMatch != VolthaVlanIdAny {
440 uniTagMatch = strconv.Itoa(subscriber.TagInfo.UniTagMatch)
441 }
442 if subscriber.TagInfo.PonSTag != VolthaVlanIdAny {
443 sTag = strconv.Itoa(subscriber.TagInfo.PonSTag)
444 }
445 if subscriber.TagInfo.PonCTag != VolthaVlanIdAny {
446 cTag = strconv.Itoa(subscriber.TagInfo.PonCTag)
447 }
448
449 if subscriber.TagInfo.UniTagMatch > 0 {
450 result = append(result, []YangItem{
451 {
452 Path: vlansPath + "/match-criteria/outer-tag/vlan-id",
453 Value: uniTagMatch,
454 },
455 {
456 Path: vlansPath + "/match-criteria/second-tag/vlan-id",
457 Value: "any",
458 },
459 }...)
460 }
461
462 if subscriber.TagInfo.UsPonSTagPriority >= 0 {
463 result = append(result, YangItem{
464 Path: vlansPath + "/ingress-rewrite/push-outer-tag/pbit",
465 Value: strconv.Itoa(subscriber.TagInfo.UsPonSTagPriority),
466 })
467 }
468 if subscriber.TagInfo.DsPonSTagPriority >= 0 {
469 result = append(result, YangItem{
470 Path: vlansPath + "/ingress-rewrite/push-outer-tag/bbf-voltha-vlan-translation:dpbit",
471 Value: strconv.Itoa(subscriber.TagInfo.DsPonSTagPriority),
472 })
473 }
474 if subscriber.TagInfo.UsPonCTagPriority >= 0 {
475 result = append(result, YangItem{
476 Path: vlansPath + "/ingress-rewrite/push-second-tag/pbit",
477 Value: strconv.Itoa(subscriber.TagInfo.UsPonCTagPriority),
478 })
479 }
480 if subscriber.TagInfo.DsPonCTagPriority >= 0 {
481 result = append(result, YangItem{
482 Path: vlansPath + "/ingress-rewrite/push-second-tag/bbf-voltha-vlan-translation:dpbit",
483 Value: strconv.Itoa(subscriber.TagInfo.DsPonCTagPriority),
484 })
485 }
486
487 result = append(result, []YangItem{
488 {
489 Path: vlansPath + "/ingress-rewrite/push-outer-tag/vlan-id",
490 Value: sTag,
491 },
492 {
493 Path: vlansPath + "/ingress-rewrite/push-second-tag/vlan-id",
494 Value: cTag,
495 },
496 }...)
497 }
498
499 return result, nil
500}
501
502//translateBandwidthProfiles returns a slice of yang items that represent the bandwidth profiles used by programmed services
503func translateBandwidthProfiles(bwProfiles []clients.BandwidthProfile) ([]YangItem, error) {
504 result := []YangItem{}
505
506 //TODO: The best way to translate this information is still under discussion, but the code
507 // to retrieve it is ready. Since this is not fundamental at the moment, an empty slice is
508 // returned, and the correct translation can be added here at a later time.
509
510 return result, nil
511}