Add support for user-friendly names in YANG data with aliases in KV store
Change-Id: I8b4fbb290dfb467b401e8cf20b825ddbb3bf897e
diff --git a/internal/core/adapter.go b/internal/core/adapter.go
index 5e19753..8f41c59 100644
--- a/internal/core/adapter.go
+++ b/internal/core/adapter.go
@@ -18,9 +18,13 @@
import (
"context"
+ "encoding/json"
"fmt"
+ "strconv"
"github.com/golang/protobuf/ptypes/empty"
+ "github.com/opencord/voltha-lib-go/v7/pkg/db"
+ "github.com/opencord/voltha-lib-go/v7/pkg/db/kvstore"
"github.com/opencord/voltha-lib-go/v7/pkg/log"
"github.com/opencord/voltha-northbound-bbf-adapter/internal/clients"
"github.com/opencord/voltha-protos/v5/go/voltha"
@@ -28,15 +32,21 @@
var AdapterInstance *VolthaYangAdapter
+const (
+ kvStoreServices = "services"
+)
+
type VolthaYangAdapter struct {
volthaNbiClient *clients.VolthaNbiClient
onosClient *clients.OnosClient
+ kvStore *db.Backend
}
-func NewVolthaYangAdapter(nbiClient *clients.VolthaNbiClient, onosClient *clients.OnosClient) *VolthaYangAdapter {
+func NewVolthaYangAdapter(nbiClient *clients.VolthaNbiClient, onosClient *clients.OnosClient, kvBackend *db.Backend) *VolthaYangAdapter {
return &VolthaYangAdapter{
volthaNbiClient: nbiClient,
onosClient: onosClient,
+ kvStore: kvBackend,
}
}
@@ -76,6 +86,49 @@
return items, nil
}
+func getLocationsToPortsMap(ports []clients.OnosPort) map[string]string {
+ //Create a map of port IDs to port names
+ //e.g. of:00000a0a0a0a0a0a/256 to BBSM000a0001-1
+ portNames := map[string]string{}
+
+ for _, port := range ports {
+ portId := fmt.Sprintf("%s/%s", port.Element, port.Port)
+ name, ok := port.Annotations["portName"]
+ if ok {
+ portNames[portId] = name
+ }
+ }
+
+ return portNames
+}
+
+func (t *VolthaYangAdapter) getServiceAliasOrFallback(ctx context.Context, uniTagServiceName string, key ServiceKey) (*ServiceAlias, error) {
+ alias, err := t.LoadServiceAlias(ctx, key)
+ if err != nil {
+ //Happens in case a service is provisioned using ONOS directly,
+ //bypassing the adapter
+ serviceName := fmt.Sprintf("%s-%s", key.Port, uniTagServiceName)
+ alias = &ServiceAlias{
+ Key: key,
+ ServiceName: serviceName,
+ VlansName: serviceName + "-vlans",
+ }
+
+ logger.Warnw(ctx, "cannot-load-service-alias", log.Fields{
+ "err": err,
+ "fallback": alias,
+ })
+
+ //Store the fallback alias to avoid the fallback on future requests
+ err := t.StoreServiceAlias(ctx, *alias)
+ if err != nil {
+ return nil, fmt.Errorf("cannot-store-fallback-service-alias")
+ }
+ }
+
+ return alias, nil
+}
+
func (t *VolthaYangAdapter) GetVlans(ctx context.Context) ([]YangItem, error) {
services, err := t.onosClient.GetProgrammedSubscribers()
if err != nil {
@@ -94,9 +147,32 @@
}
logger.Debugw(ctx, "get-onos-ports-success", log.Fields{"ports": ports})
- items, err := translateVlans(services, ports)
- if err != nil {
- return nil, fmt.Errorf("cannot-translate-vlans: %v", err)
+ portNames := getLocationsToPortsMap(ports)
+
+ items := []YangItem{}
+
+ for _, service := range services {
+ portName, ok := portNames[service.Location]
+ if !ok {
+ return nil, fmt.Errorf("no-port-name-for-location: %s", service.Location)
+ }
+
+ alias, err := t.getServiceAliasOrFallback(ctx, service.TagInfo.ServiceName, ServiceKey{
+ Port: portName,
+ STag: strconv.Itoa(service.TagInfo.PonSTag),
+ CTag: strconv.Itoa(service.TagInfo.PonCTag),
+ TpId: strconv.Itoa(service.TagInfo.TechnologyProfileID),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ vlansItems, err := translateVlans(service.TagInfo, *alias)
+ if err != nil {
+ return nil, fmt.Errorf("cannot-translate-vlans: %v", err)
+ }
+
+ items = append(items, vlansItems...)
}
return items, nil
@@ -169,9 +245,32 @@
}
logger.Debugw(ctx, "get-onos-ports-success", log.Fields{"ports": ports})
- items, err := translateServices(services, ports)
- if err != nil {
- return nil, fmt.Errorf("cannot-translate-services: %v", err)
+ portNames := getLocationsToPortsMap(ports)
+
+ items := []YangItem{}
+
+ for _, service := range services {
+ portName, ok := portNames[service.Location]
+ if !ok {
+ return nil, fmt.Errorf("no-port-name-for-location: %s", service.Location)
+ }
+
+ alias, err := t.getServiceAliasOrFallback(ctx, service.TagInfo.ServiceName, ServiceKey{
+ Port: portName,
+ STag: strconv.Itoa(service.TagInfo.PonSTag),
+ CTag: strconv.Itoa(service.TagInfo.PonCTag),
+ TpId: strconv.Itoa(service.TagInfo.TechnologyProfileID),
+ })
+ if err != nil {
+ return nil, err
+ }
+
+ serviceItems, err := translateService(service.TagInfo, *alias)
+ if err != nil {
+ return nil, fmt.Errorf("cannot-translate-service: %v", err)
+ }
+
+ items = append(items, serviceItems...)
}
return items, nil
@@ -186,3 +285,68 @@
_, err := t.onosClient.RemoveService(portName, sTag, cTag, technologyProfileId)
return err
}
+
+//Used to uniquely identify the service and
+//construct a KV Store path to the service info
+type ServiceKey struct {
+ Port string `json:"port"`
+ STag string `json:"sTag"`
+ CTag string `json:"cTag"`
+ TpId string `json:"tpId"`
+}
+
+//Holds user provided names for the definition
+//of a service in the yang datastore
+type ServiceAlias struct {
+ Key ServiceKey `json:"key"`
+ ServiceName string `json:"serviceName"`
+ VlansName string `json:"vlansName"`
+}
+
+func getServiceAliasKVPath(key ServiceKey) string {
+ return fmt.Sprintf("%s/%s/%s/%s/%s", kvStoreServices, key.Port, key.STag, key.CTag, key.TpId)
+}
+
+func (t *VolthaYangAdapter) StoreServiceAlias(ctx context.Context, alias ServiceAlias) error {
+ json, err := json.Marshal(alias)
+ if err != nil {
+ return err
+ }
+
+ if err = t.kvStore.Put(ctx, getServiceAliasKVPath(alias.Key), json); err != nil {
+ return err
+ }
+ return nil
+}
+
+func (t *VolthaYangAdapter) LoadServiceAlias(ctx context.Context, key ServiceKey) (*ServiceAlias, error) {
+ found, err := t.kvStore.Get(ctx, getServiceAliasKVPath(key))
+ if err != nil {
+ return nil, err
+ }
+
+ if found == nil {
+ return nil, fmt.Errorf("service-alias-not-found-in-kvstore: %s", key)
+ }
+
+ var foundAlias ServiceAlias
+ value, err := kvstore.ToByte(found.Value)
+ if err != nil {
+ return nil, err
+ }
+
+ if err := json.Unmarshal(value, &foundAlias); err != nil {
+ return nil, err
+ }
+
+ return &foundAlias, nil
+}
+
+func (t *VolthaYangAdapter) DeleteServiceAlias(ctx context.Context, key ServiceKey) error {
+ err := t.kvStore.Delete(ctx, getServiceAliasKVPath(key))
+ if err != nil {
+ return err
+ }
+
+ return nil
+}