[SEBA-882] add Sadis server
Change-Id: I2c973a940ccf1398b1c122908769e806eaa1dd14
diff --git a/internal/bbsim/api/grpc_api_server_legacy.go b/internal/bbsim/api/grpc_api_server_legacy.go
index d4e9999..0c03d81 100644
--- a/internal/bbsim/api/grpc_api_server_legacy.go
+++ b/internal/bbsim/api/grpc_api_server_legacy.go
@@ -192,7 +192,7 @@
s := &http.Server{Addr: hostandport, Handler: mux}
go func() {
- logger.Infof("legacy REST API server listening on %s ...", hostandport)
+ logger.Infof("legacy REST API server listening on %s", hostandport)
if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
logger.Errorf("Could not start legacy API server: %v", err)
return
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index c249f62..5d6e571 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -245,7 +245,7 @@
reflection.Register(grpcServer)
go grpcServer.Serve(lis)
- oltLogger.Debugf("OLT Listening on: %v", address)
+ oltLogger.Debugf("OLT listening on %v", address)
return grpcServer, nil
}
diff --git a/internal/bbsim/responders/sadis/sadis.go b/internal/bbsim/responders/sadis/sadis.go
new file mode 100644
index 0000000..8c5d936
--- /dev/null
+++ b/internal/bbsim/responders/sadis/sadis.go
@@ -0,0 +1,303 @@
+/*
+ * 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 (
+ "encoding/json"
+ "net/http"
+ "strings"
+ "sync"
+
+ "github.com/gorilla/mux"
+ "github.com/opencord/bbsim/internal/bbsim/devices"
+ "github.com/opencord/bbsim/internal/common"
+ log "github.com/sirupsen/logrus"
+)
+
+var sadisLogger = log.WithFields(log.Fields{
+ "module": "SADIS",
+})
+
+type sadisServer struct {
+ olt *devices.OltDevice
+}
+
+// bandwidthProfiles contains some dummy profiles
+var bandwidthProfiles = []interface{}{
+ &SadisBWPEntry{ID: "User_Bandwidth1", AIR: 100000, CBS: 10000, CIR: 30000, EBS: 1000, EIR: 20000},
+ &SadisBWPEntry{ID: "User_Bandwidth2", AIR: 100000, CBS: 5000, CIR: 100000, EBS: 5000, EIR: 1000000},
+ &SadisBWPEntry{ID: "User_Bandwidth3", AIR: 100000, CBS: 5000, CIR: 100000, EBS: 5000, EIR: 1000000},
+ &SadisBWPEntry{ID: "Default", AIR: 100000, CBS: 30, CIR: 600, EBS: 30, EIR: 400},
+}
+
+// SadisConfig is the top-level SADIS configuration struct
+type SadisConfig struct {
+ Sadis SadisEntries `json:"sadis"`
+ BandwidthProfile BandwidthProfileEntries `json:"bandwidthprofile"`
+}
+
+type SadisEntries struct {
+ Integration SadisIntegration `json:"integration"`
+ Entries []interface{} `json:"entries,omitempty"`
+}
+type BandwidthProfileEntries struct {
+ Integration SadisIntegration `json:"integration"`
+ Entries []interface{} `json:"entries,omitempty"`
+}
+
+type SadisIntegration struct {
+ URL string `json:"url,omitempty"`
+ Cache struct {
+ Enabled bool `json:"enabled"`
+ MaxSize int `json:"maxsize"`
+ TTL string `json:"ttl"`
+ } `json:"cache"`
+}
+
+type SadisOltEntry struct {
+ ID string `json:"id"`
+ HardwareIdentifier string `json:"hardwareIdentifier"`
+ IPAddress string `json:"ipAddress"`
+ NasID string `json:"nasId"`
+ UplinkPort int `json:"uplinkPort"`
+}
+
+type SadisOnuEntry struct {
+ ID string `json:"id"`
+ CTag int `json:"cTag"`
+ STag int `json:"sTag"`
+ NasPortID string `json:"nasPortId"`
+ CircuitID string `json:"circuitId"`
+ RemoteID string `json:"remoteId"`
+ TechnologyProfileID int `json:"technologyProfileId"`
+ UpstreamBandwidthProfile string `json:"upstreamBandwidthProfile"`
+ DownstreamBandwidthProfile string `json:"downstreamBandwidthProfile"`
+}
+
+// SADIS BandwithProfile Entry
+type SadisBWPEntry struct {
+ ID string `json:"id"`
+ AIR int `json:"air"`
+ CBS int `json:"cbs"`
+ CIR int `json:"cir"`
+ EBS int `json:"ebs"`
+ EIR int `json:"eir"`
+}
+
+// GetSadisConfig returns a full SADIS configuration struct ready to be marshalled into JSON
+func GetSadisConfig(olt *devices.OltDevice) *SadisConfig {
+ sadisEntries, _ := GetSadisEntries(olt)
+ bwpEntries := getBWPEntries()
+
+ conf := &SadisConfig{}
+ conf.Sadis = *sadisEntries
+ conf.BandwidthProfile = *bwpEntries
+
+ return conf
+}
+
+func GetSadisEntries(olt *devices.OltDevice) (*SadisEntries, error) {
+ solt, _ := GetOltEntry(olt)
+
+ entries := []interface{}{}
+ entries = append(entries, solt)
+
+ a := strings.Split(common.Options.BBSim.SadisRestAddress, ":")
+ port := a[len(a)-1]
+
+ integration := SadisIntegration{}
+ integration.URL = "http://bbsim:" + port + "/subscribers/%s"
+ integration.Cache.Enabled = false
+ integration.Cache.MaxSize = 50
+ integration.Cache.TTL = "PT0m"
+
+ sadis := &SadisEntries{
+ integration,
+ entries,
+ }
+
+ return sadis, nil
+}
+
+func GetOltEntry(olt *devices.OltDevice) (*SadisOltEntry, error) {
+ ip, _ := common.GetIPAddr("nni") // TODO verify which IP to report
+ solt := &SadisOltEntry{
+ ID: olt.SerialNumber,
+ HardwareIdentifier: common.Options.Olt.DeviceId,
+ IPAddress: ip,
+ NasID: olt.SerialNumber,
+ UplinkPort: 1048576, // TODO currently assumes we only have on NNI port
+ }
+ return solt, nil
+}
+
+func GetOnuEntry(olt *devices.OltDevice, onu *devices.Onu, uniId string) (*SadisOnuEntry, error) {
+ uniSuffix := "-" + uniId
+ sonu := &SadisOnuEntry{
+ ID: onu.Sn() + uniSuffix,
+ CTag: onu.CTag,
+ STag: onu.STag,
+ NasPortID: onu.Sn() + uniSuffix,
+ CircuitID: onu.Sn() + uniSuffix,
+ RemoteID: olt.SerialNumber,
+ TechnologyProfileID: 64,
+ UpstreamBandwidthProfile: "User_Bandwidth1",
+ DownstreamBandwidthProfile: "Default",
+ }
+
+ return sonu, nil
+}
+
+func getBWPEntries() *BandwidthProfileEntries {
+ a := strings.Split(common.Options.BBSim.SadisRestAddress, ":")
+ port := a[len(a)-1]
+
+ integration := SadisIntegration{}
+ integration.URL = "http://bbsim:" + port + "/bandwidthprofiles/%s"
+ integration.Cache.Enabled = true
+ integration.Cache.MaxSize = 40
+ integration.Cache.TTL = "PT1m"
+
+ bwp := &BandwidthProfileEntries{
+ Integration: integration,
+ }
+
+ return bwp
+}
+
+func (s *sadisServer) ServeBaseConfig(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ sadisConf := GetSadisConfig(s.olt)
+
+ sadisJSON, _ := json.Marshal(sadisConf)
+ sadisLogger.Tracef("SADIS JSON: %s", sadisJSON)
+
+ w.Write([]byte(sadisJSON))
+
+}
+
+func (s *sadisServer) ServeStaticConfig(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ w.WriteHeader(http.StatusOK)
+ sadisConf := GetSadisConfig(s.olt)
+
+ sadisConf.Sadis.Integration.URL = ""
+ for i := range s.olt.Pons {
+ for _, onu := range s.olt.Pons[i].Onus {
+ // FIXME currently we only support one UNI per ONU
+ sonu, _ := GetOnuEntry(s.olt, onu, "1")
+ sadisConf.Sadis.Entries = append(sadisConf.Sadis.Entries, sonu)
+ }
+ }
+
+ sadisConf.BandwidthProfile.Integration.URL = ""
+ sadisConf.BandwidthProfile.Entries = bandwidthProfiles
+
+ sadisJSON, _ := json.Marshal(sadisConf)
+ sadisLogger.Tracef("SADIS JSON: %s", sadisJSON)
+
+ w.Write([]byte(sadisJSON))
+
+}
+
+func (s *sadisServer) ServeEntry(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ vars := mux.Vars(r)
+
+ // check if the requested ID is for the OLT
+ if s.olt.SerialNumber == vars["ID"] {
+ sadisLogger.WithFields(log.Fields{
+ "OltSn": s.olt.SerialNumber,
+ }).Debug("Received SADIS OLT request")
+
+ sadisConf, _ := GetOltEntry(s.olt)
+
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(sadisConf)
+ return
+ }
+
+ i := strings.Split(vars["ID"], "-") // split ID to get serial number and uni port
+ if len(i) != 2 {
+ w.WriteHeader(http.StatusUnprocessableEntity)
+ w.Write([]byte("{}"))
+ sadisLogger.Warnf("Received invalid SADIS subscriber request: %s", vars["ID"])
+ return
+ }
+ sn, uni := i[0], i[len(i)-1]
+
+ onu, err := s.olt.FindOnuBySn(sn)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ w.Write([]byte("{}"))
+ sadisLogger.WithFields(log.Fields{
+ "OnuSn": sn,
+ "OnuId": "NA",
+ }).Warnf("Received invalid SADIS subscriber request: %s", vars["ID"])
+ return
+ }
+
+ sadisLogger.WithFields(log.Fields{
+ "OnuId": onu.ID,
+ "OnuSn": sn,
+ "OnuPortNo": uni,
+ }).Debug("Received SADIS request")
+
+ sadisConf, err := GetOnuEntry(s.olt, onu, uni)
+
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(sadisConf)
+}
+
+func (s *sadisServer) ServeBWPEntry(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "application/json")
+ vars := mux.Vars(r)
+ id := vars["ID"]
+ sadisLogger.Debugf("Received request for SADIS bandwidth profile %s", id)
+
+ for _, e := range bandwidthProfiles {
+ bwpEntry := e.(*SadisBWPEntry)
+ if bwpEntry.ID == id {
+ w.WriteHeader(http.StatusOK)
+ json.NewEncoder(w).Encode(bwpEntry)
+ return
+ }
+ }
+
+ w.WriteHeader(http.StatusNotFound)
+ w.Write([]byte("{}"))
+}
+
+// StartRestServer starts REST server which returns a SADIS configuration for the currently simulated OLT
+func StartRestServer(olt *devices.OltDevice, wg *sync.WaitGroup) {
+ addr := common.Options.BBSim.SadisRestAddress
+ sadisLogger.Infof("SADIS server listening on %s", addr)
+ s := &sadisServer{
+ olt: olt,
+ }
+
+ router := mux.NewRouter().StrictSlash(true)
+ router.HandleFunc("/cfg", s.ServeBaseConfig)
+ router.HandleFunc("/static", s.ServeStaticConfig)
+ router.HandleFunc("/subscribers/{ID}", s.ServeEntry)
+ router.HandleFunc("/bandwidthprofiles/{ID}", s.ServeBWPEntry)
+
+ log.Fatal(http.ListenAndServe(addr, router))
+
+ wg.Done()
+}
diff --git a/internal/common/helpers.go b/internal/common/helpers.go
index 4e1997b..c1bf936 100644
--- a/internal/common/helpers.go
+++ b/internal/common/helpers.go
@@ -17,8 +17,10 @@
package common
import (
- "github.com/opencord/voltha-protos/v2/go/openolt"
+ "net"
"strconv"
+
+ "github.com/opencord/voltha-protos/v2/go/openolt"
)
func OnuSnToString(sn *openolt.SerialNumber) string {
@@ -28,3 +30,31 @@
}
return s
}
+
+// GetIPAddr returns the IPv4 address of an interface. 0.0.0.0 is returned if the IP cannot be determined.
+func GetIPAddr(ifname string) (string, error) {
+ ip := "0.0.0.0"
+
+ intf, err := net.InterfaceByName(ifname)
+ if err != nil {
+ return ip, err
+ }
+
+ addrs, err := intf.Addrs()
+ if err != nil {
+ return ip, err
+ }
+
+ for _, addr := range addrs {
+ // get first IPv4 address
+ switch v := addr.(type) {
+ case *net.IPNet:
+ if v.IP.To4() != nil {
+ ip = v.IP.String()
+ break
+ }
+ }
+ }
+
+ return ip, nil
+}
diff --git a/internal/common/options.go b/internal/common/options.go
index 954e66e..361d9b3 100644
--- a/internal/common/options.go
+++ b/internal/common/options.go
@@ -68,6 +68,8 @@
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"`
}
type BBRConfig struct {
@@ -94,11 +96,13 @@
LogLevel: "debug",
LogCaller: false,
Delay: 200,
- OpenOltAddress: "0.0.0.0:50060",
- ApiAddress: "0.0.0.0:50070",
- RestApiAddress: "0.0.0.0:50071",
- LegacyApiAddress: "0.0.0.0:50072",
- LegacyRestApiAddress: "0.0.0.0:50073",
+ OpenOltAddress: ":50060",
+ ApiAddress: ":50070",
+ RestApiAddress: ":50071",
+ LegacyApiAddress: ":50072",
+ LegacyRestApiAddress: ":50073",
+ SadisRestAddress: ":50074",
+ SadisServer: true,
},
OltConfig{
Vendor: "BBSim",
@@ -181,6 +185,7 @@
conf.Olt.DeviceId = net.HardwareAddr{0xA, 0xA, 0xA, 0xA, 0xA, byte(conf.Olt.ID)}.String()
}
+ Options = conf
return conf
}