| /* |
| * 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 nbi |
| |
| import ( |
| "bytes" |
| "context" |
| "encoding/json" |
| "net" |
| "net/http" |
| "strconv" |
| |
| app "voltha-go-controller/internal/pkg/application" |
| errorCodes "voltha-go-controller/internal/pkg/errorcodes" |
| "voltha-go-controller/internal/pkg/of" |
| "voltha-go-controller/log" |
| |
| "github.com/google/gopacket/layers" |
| "github.com/gorilla/mux" |
| ) |
| |
| // SubscriberDeviceInfo - Subcriber Device Info |
| type SubscriberDeviceInfo struct { |
| ID string `json:"id"` |
| NasPortID string `json:"nasPortId"` |
| UplinkPort string `json:"uplinkPort"` |
| HardwareIdentifier string `json:"hardwareIdentifier"` |
| IPAddress string `json:"ipAddress"` |
| NasID string `json:"nasId"` |
| CircuitID string `json:"circuitId"` |
| RemoteID string `json:"remoteId"` |
| UniTagList []UniTagInformation `json:"uniTagList"` |
| NniDhcpTrapVid int `json:"nniDhcpTrapVid"` |
| Slot int `json:"slot"` |
| } |
| |
| // UniTagInformation - Service information |
| type UniTagInformation struct { |
| UpstreamBandwidthProfile string `json:"upstreamBandwidthProfile"` |
| DownstreamBandwidthProfile string `json:"downstreamBandwidthProfile"` |
| UpstreamOltBandwidthProfile string `json:"upstreamOltBandwidthProfile"` |
| DownstreamOltBandwidthProfile string `json:"downstreamOltBandwidthProfile"` |
| ServiceName string `json:"serviceName"` |
| ConfiguredMacAddress string `json:"configuredMacAddress"` |
| UniTagMatch int `json:"uniTagMatch"` |
| PonCTag int `json:"ponCTag"` |
| PonSTag int `json:"ponSTag"` |
| UsPonCTagPriority int `json:"usPonCTagPriority"` |
| UsPonSTagPriority int `json:"usPonSTagPriority"` |
| DsPonCTagPriority int `json:"dsPonCTagPriority"` |
| DsPonSTagPriority int `json:"dsPonSTagPriority"` |
| TechnologyProfileID int `json:"technologyProfileId"` |
| EnableMacLearning bool `json:"enableMacLearning"` |
| IsDhcpRequired bool `json:"isDhcpRequired"` |
| IsIgmpRequired bool `json:"isIgmpRequired"` |
| IsPppoeRequired bool `json:"isPppoeRequired"` |
| } |
| |
| func init() { |
| // Setup this package so that it's log level can be modified at run time |
| var err error |
| logger, err = log.AddPackageWithDefaultParam() |
| if err != nil { |
| panic(err) |
| } |
| } |
| |
| // SubscriberHandle handle SubscriberInfo Requests |
| type SubscriberHandle struct { |
| } |
| |
| // ServeHTTP to serve http request |
| func (sh *SubscriberHandle) ServeHTTP(w http.ResponseWriter, r *http.Request) { |
| logger.Infow(ctx, "Received-northbound-request", log.Fields{"Method": r.Method, "URL": r.URL}) |
| switch r.Method { |
| case cPost: |
| sh.AddSubscriberInfo(context.Background(), w, r) |
| case cDelete: |
| sh.DelSubscriberInfo(context.Background(), w, r) |
| default: |
| logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method}) |
| err := errorCodes.ErrOperationNotSupported |
| http.Error(w, err.Error(), http.StatusBadRequest) |
| } |
| } |
| |
| func (sh *SubscriberHandle) StatusServeHTTP(w http.ResponseWriter, r *http.Request) { |
| logger.Infow(ctx, "Received-northbound-request", log.Fields{"Method": r.Method, "URL": r.URL}) |
| switch r.Method { |
| case cGet: |
| sh.GetSubscriberAndFlowProvisionStatus(context.Background(), w, r) |
| default: |
| logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method}) |
| } |
| } |
| |
| // AddSubscriberInfo to add service |
| func (sh *SubscriberHandle) AddSubscriberInfo(cntx context.Context, w http.ResponseWriter, r *http.Request) { |
| // Get the payload to process the request |
| d := new(bytes.Buffer) |
| if _, err := d.ReadFrom(r.Body); err != nil { |
| logger.Errorw(ctx, "Error reading buffer", log.Fields{"Reason": err.Error()}) |
| http.Error(w, err.Error(), http.StatusConflict) |
| return |
| } |
| |
| // Unmarshal the request into service configuration structure |
| req := &SubscriberDeviceInfo{} |
| if err := json.Unmarshal(d.Bytes(), req); err != nil { |
| logger.Errorw(ctx, "Failed to Unmarshal Adding Subscriber", log.Fields{"req": req, "Reason": err.Error()}) |
| http.Error(w, err.Error(), http.StatusConflict) |
| return |
| } |
| logger.Infow(ctx, "Received-northbound-add-service-request", log.Fields{"req": req}) |
| |
| //vsCfgList := getVoltServiceFromSrvInfo(req) |
| |
| addAllService(cntx, req) |
| } |
| |
| func addAllService(cntx context.Context, srvInfo *SubscriberDeviceInfo) { |
| var voltAppIntr app.VoltAppInterface |
| voltApp := app.GetApplication() |
| voltAppIntr = voltApp |
| if len(srvInfo.UniTagList) == 0 { |
| logger.Infow(ctx, "Received OLT configuration", log.Fields{"req": srvInfo}) |
| err := voltAppIntr.AddDeviceConfig(cntx, srvInfo.ID, srvInfo.HardwareIdentifier, srvInfo.NasID, srvInfo.IPAddress, srvInfo.UplinkPort, srvInfo.NniDhcpTrapVid) |
| if err != nil { |
| logger.Warnw(ctx, "Device config addition failed :", log.Fields{"req": srvInfo, "Reason": err.Error()}) |
| } |
| return |
| } |
| for _, uniTagInfo := range srvInfo.UniTagList { |
| var vs app.VoltServiceCfg |
| |
| svcname := srvInfo.ID + "_" |
| svcname = svcname + srvInfo.NasPortID + "-" |
| svcname = svcname + strconv.Itoa(uniTagInfo.UniTagMatch) + "-" |
| svcname = svcname + strconv.Itoa(uniTagInfo.PonSTag) + "-" |
| svcname = svcname + strconv.Itoa(uniTagInfo.PonCTag) + "-" |
| vs.Name = svcname + strconv.Itoa(uniTagInfo.TechnologyProfileID) |
| |
| vs.Port = srvInfo.NasPortID |
| vs.SVlan = of.VlanType(uniTagInfo.PonSTag) |
| vs.CVlan = of.VlanType(uniTagInfo.PonCTag) |
| vs.UniVlan = of.VlanType(uniTagInfo.UniTagMatch) |
| vs.UsPonCTagPriority = of.PbitType(uniTagInfo.UsPonCTagPriority) |
| vs.UsPonSTagPriority = of.PbitType(uniTagInfo.UsPonSTagPriority) |
| vs.DsPonCTagPriority = of.PbitType(uniTagInfo.UsPonCTagPriority) |
| vs.DsPonSTagPriority = of.PbitType(uniTagInfo.UsPonSTagPriority) |
| vs.TechProfileID = uint16(uniTagInfo.TechnologyProfileID) |
| vs.UsMeterProfile = uniTagInfo.UpstreamBandwidthProfile |
| vs.DsMeterProfile = uniTagInfo.DownstreamBandwidthProfile |
| vs.IgmpEnabled = uniTagInfo.IsIgmpRequired |
| vs.ServiceType = uniTagInfo.ServiceName |
| |
| // Check if the service already exists for same Uniport and TechProfID |
| if voltApp.CheckServiceExists(vs.Port, vs.TechProfileID) { |
| logger.Warnw(ctx, "Service already exists for same port and TP Id", log.Fields{"ServiceName": vs.Name, "Port": vs.Port, "TechProfileID": vs.TechProfileID, "SVlan": vs.SVlan}) |
| continue |
| } |
| |
| logger.Debugw(ctx, "", log.Fields{"ServiceName": vs.Name}) |
| |
| if uniTagInfo.ServiceName == app.DpuMgmtTraffic || |
| uniTagInfo.ServiceName == app.DpuAncpTraffic || |
| uniTagInfo.ServiceName == app.FttbSubscriberTraffic { |
| vs.UniVlan = vs.CVlan |
| vs.Pbits = append(vs.Pbits, of.PbitMatchAll) |
| } else { |
| if uniTagInfo.UsPonSTagPriority == -1 { |
| vs.Pbits = append(vs.Pbits, of.PbitMatchAll) |
| // Process the p-bits received in the request |
| } else { |
| if uniTagInfo.UsPonSTagPriority < 8 { |
| vs.Pbits = append(vs.Pbits, of.PbitType(uniTagInfo.UsPonCTagPriority)) |
| } |
| |
| if uniTagInfo.UsPonSTagPriority < 8 && uniTagInfo.UsPonSTagPriority != uniTagInfo.DsPonSTagPriority { |
| vs.Pbits = append(vs.Pbits, of.PbitType(uniTagInfo.DsPonCTagPriority)) |
| } |
| } |
| } |
| //vs.McastService = uniTagInfo.IsIgmpRequired |
| if vs.IgmpEnabled { |
| vs.MvlanProfileName = "mvlan" + strconv.Itoa(uniTagInfo.PonSTag) |
| } |
| /* |
| var err error |
| if vs.MacAddr, err = net.ParseMAC(srvInfo.HardwareIdentifier); err != nil { |
| vs.MacAddr, _ = net.ParseMAC("00:00:00:00:00:00") |
| }*/ |
| |
| vs.MacAddr, _ = net.ParseMAC("00:00:00:00:00:00") |
| if len(vs.Pbits) == 0 { |
| vs.Pbits = append(vs.Pbits, of.PbitMatchNone) |
| } |
| |
| vnetName := strconv.FormatUint(uint64(vs.SVlan), 10) + "-" |
| vnetName = vnetName + strconv.FormatUint(uint64(vs.CVlan), 10) + "-" |
| vnetName = vnetName + strconv.FormatUint(uint64(vs.UniVlan), 10) |
| |
| logger.Debugw(ctx, "", log.Fields{"VnetName": vnetName}) |
| |
| vnetcfg := app.VnetConfig{ |
| Name: vnetName, |
| SVlan: vs.SVlan, |
| CVlan: vs.CVlan, |
| UniVlan: vs.UniVlan, |
| SVlanTpid: layers.EthernetTypeDot1Q, |
| DhcpRelay: uniTagInfo.IsDhcpRequired, |
| VnetType: uniTagInfo.ServiceName, |
| UsPonCTagPriority: vs.UsPonCTagPriority, |
| UsPonSTagPriority: vs.UsPonSTagPriority, |
| DsPonCTagPriority: vs.UsPonCTagPriority, |
| DsPonSTagPriority: vs.UsPonSTagPriority, |
| //ONTEtherTypeClassification: req.ONTEtherTypeClassification, |
| //VlanControl: app.VlanControl(req.VlanControl), //TODO |
| } |
| if uniTagInfo.EnableMacLearning { |
| vnetcfg.MacLearning = app.Learn |
| } |
| if uniTagInfo.UsPonSTagPriority < 8 { |
| vnetcfg.UsDhcpPbit = append(vnetcfg.UsDhcpPbit, of.PbitType(uniTagInfo.UsPonSTagPriority)) |
| } |
| if vs.CVlan != of.VlanAny && vs.SVlan != of.VlanAny { |
| if uniTagInfo.ServiceName == app.DpuMgmtTraffic || |
| uniTagInfo.ServiceName == app.DpuAncpTraffic { |
| vnetcfg.VlanControl = app.ONUCVlan |
| } else if uniTagInfo.ServiceName == app.FttbSubscriberTraffic { |
| vnetcfg.VlanControl = app.OLTSVlan |
| } else { |
| vnetcfg.VlanControl = app.ONUCVlanOLTSVlan |
| } |
| } else if vs.CVlan == of.VlanAny && vs.UniVlan == of.VlanAny { |
| vnetcfg.VlanControl = app.OLTSVlan |
| } |
| if err := voltAppIntr.AddVnet(cntx, vnetcfg, nil); err != nil { |
| logger.Errorw(ctx, "AddVnet Failed", log.Fields{"VnetName": vnetName, "Error": err}) |
| } |
| if err := voltAppIntr.AddService(cntx, vs, nil); err != nil { |
| logger.Errorw(ctx, "AddService Failed", log.Fields{"Service": vs.Name, "Error": err.Error()}) |
| } |
| } |
| } |
| |
| // DelSubscriberInfo to delete service |
| func (sh *SubscriberHandle) DelSubscriberInfo(cntx context.Context, w http.ResponseWriter, r *http.Request) { |
| vars := mux.Vars(r) |
| id := vars["id"] |
| |
| d := new(bytes.Buffer) |
| if _, err := d.ReadFrom(r.Body); err != nil { |
| logger.Errorw(ctx, "Error reading buffer", log.Fields{"req id": id, "Reason": err.Error()}) |
| w.WriteHeader(http.StatusConflict) |
| return |
| } |
| |
| // Unmarshal the request into service configuration structure |
| req := &SubscriberDeviceInfo{} |
| if err := json.Unmarshal(d.Bytes(), req); err != nil { |
| logger.Warnw(ctx, "Unmarshal Failed", log.Fields{"Reason": err.Error()}) |
| http.Error(w, err.Error(), http.StatusConflict) |
| return |
| } |
| |
| for _, uniTagInfo := range req.UniTagList { |
| svcname := req.ID + "_" |
| svcname = svcname + req.NasPortID + "-" |
| svcname = svcname + strconv.Itoa(uniTagInfo.UniTagMatch) + "-" |
| svcname = svcname + strconv.Itoa(uniTagInfo.PonSTag) + "-" |
| svcname = svcname + strconv.Itoa(uniTagInfo.PonCTag) + "-" |
| svcname = svcname + strconv.Itoa(uniTagInfo.TechnologyProfileID) |
| |
| if uniTagInfo.ServiceName == app.FttbSubscriberTraffic { |
| id = svcname |
| } |
| } |
| |
| logger.Infow(ctx, "Received northbound-del-service-req", log.Fields{"ServiceName": id}) |
| err := app.GetApplication().DelServiceWithPrefix(cntx, id) |
| if err != nil { |
| logger.Warnw(ctx, "northbound-del-service-req failed, Subscriber not exist", log.Fields{"ServiceName": id}) |
| http.Error(w, err.Error(), http.StatusBadRequest) |
| return |
| } |
| |
| // HTTP response with 202 accepted for service delete request |
| w.WriteHeader(http.StatusAccepted) |
| } |
| |
| // DelSubscriberInfo to delete service |
| func (sh *SubscriberHandle) GetSubscriberAndFlowProvisionStatus(cntx context.Context, w http.ResponseWriter, r *http.Request) { |
| vars := mux.Vars(r) |
| portName := vars["portName"] |
| logger.Debugw(ctx, "Received-northbound-GetSubscriberProvisionStatus-request", log.Fields{"req": portName}) |
| var voltAppIntr app.VoltAppInterface |
| voltApp := app.GetApplication() |
| voltAppIntr = voltApp |
| flowProvisionStatus := voltAppIntr.GetFlowProvisionStatus(portName) |
| flowProvisionStatusRes, err := json.Marshal(flowProvisionStatus) |
| if err != nil { |
| logger.Errorw(ctx, "Error occurred while marshaling flowProvisionStatus response", log.Fields{"Error": err}) |
| w.WriteHeader(http.StatusInternalServerError) |
| return |
| } |
| w.Header().Add("Content-Type", "application/json") |
| _, err = w.Write(flowProvisionStatusRes) |
| if err != nil { |
| logger.Errorw(ctx, "error in sending flowProvisionStatus response", log.Fields{"Error": err}) |
| w.WriteHeader(http.StatusInternalServerError) |
| } |
| } |