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
+}
diff --git a/internal/core/adapter_test.go b/internal/core/adapter_test.go
new file mode 100644
index 0000000..7d8f387
--- /dev/null
+++ b/internal/core/adapter_test.go
@@ -0,0 +1,68 @@
+/*
+* Copyright 2022-present Open Networking Foundation
+
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+
+* http://www.apache.org/licenses/LICENSE-2.0
+
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+ */
+
+package core
+
+import (
+	"testing"
+
+	"github.com/opencord/voltha-northbound-bbf-adapter/internal/clients"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestLocationsToPortsMap(t *testing.T) {
+	ports := []clients.OnosPort{
+		{
+			Element: "of:00001",
+			Port:    "256",
+			Annotations: map[string]string{
+				"portName": "TESTPORT-1",
+			},
+		},
+		{
+			Element: "of:00001",
+			Port:    "257",
+			Annotations: map[string]string{
+				"portName": "TESTPORT-2",
+			},
+		},
+	}
+
+	portNames := getLocationsToPortsMap(ports)
+
+	assert.NotEmpty(t, portNames, "Empty map")
+
+	name, ok := portNames["of:00001/256"]
+	assert.True(t, ok, "First port name not found")
+	assert.Equal(t, "TESTPORT-1", name)
+
+	name, ok = portNames["of:00001/257"]
+	assert.True(t, ok, "Second port name not found")
+	assert.Equal(t, "TESTPORT-2", name)
+}
+
+func TestServiceAliasKVPath(t *testing.T) {
+	serviceKey := ServiceKey{
+		Port: "PORT",
+		STag: "100",
+		CTag: "101",
+		TpId: "64",
+	}
+
+	path := getServiceAliasKVPath(serviceKey)
+
+	assert.Equal(t, "services/PORT/100/101/64", path)
+}
diff --git a/internal/core/translation.go b/internal/core/translation.go
index 623e261..5275b12 100644
--- a/internal/core/translation.go
+++ b/internal/core/translation.go
@@ -320,182 +320,123 @@
 	return notification, channelTermination, nil
 }
 
