blob: 8f41c597f409a1f3f3850f1305789905e830c748 [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 "context"
Elia Battistonaa7a0482022-08-17 12:24:02 +000021 "encoding/json"
Elia Battistone1cecb22022-03-21 10:05:25 +010022 "fmt"
Elia Battistonaa7a0482022-08-17 12:24:02 +000023 "strconv"
Elia Battistone1cecb22022-03-21 10:05:25 +010024
25 "github.com/golang/protobuf/ptypes/empty"
Elia Battistonaa7a0482022-08-17 12:24:02 +000026 "github.com/opencord/voltha-lib-go/v7/pkg/db"
27 "github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
Elia Battistone1cecb22022-03-21 10:05:25 +010028 "github.com/opencord/voltha-lib-go/v7/pkg/log"
29 "github.com/opencord/voltha-northbound-bbf-adapter/internal/clients"
Elia Battiston4750d3c2022-07-14 13:24:56 +000030 "github.com/opencord/voltha-protos/v5/go/voltha"
Elia Battistone1cecb22022-03-21 10:05:25 +010031)
32
33var AdapterInstance *VolthaYangAdapter
34
Elia Battistonaa7a0482022-08-17 12:24:02 +000035const (
36 kvStoreServices = "services"
37)
38
Elia Battistone1cecb22022-03-21 10:05:25 +010039type VolthaYangAdapter struct {
40 volthaNbiClient *clients.VolthaNbiClient
Elia Battistona1333642022-07-27 12:17:24 +000041 onosClient *clients.OnosClient
Elia Battistonaa7a0482022-08-17 12:24:02 +000042 kvStore *db.Backend
Elia Battistone1cecb22022-03-21 10:05:25 +010043}
44
Elia Battistonaa7a0482022-08-17 12:24:02 +000045func NewVolthaYangAdapter(nbiClient *clients.VolthaNbiClient, onosClient *clients.OnosClient, kvBackend *db.Backend) *VolthaYangAdapter {
Elia Battistone1cecb22022-03-21 10:05:25 +010046 return &VolthaYangAdapter{
47 volthaNbiClient: nbiClient,
Elia Battistona1333642022-07-27 12:17:24 +000048 onosClient: onosClient,
Elia Battistonaa7a0482022-08-17 12:24:02 +000049 kvStore: kvBackend,
Elia Battistone1cecb22022-03-21 10:05:25 +010050 }
51}
52
53func (t *VolthaYangAdapter) GetDevices(ctx context.Context) ([]YangItem, error) {
54 devices, err := t.volthaNbiClient.Service.ListDevices(ctx, &empty.Empty{})
55 if err != nil {
Elia Battiston4750d3c2022-07-14 13:24:56 +000056 return nil, fmt.Errorf("get-devices-failed: %v", err)
Elia Battistone1cecb22022-03-21 10:05:25 +010057 }
Elia Battiston4750d3c2022-07-14 13:24:56 +000058 logger.Debugw(ctx, "get-devices-success", log.Fields{"devices": devices})
Elia Battistone1cecb22022-03-21 10:05:25 +010059
Elia Battiston4750d3c2022-07-14 13:24:56 +000060 items := []YangItem{}
Elia Battistone1cecb22022-03-21 10:05:25 +010061
Elia Battiston4750d3c2022-07-14 13:24:56 +000062 for _, device := range devices.Items {
63 items = append(items, translateDevice(device)...)
64
65 if !device.Root {
66 //If the device is an ONU, also expose UNIs
67 ports, err := t.volthaNbiClient.Service.ListDevicePorts(ctx, &voltha.ID{Id: device.Id})
68 if err != nil {
69 return nil, fmt.Errorf("get-onu-ports-failed: %v", err)
70 }
Elia Battistona1333642022-07-27 12:17:24 +000071 logger.Debugw(ctx, "get-onu-ports-success", log.Fields{"deviceId": device.Id, "ports": ports})
Elia Battiston4750d3c2022-07-14 13:24:56 +000072
73 portsItems, err := translateOnuPorts(device.Id, ports)
74 if err != nil {
75 logger.Errorw(ctx, "cannot-translate-onu-ports", log.Fields{
76 "deviceId": device.Id,
77 "err": err,
78 })
79 continue
80 }
81
82 items = append(items, portsItems...)
83 }
84 }
Elia Battistone1cecb22022-03-21 10:05:25 +010085
86 return items, nil
87}
Elia Battistona1333642022-07-27 12:17:24 +000088
Elia Battistonaa7a0482022-08-17 12:24:02 +000089func getLocationsToPortsMap(ports []clients.OnosPort) map[string]string {
90 //Create a map of port IDs to port names
91 //e.g. of:00000a0a0a0a0a0a/256 to BBSM000a0001-1
92 portNames := map[string]string{}
93
94 for _, port := range ports {
95 portId := fmt.Sprintf("%s/%s", port.Element, port.Port)
96 name, ok := port.Annotations["portName"]
97 if ok {
98 portNames[portId] = name
99 }
100 }
101
102 return portNames
103}
104
105func (t *VolthaYangAdapter) getServiceAliasOrFallback(ctx context.Context, uniTagServiceName string, key ServiceKey) (*ServiceAlias, error) {
106 alias, err := t.LoadServiceAlias(ctx, key)
107 if err != nil {
108 //Happens in case a service is provisioned using ONOS directly,
109 //bypassing the adapter
110 serviceName := fmt.Sprintf("%s-%s", key.Port, uniTagServiceName)
111 alias = &ServiceAlias{
112 Key: key,
113 ServiceName: serviceName,
114 VlansName: serviceName + "-vlans",
115 }
116
117 logger.Warnw(ctx, "cannot-load-service-alias", log.Fields{
118 "err": err,
119 "fallback": alias,
120 })
121
122 //Store the fallback alias to avoid the fallback on future requests
123 err := t.StoreServiceAlias(ctx, *alias)
124 if err != nil {
125 return nil, fmt.Errorf("cannot-store-fallback-service-alias")
126 }
127 }
128
129 return alias, nil
130}
131
Elia Battistona1333642022-07-27 12:17:24 +0000132func (t *VolthaYangAdapter) GetVlans(ctx context.Context) ([]YangItem, error) {
133 services, err := t.onosClient.GetProgrammedSubscribers()
134 if err != nil {
135 return nil, fmt.Errorf("get-programmed-subscribers-failed: %v", err)
136 }
137 logger.Debugw(ctx, "get-programmed-subscribers-success", log.Fields{"services": services})
138
139 //No need for other requests if there are no services
140 if len(services) == 0 {
141 return []YangItem{}, nil
142 }
143
144 ports, err := t.onosClient.GetPorts()
145 if err != nil {
146 return nil, fmt.Errorf("get-onos-ports-failed: %v", err)
147 }
148 logger.Debugw(ctx, "get-onos-ports-success", log.Fields{"ports": ports})
149
Elia Battistonaa7a0482022-08-17 12:24:02 +0000150 portNames := getLocationsToPortsMap(ports)
151
152 items := []YangItem{}
153
154 for _, service := range services {
155 portName, ok := portNames[service.Location]
156 if !ok {
157 return nil, fmt.Errorf("no-port-name-for-location: %s", service.Location)
158 }
159
160 alias, err := t.getServiceAliasOrFallback(ctx, service.TagInfo.ServiceName, ServiceKey{
161 Port: portName,
162 STag: strconv.Itoa(service.TagInfo.PonSTag),
163 CTag: strconv.Itoa(service.TagInfo.PonCTag),
164 TpId: strconv.Itoa(service.TagInfo.TechnologyProfileID),
165 })
166 if err != nil {
167 return nil, err
168 }
169
170 vlansItems, err := translateVlans(service.TagInfo, *alias)
171 if err != nil {
172 return nil, fmt.Errorf("cannot-translate-vlans: %v", err)
173 }
174
175 items = append(items, vlansItems...)
Elia Battistona1333642022-07-27 12:17:24 +0000176 }
177
178 return items, nil
179}
180
181func (t *VolthaYangAdapter) GetBandwidthProfiles(ctx context.Context) ([]YangItem, error) {
182 services, err := t.onosClient.GetProgrammedSubscribers()
183 if err != nil {
184 return nil, fmt.Errorf("get-programmed-subscribers-failed: %v", err)
185 }
186 logger.Debugw(ctx, "get-programmed-subscribers-success", log.Fields{"services": services})
187
188 //No need for other requests if there are no services
189 if len(services) == 0 {
190 return []YangItem{}, nil
191 }
192
193 bwProfilesMap := map[string]bool{}
194 bwProfiles := []clients.BandwidthProfile{}
195
196 for _, service := range services {
197 //Get information on downstream bw profile if new
198 if _, ok := bwProfilesMap[service.TagInfo.DownstreamBandwidthProfile]; !ok {
199 bw, err := t.onosClient.GetBandwidthProfile(service.TagInfo.DownstreamBandwidthProfile)
200 if err != nil {
201 return nil, fmt.Errorf("get-bw-profile-failed: %s %v", service.TagInfo.DownstreamBandwidthProfile, err)
202 }
203 logger.Debugw(ctx, "get-bw-profile-success", log.Fields{"bwProfile": bw})
204
205 bwProfiles = append(bwProfiles, *bw)
206 bwProfilesMap[service.TagInfo.DownstreamBandwidthProfile] = true
207 }
208
209 //Get information on upstream bw profile if new
210 if _, ok := bwProfilesMap[service.TagInfo.UpstreamBandwidthProfile]; !ok {
211 bw, err := t.onosClient.GetBandwidthProfile(service.TagInfo.UpstreamBandwidthProfile)
212 if err != nil {
213 return nil, fmt.Errorf("get-bw-profile-failed: %s %v", service.TagInfo.UpstreamBandwidthProfile, err)
214 }
215 logger.Debugw(ctx, "get-bw-profile-success", log.Fields{"bwProfile": bw})
216
217 bwProfiles = append(bwProfiles, *bw)
218 bwProfilesMap[service.TagInfo.UpstreamBandwidthProfile] = true
219 }
220 }
221
222 items, err := translateBandwidthProfiles(bwProfiles)
223 if err != nil {
224 return nil, fmt.Errorf("cannot-translate-bandwidth-profiles: %v", err)
225 }
226
227 return items, nil
228}
229
230func (t *VolthaYangAdapter) GetServices(ctx context.Context) ([]YangItem, error) {
231 services, err := t.onosClient.GetProgrammedSubscribers()
232 if err != nil {
233 return nil, fmt.Errorf("get-programmed-subscribers-failed: %v", err)
234 }
235 logger.Debugw(ctx, "get-programmed-subscribers-success", log.Fields{"services": services})
236
237 //No need for other requests if there are no services
238 if len(services) == 0 {
239 return []YangItem{}, nil
240 }
241
242 ports, err := t.onosClient.GetPorts()
243 if err != nil {
244 return nil, fmt.Errorf("get-onos-ports-failed: %v", err)
245 }
246 logger.Debugw(ctx, "get-onos-ports-success", log.Fields{"ports": ports})
247
Elia Battistonaa7a0482022-08-17 12:24:02 +0000248 portNames := getLocationsToPortsMap(ports)
249
250 items := []YangItem{}
251
252 for _, service := range services {
253 portName, ok := portNames[service.Location]
254 if !ok {
255 return nil, fmt.Errorf("no-port-name-for-location: %s", service.Location)
256 }
257
258 alias, err := t.getServiceAliasOrFallback(ctx, service.TagInfo.ServiceName, ServiceKey{
259 Port: portName,
260 STag: strconv.Itoa(service.TagInfo.PonSTag),
261 CTag: strconv.Itoa(service.TagInfo.PonCTag),
262 TpId: strconv.Itoa(service.TagInfo.TechnologyProfileID),
263 })
264 if err != nil {
265 return nil, err
266 }
267
268 serviceItems, err := translateService(service.TagInfo, *alias)
269 if err != nil {
270 return nil, fmt.Errorf("cannot-translate-service: %v", err)
271 }
272
273 items = append(items, serviceItems...)
Elia Battistona1333642022-07-27 12:17:24 +0000274 }
275
276 return items, nil
277}
278
279func (t *VolthaYangAdapter) ProvisionService(portName string, sTag string, cTag string, technologyProfileId string) error {
280 _, err := t.onosClient.ProvisionService(portName, sTag, cTag, technologyProfileId)
281 return err
282}
283
284func (t *VolthaYangAdapter) RemoveService(portName string, sTag string, cTag string, technologyProfileId string) error {
285 _, err := t.onosClient.RemoveService(portName, sTag, cTag, technologyProfileId)
286 return err
287}
Elia Battistonaa7a0482022-08-17 12:24:02 +0000288
289//Used to uniquely identify the service and
290//construct a KV Store path to the service info
291type ServiceKey struct {
292 Port string `json:"port"`
293 STag string `json:"sTag"`
294 CTag string `json:"cTag"`
295 TpId string `json:"tpId"`
296}
297
298//Holds user provided names for the definition
299//of a service in the yang datastore
300type ServiceAlias struct {
301 Key ServiceKey `json:"key"`
302 ServiceName string `json:"serviceName"`
303 VlansName string `json:"vlansName"`
304}
305
306func getServiceAliasKVPath(key ServiceKey) string {
307 return fmt.Sprintf("%s/%s/%s/%s/%s", kvStoreServices, key.Port, key.STag, key.CTag, key.TpId)
308}
309
310func (t *VolthaYangAdapter) StoreServiceAlias(ctx context.Context, alias ServiceAlias) error {
311 json, err := json.Marshal(alias)
312 if err != nil {
313 return err
314 }
315
316 if err = t.kvStore.Put(ctx, getServiceAliasKVPath(alias.Key), json); err != nil {
317 return err
318 }
319 return nil
320}
321
322func (t *VolthaYangAdapter) LoadServiceAlias(ctx context.Context, key ServiceKey) (*ServiceAlias, error) {
323 found, err := t.kvStore.Get(ctx, getServiceAliasKVPath(key))
324 if err != nil {
325 return nil, err
326 }
327
328 if found == nil {
329 return nil, fmt.Errorf("service-alias-not-found-in-kvstore: %s", key)
330 }
331
332 var foundAlias ServiceAlias
333 value, err := kvstore.ToByte(found.Value)
334 if err != nil {
335 return nil, err
336 }
337
338 if err := json.Unmarshal(value, &foundAlias); err != nil {
339 return nil, err
340 }
341
342 return &foundAlias, nil
343}
344
345func (t *VolthaYangAdapter) DeleteServiceAlias(ctx context.Context, key ServiceKey) error {
346 err := t.kvStore.Delete(ctx, getServiceAliasKVPath(key))
347 if err != nil {
348 return err
349 }
350
351 return nil
352}