blob: 394c9ebb123267e62c7679fbce0fde835cd806fc [file] [log] [blame]
/*
* 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"
"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 = map[string][]*SadisBWPEntry{
common.BP_FORMAT_MEF: {
{ID: "User_Bandwidth1", AIR: 100000, CBS: 10000, CIR: 30000, EBS: 1000, EIR: 100000},
{ID: "User_Bandwidth2", AIR: 100000, CBS: 5000, CIR: 100000, EBS: 5000, EIR: 100000},
{ID: "User_Bandwidth3", AIR: 100000, CBS: 5000, CIR: 1000000, EBS: 5000, EIR: 1000000},
{ID: "Default", AIR: 100000, CBS: 30, CIR: 600, EBS: 30, EIR: 400},
},
common.BP_FORMAT_IETF: {
{ID: "User_Bandwidth1", CBS: 10000, CIR: 30000, GIR: 100000, PIR: 20000, PBS: 1000},
{ID: "User_Bandwidth2", CBS: 5000, CIR: 100000, GIR: 100000, PIR: 30000, PBS: 5000},
{ID: "User_Bandwidth3", CBS: 5000, CIR: 1000000, GIR: 100000, PIR: 40000, PBS: 5000},
{ID: "Default", CBS: 30, CIR: 600, GIR: 0, PIR: 32000, PBS: 30},
},
}
// 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 []*SadisBWPEntry `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 SadisOnuEntryV2 struct {
ID string `json:"id"`
NasPortID string `json:"nasPortId"`
CircuitID string `json:"circuitId"`
RemoteID string `json:"remoteId"`
UniTagList []SadisUniTag `json:"uniTagList"` // this can be SadisUniTagAtt, SadisUniTagDt
}
type SadisUniTag 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"`
IsDhcpRequired bool `json:"isDhcpRequired,omitempty"`
IsIgmpRequired bool `json:"isIgmpRequired,omitempty"`
ConfiguredMacAddress string `json:"configuredMacAddress,omitempty"`
UsPonCTagPriority uint8 `json:"usPonCTagPriority,omitempty"`
UsPonSTagPriority uint8 `json:"usPonSTagPriority,omitempty"`
DsPonCTagPriority uint8 `json:"dsPonCTagPriority,omitempty"`
DsPonSTagPriority uint8 `json:"dsPonSTagPriority,omitempty"`
ServiceName string `json:"serviceName,omitempty"`
}
// SADIS BandwithProfile Entry
type SadisBWPEntry struct {
// common attributes
ID string `json:"id"`
CBS int `json:"cbs"`
CIR int `json:"cir"`
// MEF attributes
AIR int `json:"air,omitempty"`
EBS int `json:"ebs,omitempty"`
EIR int `json:"eir,omitempty"`
// IETF attributes
GIR int `json:"gir,omitempty"`
PIR int `json:"pir,omitempty"`
PBS int `json:"pbs,omitempty"`
}
// GetSadisConfig returns a full SADIS configuration struct ready to be marshalled into JSON
func GetSadisConfig(olt *devices.OltDevice, version string) *SadisConfig {
sadisEntries, _ := GetSadisEntries(olt, version)
bwpEntries := getBWPEntries(version)
conf := &SadisConfig{}
conf.Sadis = *sadisEntries
conf.BandwidthProfile = *bwpEntries
return conf
}
func GetSadisEntries(olt *devices.OltDevice, version string) (*SadisEntries, error) {
solt, _ := GetOltEntry(olt)
entries := []interface{}{}
entries = append(entries, solt)
a := strings.Split(common.Config.BBSim.SadisRestAddress, ":")
port := a[len(a)-1]
integration := SadisIntegration{}
integration.URL = "http://bbsim:" + port + "/" + version + "/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.Config.Olt.DeviceId,
IPAddress: ip,
NasID: olt.SerialNumber,
UplinkPort: 1048576, // TODO currently assumes we only have one NNI port
}
return solt, nil
}
func GetOnuEntryV2(olt *devices.OltDevice, onu *devices.Onu, uniId string) (*SadisOnuEntryV2, error) {
uniSuffix := "-" + uniId
sonuv2 := &SadisOnuEntryV2{
ID: onu.Sn() + uniSuffix,
}
// createUniTagList
for _, s := range onu.Services {
service := s.(*devices.Service)
tag := SadisUniTag{
ServiceName: service.Name,
IsIgmpRequired: service.NeedsIgmp,
IsDhcpRequired: service.NeedsDhcp,
TechnologyProfileID: service.TechnologyProfileID,
UpstreamBandwidthProfile: "User_Bandwidth1",
DownstreamBandwidthProfile: "User_Bandwidth2",
PonCTag: service.CTag,
PonSTag: service.STag,
}
if service.UniTagMatch != 0 {
tag.UniTagMatch = service.UniTagMatch
}
if service.ConfigureMacAddress {
tag.ConfiguredMacAddress = service.HwAddress.String()
}
if service.UsPonCTagPriority != 0 {
tag.UsPonCTagPriority = service.UsPonCTagPriority
}
if service.UsPonSTagPriority != 0 {
tag.UsPonSTagPriority = service.UsPonSTagPriority
}
if service.DsPonCTagPriority != 0 {
tag.DsPonCTagPriority = service.DsPonCTagPriority
}
if service.DsPonSTagPriority != 0 {
tag.DsPonSTagPriority = service.DsPonSTagPriority
}
sonuv2.UniTagList = append(sonuv2.UniTagList, tag)
}
return sonuv2, nil
}
func getBWPEntries(version string) *BandwidthProfileEntries {
a := strings.Split(common.Config.BBSim.SadisRestAddress, ":")
port := a[len(a)-1]
integration := SadisIntegration{}
integration.URL = "http://bbsim:" + port + "/" + version + "/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)
vars := mux.Vars(r)
if vars["version"] != "v1" && vars["version"] != "v2" {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte("{}"))
return
}
sadisConf := GetSadisConfig(s.Olt, vars["version"])
sadisJSON, _ := json.Marshal(sadisConf)
_, _ = 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)
vars := mux.Vars(r)
sadisConf := GetSadisConfig(s.Olt, vars["version"])
sadisConf.Sadis.Integration.URL = ""
for i := range s.Olt.Pons {
for _, onu := range s.Olt.Pons[i].Onus {
if vars["version"] == "v2" {
sonuV2, _ := GetOnuEntryV2(s.Olt, onu, "1")
sadisConf.Sadis.Entries = append(sadisConf.Sadis.Entries, sonuV2)
}
}
}
sadisConf.BandwidthProfile.Integration.URL = ""
sadisConf.BandwidthProfile.Entries = bandwidthProfiles[common.Config.BBSim.BandwidthProfileFormat]
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 SubscriberId: %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("Requested Subscriber entry not found for OnuSn: %s", vars["ID"])
return
}
sadisLogger.WithFields(log.Fields{
"OnuId": onu.ID,
"OnuSn": sn,
"OnuPortNo": uni,
}).Debug("Received SADIS request")
if vars["version"] == "v1" {
// TODO format error
w.WriteHeader(http.StatusBadRequest)
_ = json.NewEncoder(w).Encode("Sadis v1 is not supported anymore, please go back to an earlier BBSim version")
} else if vars["version"] == "v2" {
w.WriteHeader(http.StatusOK)
sadisConf, _ := GetOnuEntryV2(s.Olt, onu, uni)
_ = 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"]
if vars["version"] != "v1" && vars["version"] != "v2" {
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte("{}"))
return
}
sadisLogger.Debugf("Received request for SADIS bandwidth profile %s", id)
for _, bwpEntry := range bandwidthProfiles[common.Config.BBSim.BandwidthProfileFormat] {
if bwpEntry.ID == id {
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(bwpEntry)
return
}
}
w.WriteHeader(http.StatusNotFound)
_, _ = w.Write([]byte("{}"))
}