-//translateServices returns a slice of yang items that represent the currently programmed services
-func translateServices(subscribers []clients.ProgrammedSubscriber, ports []clients.OnosPort) ([]YangItem, error) {
-	//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
-		}
-	}
-
+//translateService returns a slice of yang items that represent a programmed service
+func translateService(tagInfo clients.SadisUniTag, alias ServiceAlias) ([]YangItem, error) {
 	result := []YangItem{}
 
-	for _, subscriber := range subscribers {
-		portName, ok := portNames[subscriber.Location]
-		if !ok {
-			return nil, fmt.Errorf("no-port-name-for-location: %s", subscriber.Location)
-		}
+	portPath := GetServicePortPath(alias.ServiceName, alias.Key.Port)
 
-		serviceName := fmt.Sprintf("%s-%s", portName, subscriber.TagInfo.ServiceName)
-
-		portPath := GetServicePortPath(serviceName, portName)
-
-		if subscriber.TagInfo.ConfiguredMacAddress != "" {
-			result = append(result, YangItem{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:configured-mac-address",
-				Value: subscriber.TagInfo.ConfiguredMacAddress,
-			})
-		}
-
-		result = append(result, []YangItem{
-			{
-				Path:  fmt.Sprintf("%s/port-vlans/port-vlan[name='%s']", portPath, serviceName),
-				Value: "",
-			},
-			{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:technology-profile-id",
-				Value: strconv.Itoa(subscriber.TagInfo.TechnologyProfileID),
-			},
-			{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:downstream-subscriber-bp-name",
-				Value: subscriber.TagInfo.DownstreamBandwidthProfile,
-			},
-			{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:upstream-subscriber-bp-name",
-				Value: subscriber.TagInfo.UpstreamBandwidthProfile,
-			},
-			{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:mac-learning-enabled",
-				Value: strconv.FormatBool(subscriber.TagInfo.EnableMacLearning),
-			},
-			{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:dhcp-required",
-				Value: strconv.FormatBool(subscriber.TagInfo.IsDhcpRequired),
-			},
-			{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:igmp-required",
-				Value: strconv.FormatBool(subscriber.TagInfo.IsIgmpRequired),
-			},
-			{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:pppoe-required",
-				Value: strconv.FormatBool(subscriber.TagInfo.IsPPPoERequired),
-			},
-		}...)
-
-		if subscriber.TagInfo.UpstreamOltBandwidthProfile != "" {
-			result = append(result, YangItem{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:upstream-olt-bp-name",
-				Value: subscriber.TagInfo.UpstreamOltBandwidthProfile,
-			})
-		}
-
-		if subscriber.TagInfo.DownstreamOltBandwidthProfile != "" {
-			result = append(result, YangItem{
-				Path:  portPath + "/bbf-nt-service-profile-voltha:downstream-olt-bp-name",
-				Value: subscriber.TagInfo.UpstreamOltBandwidthProfile,
-			})
-		}
+	if tagInfo.ConfiguredMacAddress != "" {
+		result = append(result, YangItem{
+			Path:  portPath + "/bbf-nt-service-profile-voltha:configured-mac-address",
+			Value: tagInfo.ConfiguredMacAddress,
+		})
 	}
 
+	result = append(result, []YangItem{
+		{
+			Path:  fmt.Sprintf("%s/port-vlans/port-vlan[name='%s']", portPath, alias.VlansName),
+			Value: "",
+		},
+		{
+			Path:  portPath + "/bbf-nt-service-profile-voltha:technology-profile-id",
+			Value: strconv.Itoa(tagInfo.TechnologyProfileID),
+		},
+		{
+			Path:  portPath + "/bbf-nt-service-profile-voltha:mac-learning-enabled",
+			Value: strconv.FormatBool(tagInfo.EnableMacLearning),
+		},
+		{
+			Path:  portPath + "/bbf-nt-service-profile-voltha:dhcp-required",
+			Value: strconv.FormatBool(tagInfo.IsDhcpRequired),
+		},
+		{
+			Path:  portPath + "/bbf-nt-service-profile-voltha:igmp-required",
+			Value: strconv.FormatBool(tagInfo.IsIgmpRequired),
+		},
+		{
+			Path:  portPath + "/bbf-nt-service-profile-voltha:pppoe-required",
+			Value: strconv.FormatBool(tagInfo.IsPPPoERequired),
+		},
+	}...)
+
+	// TODO: The creation of leaves for Bandwidth Profiles has been temporarily
+	// removed to avoid validation issues during reconciliation, until the translation
+	// of Bandwidth Profiles is agreed.
+	// See translateBandwidthProfiles()
+
 	return result, nil
 }
 
-//translateVlans returns a slice of yang items that represent the vlans used by programmed services
-func translateVlans(subscribers []clients.ProgrammedSubscriber, ports []clients.OnosPort) ([]YangItem, error) {
-	//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
-		}
-	}
-
+//translateVlans returns a slice of yang items that represent the vlans used by a programmed service
+func translateVlans(tagInfo clients.SadisUniTag, alias ServiceAlias) ([]YangItem, error) {
 	result := []YangItem{}
 
-	for _, subscriber := range subscribers {
-		portName, ok := portNames[subscriber.Location]
-		if !ok {
-			return nil, fmt.Errorf("no-port-name-for-location: %s", subscriber.Location)
-		}
+	vlansPath := GetVlansPath(alias.VlansName)
 
-		serviceName := fmt.Sprintf("%s-%s", portName, subscriber.TagInfo.ServiceName)
+	uniTagMatch := YangVlanIdAny
+	sTag := YangVlanIdAny
+	cTag := YangVlanIdAny
 
-		vlansPath := GetVlansPath(serviceName)
+	if tagInfo.UniTagMatch != VolthaVlanIdAny {
+		uniTagMatch = strconv.Itoa(tagInfo.UniTagMatch)
+	}
+	if tagInfo.PonSTag != VolthaVlanIdAny {
+		sTag = strconv.Itoa(tagInfo.PonSTag)
+	}
+	if tagInfo.PonCTag != VolthaVlanIdAny {
+		cTag = strconv.Itoa(tagInfo.PonCTag)
+	}
 
-		uniTagMatch := YangVlanIdAny
-		sTag := YangVlanIdAny
-		cTag := YangVlanIdAny
-
-		if subscriber.TagInfo.UniTagMatch != VolthaVlanIdAny {
-			uniTagMatch = strconv.Itoa(subscriber.TagInfo.UniTagMatch)
-		}
-		if subscriber.TagInfo.PonSTag != VolthaVlanIdAny {
-			sTag = strconv.Itoa(subscriber.TagInfo.PonSTag)
-		}
-		if subscriber.TagInfo.PonCTag != VolthaVlanIdAny {
-			cTag = strconv.Itoa(subscriber.TagInfo.PonCTag)
-		}
-
-		if subscriber.TagInfo.UniTagMatch > 0 {
-			result = append(result, []YangItem{
-				{
-					Path:  vlansPath + "/match-criteria/outer-tag/vlan-id",
-					Value: uniTagMatch,
-				},
-				{
-					Path:  vlansPath + "/match-criteria/second-tag/vlan-id",
-					Value: "any",
-				},
-			}...)
-		}
-
-		if subscriber.TagInfo.UsPonSTagPriority >= 0 {
-			result = append(result, YangItem{
-				Path:  vlansPath + "/ingress-rewrite/push-outer-tag/pbit",
-				Value: strconv.Itoa(subscriber.TagInfo.UsPonSTagPriority),
-			})
-		}
-		if subscriber.TagInfo.DsPonSTagPriority >= 0 {
-			result = append(result, YangItem{
-				Path:  vlansPath + "/ingress-rewrite/push-outer-tag/bbf-voltha-vlan-translation:dpbit",
-				Value: strconv.Itoa(subscriber.TagInfo.DsPonSTagPriority),
-			})
-		}
-		if subscriber.TagInfo.UsPonCTagPriority >= 0 {
-			result = append(result, YangItem{
-				Path:  vlansPath + "/ingress-rewrite/push-second-tag/pbit",
-				Value: strconv.Itoa(subscriber.TagInfo.UsPonCTagPriority),
-			})
-		}
-		if subscriber.TagInfo.DsPonCTagPriority >= 0 {
-			result = append(result, YangItem{
-				Path:  vlansPath + "/ingress-rewrite/push-second-tag/bbf-voltha-vlan-translation:dpbit",
-				Value: strconv.Itoa(subscriber.TagInfo.DsPonCTagPriority),
-			})
-		}
-
+	if tagInfo.UniTagMatch > 0 {
 		result = append(result, []YangItem{
 			{
-				Path:  vlansPath + "/ingress-rewrite/push-outer-tag/vlan-id",
-				Value: sTag,
+				Path:  vlansPath + "/match-criteria/outer-tag/vlan-id",
+				Value: uniTagMatch,
 			},
 			{
-				Path:  vlansPath + "/ingress-rewrite/push-second-tag/vlan-id",
-				Value: cTag,
+				Path:  vlansPath + "/match-criteria/second-tag/vlan-id",
+				Value: "any",
 			},
 		}...)
 	}
 
+	if tagInfo.UsPonSTagPriority >= 0 {
+		result = append(result, YangItem{
+			Path:  vlansPath + "/ingress-rewrite/push-outer-tag/pbit",
+			Value: strconv.Itoa(tagInfo.UsPonSTagPriority),
+		})
+	}
+	if tagInfo.DsPonSTagPriority >= 0 {
+		result = append(result, YangItem{
+			Path:  vlansPath + "/ingress-rewrite/push-outer-tag/bbf-voltha-vlan-translation:dpbit",
+			Value: strconv.Itoa(tagInfo.DsPonSTagPriority),
+		})
+	}
+	if tagInfo.UsPonCTagPriority >= 0 {
+		result = append(result, YangItem{
+			Path:  vlansPath + "/ingress-rewrite/push-second-tag/pbit",
+			Value: strconv.Itoa(tagInfo.UsPonCTagPriority),
+		})
+	}
+	if tagInfo.DsPonCTagPriority >= 0 {
+		result = append(result, YangItem{
+			Path:  vlansPath + "/ingress-rewrite/push-second-tag/bbf-voltha-vlan-translation:dpbit",
+			Value: strconv.Itoa(tagInfo.DsPonCTagPriority),
+		})
+	}
+
+	result = append(result, []YangItem{
+		{
+			Path:  vlansPath + "/ingress-rewrite/push-outer-tag/vlan-id",
+			Value: sTag,
+		},
+		{
+			Path:  vlansPath + "/ingress-rewrite/push-second-tag/vlan-id",
+			Value: cTag,
+		},
+	}...)
+
 	return result, nil
 }
 
diff --git a/internal/core/translation_test.go b/internal/core/translation_test.go
index 837acd9..a56b215 100644
--- a/internal/core/translation_test.go
+++ b/internal/core/translation_test.go
@@ -325,49 +325,41 @@
 }
 
 func TestTranslateServices(t *testing.T) {
-	subscribers := []clients.ProgrammedSubscriber{
-		{
-			Location: "of:00001/256",
-			TagInfo: clients.SadisUniTag{
-				UniTagMatch:                 100,
-				PonCTag:                     4096,
-				PonSTag:                     102,
-				TechnologyProfileID:         64,
-				UpstreamBandwidthProfile:    "BW1",
-				DownstreamBandwidthProfile:  "BW2",
-				UpstreamOltBandwidthProfile: "OLTBW",
-				IsDhcpRequired:              true,
-				IsIgmpRequired:              false,
-				IsPPPoERequired:             false,
-				ConfiguredMacAddress:        "00:11:22:33:44:55",
-				EnableMacLearning:           true,
-				UsPonCTagPriority:           1,
-				UsPonSTagPriority:           2,
-				DsPonCTagPriority:           3,
-				DsPonSTagPriority:           -1,
-				ServiceName:                 "testService",
-			},
+	subscriber := clients.ProgrammedSubscriber{
+		Location: "of:00001/256",
+		TagInfo: clients.SadisUniTag{
+			UniTagMatch:                 100,
+			PonCTag:                     4096,
+			PonSTag:                     102,
+			TechnologyProfileID:         64,
+			UpstreamBandwidthProfile:    "BW1",
+			DownstreamBandwidthProfile:  "BW2",
+			UpstreamOltBandwidthProfile: "OLTBW",
+			IsDhcpRequired:              true,
+			IsIgmpRequired:              false,
+			IsPPPoERequired:             false,
+			ConfiguredMacAddress:        "00:11:22:33:44:55",
+			EnableMacLearning:           true,
+			UsPonCTagPriority:           1,
+			UsPonSTagPriority:           2,
+			DsPonCTagPriority:           3,
+			DsPonSTagPriority:           -1,
+			ServiceName:                 "testService",
 		},
 	}
 
-	ports := []clients.OnosPort{
-		{
-			Element: "of:00001",
-			Port:    "256",
-			Annotations: map[string]string{
-				"portName": "TESTPORT-1",
-			},
+	alias := ServiceAlias{
+		Key: ServiceKey{
+			Port: "TESTPORT-1",
+			STag: "101",
+			CTag: "102",
+			TpId: "64",
 		},
-		{
-			Element: "of:00001",
-			Port:    "257",
-			Annotations: map[string]string{
-				"portName": "TESTPORT-2",
-			},
-		},
+		ServiceName: "TESTPORT-1-testService",
+		VlansName:   "TESTPORT-1-testService-vlans",
 	}
 
-	servicesItesm, err := translateServices(subscribers, ports)
+	servicesItesm, err := translateService(subscriber.TagInfo, alias)
 	assert.Nil(t, err, "Translation error")
 
 	assert.NotEmpty(t, servicesItesm, "No services items")
@@ -380,18 +372,6 @@
 			Value: "00:11:22:33:44:55",
 		},
 		{
-			Path:  servicePortPath + "/bbf-nt-service-profile-voltha:upstream-subscriber-bp-name",
-			Value: "BW1",
-		},
-		{
-			Path:  servicePortPath + "/bbf-nt-service-profile-voltha:downstream-subscriber-bp-name",
-			Value: "BW2",
-		},
-		{
-			Path:  servicePortPath + "/bbf-nt-service-profile-voltha:upstream-olt-bp-name",
-			Value: "OLTBW",
-		},
-		{
 			Path:  servicePortPath + "/bbf-nt-service-profile-voltha:mac-learning-enabled",
 			Value: "true",
 		},
@@ -409,7 +389,7 @@
 		},
 	}
 
-	_, ok := getItemWithPath(servicesItesm, servicePortPath+"/port-vlans/port-vlan[name='TESTPORT-1-testService']")
+	_, ok := getItemWithPath(servicesItesm, servicePortPath+"/port-vlans/port-vlan[name='TESTPORT-1-testService-vlans']")
 	assert.True(t, ok, "No vlans leafref in services")
 
 	_, ok = getItemWithPath(servicesItesm, servicePortPath+"/bbf-nt-service-profile-voltha:downstream-olt-bp-name")
@@ -423,54 +403,46 @@
 }
 
 func TestTranslateVlans(t *testing.T) {
-	subscribers := []clients.ProgrammedSubscriber{
-		{
-			Location: "of:00001/256",
-			TagInfo: clients.SadisUniTag{
-				UniTagMatch:                 100,
-				PonCTag:                     4096,
-				PonSTag:                     102,
-				TechnologyProfileID:         64,
-				UpstreamBandwidthProfile:    "BW1",
-				DownstreamBandwidthProfile:  "BW2",
-				UpstreamOltBandwidthProfile: "OLTBW",
-				IsDhcpRequired:              true,
-				IsIgmpRequired:              false,
-				IsPPPoERequired:             false,
-				ConfiguredMacAddress:        "00:11:22:33:44:55",
-				EnableMacLearning:           true,
-				UsPonCTagPriority:           1,
-				UsPonSTagPriority:           2,
-				DsPonCTagPriority:           3,
-				DsPonSTagPriority:           -1,
-				ServiceName:                 "testService",
-			},
+	subscriber := clients.ProgrammedSubscriber{
+		Location: "of:00001/256",
+		TagInfo: clients.SadisUniTag{
+			UniTagMatch:                 100,
+			PonCTag:                     4096,
+			PonSTag:                     102,
+			TechnologyProfileID:         64,
+			UpstreamBandwidthProfile:    "BW1",
+			DownstreamBandwidthProfile:  "BW2",
+			UpstreamOltBandwidthProfile: "OLTBW",
+			IsDhcpRequired:              true,
+			IsIgmpRequired:              false,
+			IsPPPoERequired:             false,
+			ConfiguredMacAddress:        "00:11:22:33:44:55",
+			EnableMacLearning:           true,
+			UsPonCTagPriority:           1,
+			UsPonSTagPriority:           2,
+			DsPonCTagPriority:           3,
+			DsPonSTagPriority:           -1,
+			ServiceName:                 "testService",
 		},
 	}
 
-	ports := []clients.OnosPort{
-		{
-			Element: "of:00001",
-			Port:    "256",
-			Annotations: map[string]string{
-				"portName": "TESTPORT-1",
-			},
+	alias := ServiceAlias{
+		Key: ServiceKey{
+			Port: "TESTPORT-1",
+			STag: "101",
+			CTag: "102",
+			TpId: "64",
 		},
-		{
-			Element: "of:00001",
-			Port:    "257",
-			Annotations: map[string]string{
-				"portName": "TESTPORT-2",
-			},
-		},
+		ServiceName: "TESTPORT-1-testService",
+		VlansName:   "TESTPORT-1-testService-vlans",
 	}
 
-	vlanItems, err := translateVlans(subscribers, ports)
+	vlanItems, err := translateVlans(subscriber.TagInfo, alias)
 	assert.Nil(t, err, "Translation error")
 
 	assert.NotEmpty(t, vlanItems, "No vlans items")
 
-	vlanPath := VlansPath + "/vlan-translation-profile[name='TESTPORT-1-testService']"
+	vlanPath := VlansPath + "/vlan-translation-profile[name='TESTPORT-1-testService-vlans']"
 
 	expected := []YangItem{
 		{