[VOL-2761][VOL-2905] Support for DT workflow

Change-Id: I9fe1fae20d3a5970a474a234aa3bde0f9110569e
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index 0d64a14..28ac5a1 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -33,7 +33,7 @@
 	"github.com/opencord/bbsim/internal/common"
 	omcisim "github.com/opencord/omci-sim"
 	"github.com/opencord/voltha-protos/v2/go/openolt"
-	tech_profile "github.com/opencord/voltha-protos/v2/go/tech_profile"
+	"github.com/opencord/voltha-protos/v2/go/tech_profile"
 	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/codes"
@@ -148,19 +148,43 @@
 	}
 
 	// create PON ports
-	availableCTag := options.BBSim.CTagInit
-	for i := 0; i < olt.NumPon; i++ {
-		p := CreatePonPort(&olt, uint32(i))
 
-		// create ONU devices
-		for j := 0; j < olt.NumOnuPerPon; j++ {
-			delay := time.Duration(olt.Delay*j) * time.Millisecond
-			o := CreateONU(&olt, *p, uint32(j+1), options.BBSim.STag, availableCTag, options.BBSim.EnableAuth, options.BBSim.EnableDhcp, delay, isMock)
-			p.Onus = append(p.Onus, o)
-			availableCTag = availableCTag + 1
+	if options.BBSim.STagAllocation == common.TagAllocationShared && options.BBSim.CTagAllocation == common.TagAllocationShared {
+		oltLogger.Fatalf("This configuration will result in duplicate C/S tags combination")
+	} else if options.BBSim.STagAllocation == common.TagAllocationUnique && options.BBSim.CTagAllocation == common.TagAllocationUnique {
+		oltLogger.Fatalf("This configuration is not supported yet")
+	} else if options.BBSim.STagAllocation == common.TagAllocationShared && options.BBSim.CTagAllocation == common.TagAllocationUnique {
+		// ATT case
+		availableCTag := options.BBSim.CTag
+		for i := 0; i < olt.NumPon; i++ {
+			p := CreatePonPort(&olt, uint32(i))
+
+			// create ONU devices
+			for j := 0; j < olt.NumOnuPerPon; j++ {
+				delay := time.Duration(olt.Delay*j) * time.Millisecond
+				o := CreateONU(&olt, *p, uint32(j+1), options.BBSim.STag, availableCTag, options.BBSim.EnableAuth, options.BBSim.EnableDhcp, delay, isMock)
+				p.Onus = append(p.Onus, o)
+				availableCTag = availableCTag + 1
+			}
+
+			olt.Pons = append(olt.Pons, p)
 		}
+	} else if options.BBSim.STagAllocation == common.TagAllocationUnique && options.BBSim.CTagAllocation == common.TagAllocationShared {
+		// DT case
+		availableSTag := options.BBSim.STag
+		for i := 0; i < olt.NumPon; i++ {
+			p := CreatePonPort(&olt, uint32(i))
 
-		olt.Pons = append(olt.Pons, p)
+			// create ONU devices
+			for j := 0; j < olt.NumOnuPerPon; j++ {
+				delay := time.Duration(olt.Delay*j) * time.Millisecond
+				o := CreateONU(&olt, *p, uint32(j+1), availableSTag, options.BBSim.CTag, options.BBSim.EnableAuth, options.BBSim.EnableDhcp, delay, isMock)
+				p.Onus = append(p.Onus, o)
+				availableSTag = availableSTag + 1
+			}
+
+			olt.Pons = append(olt.Pons, p)
+		}
 	}
 
 	if isMock != true {
@@ -1069,7 +1093,7 @@
 	pon, err := o.GetPonById(omci_msg.IntfId)
 	if err != nil {
 		oltLogger.WithFields(log.Fields{
-			"error": err,
+			"error":  err,
 			"onu_id": omci_msg.OnuId,
 			"pon_id": omci_msg.IntfId,
 		}).Error("pon ID not found")
@@ -1079,7 +1103,7 @@
 	onu, err := pon.GetOnuById(omci_msg.OnuId)
 	if err != nil {
 		oltLogger.WithFields(log.Fields{
-			"error": err,
+			"error":  err,
 			"onu_id": omci_msg.OnuId,
 			"pon_id": omci_msg.IntfId,
 		}).Error("onu ID not found")
diff --git a/internal/bbsim/responders/sadis/sadis.go b/internal/bbsim/responders/sadis/sadis.go
index d24daeb..fc2b451 100644
--- a/internal/bbsim/responders/sadis/sadis.go
+++ b/internal/bbsim/responders/sadis/sadis.go
@@ -93,10 +93,10 @@
 	NasPortID  string        `json:"nasPortId"`
 	CircuitID  string        `json:"circuitId"`
 	RemoteID   string        `json:"remoteId"`
-	UniTagList []SadisUniTag `json:"uniTagList"`
+	UniTagList []interface{} `json:"uniTagList"` // this can be SadisUniTagAtt, SadisUniTagDt
 }
 
-type SadisUniTag struct {
+type SadisUniTagAtt struct {
 	PonCTag                    int    `json:"ponCTag, omitempty"`
 	PonSTag                    int    `json:"ponSTag, omitempty"`
 	TechnologyProfileID        int    `json:"technologyProfileId, omitempty"`
@@ -106,6 +106,15 @@
 	IsIgmpRequired             bool   `json:"isIgmpRequired, omitempty"`
 }
 
+type SadisUniTagDt struct {
+	UniTagMatch                int    `json:"uniTagMatch, omitempty"`
+	PonCTag                    int    `json:"ponCTag, omitempty"`
+	PonSTag                    int    `json:"ponSTag, omitempty"`
+	TechnologyProfileID        int    `json:"technologyProfileId, omitempty"`
+	UpstreamBandwidthProfile   string `json:"upstreamBandwidthProfile, omitempty"`
+	DownstreamBandwidthProfile string `json:"downstreamBandwidthProfile, omitempty"`
+}
+
 // SADIS BandwithProfile Entry
 type SadisBWPEntry struct {
 	ID  string `json:"id"`
@@ -189,18 +198,34 @@
 		RemoteID:  onu.Sn() + uniSuffix,
 	}
 
-	// TODO this sadis config only works for the ATT workflow
-	// address VOL-2761 to support DT
-	sonuUniTag := SadisUniTag{
-		PonCTag:             onu.CTag,
-		PonSTag:             onu.STag,
-		TechnologyProfileID: 64,
-		// NOTE do we want to select a random bandwidth profile?
-		// if so use bandwidthProfiles[rand.Intn(len(bandwidthProfiles))].ID
-		UpstreamBandwidthProfile:   "Default",
-		DownstreamBandwidthProfile: "User_Bandwidth1",
-		IsDhcpRequired:             true,
-		IsIgmpRequired:             true,
+	// base structure common to all use cases
+	var sonuUniTag interface{}
+
+	// set workflow specific params
+	switch common.Options.BBSim.SadisFormat {
+	case common.SadisFormatAtt:
+		sonuUniTag = SadisUniTagAtt{
+			PonCTag:             onu.CTag,
+			PonSTag:             onu.STag,
+			TechnologyProfileID: 64,
+			// NOTE do we want to select a random bandwidth profile?
+			// if so use bandwidthProfiles[rand.Intn(len(bandwidthProfiles))].ID
+			UpstreamBandwidthProfile:   "Default",
+			DownstreamBandwidthProfile: "User_Bandwidth1",
+			IsDhcpRequired:             true,
+			IsIgmpRequired:             true,
+		}
+	case common.SadisFormatDt:
+		sonuUniTag = SadisUniTagDt{
+			PonCTag:             4096,
+			PonSTag:             onu.STag,
+			TechnologyProfileID: 64,
+			// NOTE do we want to select a random bandwidth profile?
+			// if so use bandwidthProfiles[rand.Intn(len(bandwidthProfiles))].ID
+			UpstreamBandwidthProfile:   "Default",
+			DownstreamBandwidthProfile: "User_Bandwidth1",
+			UniTagMatch:                4096,
+		}
 	}
 
 	sonuv2.UniTagList = append(sonuv2.UniTagList, sonuUniTag)
diff --git a/internal/bbsim/responders/sadis/sadis_test.go b/internal/bbsim/responders/sadis/sadis_test.go
new file mode 100644
index 0000000..7c8bc1b
--- /dev/null
+++ b/internal/bbsim/responders/sadis/sadis_test.go
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2018-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 sadis
+
+import (
+	"fmt"
+	"github.com/opencord/bbsim/internal/bbsim/devices"
+	"github.com/opencord/bbsim/internal/common"
+	"gotest.tools/assert"
+	"net"
+	"testing"
+)
+
+func createMockDevices() (devices.OltDevice, devices.Onu) {
+	olt := devices.OltDevice{
+		ID: 0,
+	}
+
+	onu := devices.Onu{
+		ID:        1,
+		PonPortID: 1,
+		STag:      900,
+		CTag:      923,
+		HwAddress: net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(1), byte(1)},
+		PortNo:    0,
+	}
+	onu.SerialNumber = onu.NewSN(0, onu.PonPortID, onu.ID)
+
+	return olt, onu
+}
+
+func TestSadisServer_GetOnuEntryV1(t *testing.T) {
+
+	olt, onu := createMockDevices()
+
+	uni := "1"
+
+	res, err := GetOnuEntryV1(&olt, &onu, uni)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.Equal(t, res.ID, fmt.Sprintf("%s-%s",onu.Sn(), uni))
+	assert.Equal(t, res.CTag, 923)
+	assert.Equal(t, res.STag, 900)
+	assert.Equal(t, res.RemoteID, string(olt.SerialNumber))
+	assert.Equal(t, res.DownstreamBandwidthProfile, "Default")
+	assert.Equal(t, res.UpstreamBandwidthProfile, "User_Bandwidth1")
+	assert.Equal(t, res.TechnologyProfileID, 64)
+
+}
+
+func TestSadisServer_GetOnuEntryV2_Att(t *testing.T) {
+	olt, onu := createMockDevices()
+
+	uni := "1"
+
+	res, err := GetOnuEntryV2(&olt, &onu, uni)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.Equal(t, res.ID, fmt.Sprintf("%s-%s",onu.Sn(), uni))
+	assert.Equal(t, res.RemoteID, fmt.Sprintf("%s-%s",onu.Sn(), uni))
+
+	// assert the correct type
+	uniTagList, ok := res.UniTagList[0].(SadisUniTagAtt)
+	if !ok {
+		t.Fatal("UniTagList has the wrong type")
+	}
+
+	assert.Equal(t, uniTagList.PonCTag, 923)
+	assert.Equal(t, uniTagList.PonSTag, 900)
+	assert.Equal(t, uniTagList.DownstreamBandwidthProfile, "User_Bandwidth1")
+	assert.Equal(t, uniTagList.UpstreamBandwidthProfile, "Default")
+	assert.Equal(t, uniTagList.TechnologyProfileID, 64)
+	assert.Equal(t, uniTagList.IsDhcpRequired, true)
+	assert.Equal(t, uniTagList.IsIgmpRequired, true)
+}
+
+func TestSadisServer_GetOnuEntryV2_Dt(t *testing.T) {
+	common.Options.BBSim.SadisFormat = common.SadisFormatDt
+	olt, onu := createMockDevices()
+
+	uni := "1"
+
+	res, err := GetOnuEntryV2(&olt, &onu, uni)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	assert.Equal(t, res.ID, fmt.Sprintf("%s-%s",onu.Sn(), uni))
+	assert.Equal(t, res.RemoteID, fmt.Sprintf("%s-%s",onu.Sn(), uni))
+
+	// assert the correct type
+	uniTagList, ok := res.UniTagList[0].(SadisUniTagDt)
+	if !ok {
+		t.Fatal("UniTagList has the wrong type")
+	}
+
+	assert.Equal(t, uniTagList.PonCTag, 4096)
+	assert.Equal(t, uniTagList.PonSTag, 900)
+	assert.Equal(t, uniTagList.DownstreamBandwidthProfile, "User_Bandwidth1")
+	assert.Equal(t, uniTagList.UpstreamBandwidthProfile, "Default")
+	assert.Equal(t, uniTagList.TechnologyProfileID, 64)
+	assert.Equal(t, uniTagList.UniTagMatch, 4096)
+}
\ No newline at end of file
diff --git a/internal/common/options.go b/internal/common/options.go
index d7bc9eb..6088a1d 100644
--- a/internal/common/options.go
+++ b/internal/common/options.go
@@ -17,12 +17,77 @@
 package common
 
 import (
+	"errors"
 	"flag"
 	"fmt"
+	"github.com/ghodss/yaml"
 	"io/ioutil"
+	log "github.com/sirupsen/logrus"
 	"net"
+	"strings"
+)
 
-	"gopkg.in/yaml.v2"
+var tagAllocationValues = []string{
+	"unknown",
+	"shared",
+	"unique",
+}
+
+type TagAllocation int
+
+func (t TagAllocation) String() string {
+	return tagAllocationValues[t]
+}
+
+func tagAllocationFromString(s string) (TagAllocation, error) {
+	for i, v := range tagAllocationValues {
+		if v == s {
+			return TagAllocation(i), nil
+		}
+	}
+	log.WithFields(log.Fields{
+		"ValidValues": strings.Join(tagAllocationValues[1:], ", "),
+	}).Errorf("%s-is-not-a-valid-tag-allocation", s)
+	return TagAllocation(0), errors.New(fmt.Sprintf("%s-is-not-a-valid-tag-allocation", s))
+}
+
+const (
+	_ TagAllocation = iota
+	TagAllocationShared
+	TagAllocationUnique
+)
+
+
+var sadisFormatValues = []string{
+	"unknown",
+	"att",
+	"dt",
+	"tt",
+}
+
+type SadisFormat int
+
+func (s SadisFormat) String() string {
+	return sadisFormatValues[s]
+}
+
+func sadisFormatFromString(s string) (SadisFormat, error) {
+	for i, v := range sadisFormatValues {
+		if v == s {
+			return SadisFormat(i), nil
+		}
+	}
+	log.WithFields(log.Fields{
+		"ValidValues": strings.Join(sadisFormatValues[1:], ", "),
+	}).Errorf("%s-is-not-a-valid-sadis-format", s)
+	return SadisFormat(0), errors.New(fmt.Sprintf("%s-is-not-a-valid-sadis-format", s))
+}
+
+const (
+	_ SadisFormat = iota
+	SadisFormatAtt
+	SadisFormatDt
+	SadisFormatTt
 )
 
 type BBRCliOptions struct {
@@ -56,25 +121,28 @@
 }
 
 type BBSimConfig struct {
-	EnableDhcp           bool    `yaml:"enable_dhcp"`
-	EnableAuth           bool    `yaml:"enable_auth"`
-	LogLevel             string  `yaml:"log_level"`
-	LogCaller            bool    `yaml:"log_caller"`
-	Delay                int     `yaml:"delay"`
-	CpuProfile           *string `yaml:"cpu_profile"`
-	CTagInit             int     `yaml:"c_tag"`
-	STag                 int     `yaml:"s_tag"`
-	OpenOltAddress       string  `yaml:"openolt_address"`
-	ApiAddress           string  `yaml:"api_address"`
-	RestApiAddress       string  `yaml:"rest_api_address"`
-	LegacyApiAddress     string  `yaml:"legacy_api_address"`
-	LegacyRestApiAddress string  `yaml:"legacy_rest_api_address"`
-	SadisRestAddress     string  `yaml:"sadis_rest_address"`
-	SadisServer          bool    `yaml:"sadis_server"`
-	KafkaAddress         string  `yaml:"kafka_address"`
-	Events               bool    `yaml:"enable_events"`
-	ControlledActivation string  `yaml:"controlled_activation"`
-	EnablePerf           bool    `yaml:"enable_perf"`
+	EnableDhcp           bool          `yaml:"enable_dhcp"`
+	EnableAuth           bool          `yaml:"enable_auth"`
+	LogLevel             string        `yaml:"log_level"`
+	LogCaller            bool          `yaml:"log_caller"`
+	Delay                int           `yaml:"delay"`
+	CpuProfile           *string       `yaml:"cpu_profile"`
+	CTagAllocation       TagAllocation `yaml:"c_tag_allocation"`
+	CTag                 int           `yaml:"c_tag"`
+	STagAllocation       TagAllocation `yaml:"s_tag_allocation"`
+	STag                 int           `yaml:"s_tag"`
+	OpenOltAddress       string        `yaml:"openolt_address"`
+	ApiAddress           string        `yaml:"api_address"`
+	RestApiAddress       string        `yaml:"rest_api_address"`
+	LegacyApiAddress     string        `yaml:"legacy_api_address"`
+	LegacyRestApiAddress string        `yaml:"legacy_rest_api_address"`
+	SadisRestAddress     string        `yaml:"sadis_rest_address"`
+	SadisServer          bool          `yaml:"sadis_server"`
+	SadisFormat          SadisFormat   `yaml:"sadis_format"`
+	KafkaAddress         string        `yaml:"kafka_address"`
+	Events               bool          `yaml:"enable_events"`
+	ControlledActivation string        `yaml:"controlled_activation"`
+	EnablePerf           bool          `yaml:"enable_perf"`
 }
 
 type BBRConfig struct {
@@ -94,8 +162,10 @@
 
 	c := &BBSimYamlConfig{
 		BBSimConfig{
+			STagAllocation:       TagAllocationShared,
 			STag:                 900,
-			CTagInit:             900,
+			CTagAllocation:       TagAllocationUnique,
+			CTag:                 900,
 			EnableDhcp:           false,
 			EnableAuth:           false,
 			LogLevel:             "debug",
@@ -108,6 +178,7 @@
 			LegacyRestApiAddress: ":50073",
 			SadisRestAddress:     ":50074",
 			SadisServer:          true,
+			SadisFormat:          SadisFormatAtt,
 			KafkaAddress:         ":9092",
 			Events:               false,
 			ControlledActivation: "default",
@@ -150,6 +221,8 @@
 		fmt.Printf("Error parsing YAML file: %s\n", err)
 	}
 
+	// TODO convert from string to TagAllocation
+
 	return yamlConfig, nil
 }
 
@@ -166,8 +239,13 @@
 	api_address := flag.String("api_address", conf.BBSim.ApiAddress, "IP address:port")
 	rest_api_address := flag.String("rest_api_address", conf.BBSim.RestApiAddress, "IP address:port")
 
+	s_tag_allocation := flag.String("s_tag_allocation", conf.BBSim.STagAllocation.String(), "Use 'unique' for incremental values, 'shared' to use the same value in all the ONUs")
 	s_tag := flag.Int("s_tag", conf.BBSim.STag, "S-Tag initial value")
-	c_tag_init := flag.Int("c_tag", conf.BBSim.CTagInit, "C-Tag starting value, each ONU will get a sequential one (targeting 1024 ONUs per BBSim instance the range is big enough)")
+
+	c_tag_allocation := flag.String("c_tag_allocation", conf.BBSim.CTagAllocation.String(), "Use 'unique' for incremental values, 'shared' to use the same value in all the ONUs")
+	c_tag := flag.Int("c_tag", conf.BBSim.CTag, "C-Tag starting value, each ONU will get a sequential one (targeting 1024 ONUs per BBSim instance the range is big enough)")
+
+	sadisFormat := flag.String("sadisFormat", conf.BBSim.SadisFormat.String(), fmt.Sprintf("Which format should sadis expose? [%s]", strings.Join(sadisFormatValues[1:], "|")))
 
 	auth := flag.Bool("auth", conf.BBSim.EnableAuth, "Set this flag if you want authentication to start automatically")
 	dhcp := flag.Bool("dhcp", conf.BBSim.EnableDhcp, "Set this flag if you want DHCP to start automatically")
@@ -185,12 +263,33 @@
 	kafkaAddress := flag.String("kafkaAddress", conf.BBSim.KafkaAddress, "IP:Port for kafka")
 	flag.Parse()
 
+	sTagAlloc, err := tagAllocationFromString(*s_tag_allocation)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	cTagAlloc, err := tagAllocationFromString(*c_tag_allocation)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	sf, err := sadisFormatFromString(*sadisFormat)
+	if err != nil {
+		log.Fatal(err)
+	}
+
+	if sf == SadisFormatTt {
+		log.Fatalf("Sadis format %s is not yet supported", sf.String())
+	}
+
 	conf.Olt.ID = int(*olt_id)
 	conf.Olt.NniPorts = uint32(*nni)
 	conf.Olt.PonPorts = uint32(*pon)
 	conf.Olt.OnusPonPort = uint32(*onu)
+	conf.BBSim.STagAllocation = sTagAlloc
 	conf.BBSim.STag = int(*s_tag)
-	conf.BBSim.CTagInit = int(*c_tag_init)
+	conf.BBSim.CTagAllocation = cTagAlloc
+	conf.BBSim.CTag = int(*c_tag)
 	conf.BBSim.CpuProfile = profileCpu
 	conf.BBSim.LogLevel = *logLevel
 	conf.BBSim.LogCaller = *logCaller
@@ -204,6 +303,7 @@
 	conf.BBSim.OpenOltAddress = *openolt_address
 	conf.BBSim.ApiAddress = *api_address
 	conf.BBSim.RestApiAddress = *rest_api_address
+	conf.BBSim.SadisFormat = sf
 
 	// update device id if not set
 	if conf.Olt.DeviceId == "" {