[VOL-4814] Adding Rest interfaces for flow and subscribers
Change-Id: I7da50b14e7600884a8b38c37b63704241942d8af
diff --git a/internal/pkg/application/application.go b/internal/pkg/application/application.go
index e7d2d04..04f4a6a 100644
--- a/internal/pkg/application/application.go
+++ b/internal/pkg/application/application.go
@@ -1283,12 +1283,17 @@
for _, vpv := range vpvs.([]*VoltPortVnet) {
vpv.VpvLock.Lock()
-
- //Do not trigger indication for the vpv which is already removed from vpv list as
- // part of service delete (during the lock wait duration)
- // In that case, the services associated wil be zero
- if vpv.servicesCount.Load() != 0 {
- vpv.PortUpInd(cntx, d, port)
+ //If no service is activated drop the portUpInd
+ if vpv.IsServiceActivated(cntx) {
+ //Do not trigger indication for the vpv which is already removed from vpv list as
+ // part of service delete (during the lock wait duration)
+ // In that case, the services associated wil be zero
+ if vpv.servicesCount.Load() != 0 {
+ vpv.PortUpInd(cntx, d, port)
+ }
+ } else {
+ // Service not activated, still attach device to service
+ vpv.setDevice(d.Name)
}
vpv.VpvLock.Unlock()
}
diff --git a/internal/pkg/application/dhcprelay.go b/internal/pkg/application/dhcprelay.go
index b779f1f..dd92383 100644
--- a/internal/pkg/application/dhcprelay.go
+++ b/internal/pkg/application/dhcprelay.go
@@ -21,6 +21,7 @@
"errors"
"net"
"sync"
+ "time"
"github.com/google/gopacket"
"github.com/google/gopacket/layers"
@@ -1342,3 +1343,53 @@
func init() {
dhcpNws = NewDhcpNetworks()
}
+
+type DhcpAllocation struct {
+ SubscriberID string `json:"subscriberId"`
+ ConnectPoint string `json:"connectPoint"`
+ MacAddress net.HardwareAddr `json:"macAddress"`
+ State int `json:"state"`
+ VlanID int `json:"vlanId"`
+ CircuitID []byte `json:"circuitId"`
+ IpAllocated net.IP `json:"ipAllocated"`
+ AllocationTimeStamp time.Time `json:"allocationTimestamp"`
+}
+
+// GetAllocations returns DhcpAllocation info for all devices or for a device ID
+func (va *VoltApplication) GetAllocations(cntx context.Context, deviceID string) ([]*DhcpAllocation, error) {
+ logger.Debugw(ctx, "GetAllocations", log.Fields{"DeviceID": deviceID})
+ var allocations []*DhcpAllocation
+ for _, drv := range dhcpNws.Networks {
+ drv.sessionLock.RLock()
+ for _, session := range drv.sessions {
+ vpv, ok := session.(*VoltPortVnet)
+ if ok {
+ var subscriber string
+ // return Name of first service
+ vpv.services.Range(func(key, value interface{}) bool {
+ svc := value.(*VoltService)
+ subscriber = svc.Name
+ return false
+ })
+ // If deviceID is not provided, return all allocations
+ // If deviceID exists then filter on deviceID
+ if len(deviceID) == 0 || deviceID == vpv.Device {
+ allocation := &DhcpAllocation {
+ SubscriberID : subscriber,
+ ConnectPoint : vpv.Device,
+ MacAddress : vpv.MacAddr,
+ State : int(vpv.RelayState) ,
+ VlanID : int(vpv.SVlan) ,
+ CircuitID : vpv.CircuitID ,
+ IpAllocated : vpv.Ipv4Addr ,
+ AllocationTimeStamp : vpv.DhcpExpiryTime,
+ }
+ logger.Debugw(ctx, "DHCP Allocation found", log.Fields{"DhcpAlloc": allocation})
+ allocations = append(allocations, allocation)
+ }
+ }
+ }
+ drv.sessionLock.RUnlock()
+ }
+ return allocations, nil
+}
diff --git a/internal/pkg/application/service.go b/internal/pkg/application/service.go
index 690f348..af903af 100644
--- a/internal/pkg/application/service.go
+++ b/internal/pkg/application/service.go
@@ -42,6 +42,8 @@
const (
// DSLAttrEnabled constant
DSLAttrEnabled string = "ENABLED"
+ // DeviceAny constant
+ DeviceAny string = "DEVICE-ANY"
)
// VoltServiceCfg structure
@@ -95,7 +97,7 @@
MinDataRateDs uint32
MaxDataRateUs uint32
MaxDataRateDs uint32
-
+ IsActivated bool
Trigger ServiceTrigger
}
@@ -717,9 +719,10 @@
subflow1.SetGoToTable(1)
subflow1.SetInPort(inport)
+ /*
if pbits != PbitMatchNone {
subflow1.SetMatchPbit(pbits)
- }
+ }*/
if err := vs.setUSMatchActionVlanT0(subflow1); err != nil {
return nil, err
}
@@ -2005,3 +2008,124 @@
},
})
}
+
+// GetProgrammedSubscribers to get list of programmed subscribers
+func (va *VoltApplication) GetProgrammedSubscribers (cntx context.Context, deviceID, portNo string) ([]*VoltService, error) {
+ var svcList []*VoltService
+ logger.Infow(ctx, "GetProgrammedSubscribers Request ", log.Fields{"Device": deviceID, "Port": portNo})
+ va.ServiceByName.Range(func(key, value interface{}) bool {
+ vs := value.(*VoltService)
+ if (len(deviceID) > 0 ) {
+ if (len(portNo) > 0) {
+ if deviceID == vs.Device && portNo == vs.Port {
+ svcList = append(svcList, vs)
+ }
+ } else {
+ if deviceID == vs.Device {
+ svcList = append(svcList, vs)
+ }
+ }
+ } else {
+ svcList = append(svcList, vs)
+ }
+ return true
+ })
+ return svcList, nil
+}
+
+// ActivateService to activate pre-provisioned service
+func (va *VoltApplication) ActivateService(cntx context.Context, deviceID, portNo string, sVlan, cVlan of.VlanType, tpID uint16) {
+ logger.Infow(ctx, "Service Activate Request ", log.Fields{"Device": deviceID, "Port": portNo})
+ va.ServiceByName.Range(func(key, value interface{}) bool {
+ vs := value.(*VoltService)
+ // If device id is not provided check only port number
+ if deviceID == DeviceAny {
+ deviceID = vs.Device
+ }
+ // If svlan if provided, then the tags and tpID of service has to be matching
+ if (sVlan != of.VlanNone && ( sVlan != vs.SVlan || cVlan != vs.CVlan || tpID != vs.TechProfileID) ) {
+ return true
+ }
+ if portNo == vs.Port && !vs.IsActivated {
+ d := va.GetDevice(deviceID)
+ if d == nil {
+ logger.Warnw(ctx, "Device Not Found", log.Fields{"Device": deviceID})
+ return true
+ }
+ p := d.GetPort(vs.Port)
+ if p == nil {
+ logger.Warnw(ctx, "Wrong device or port", log.Fields{"Device": deviceID, "Port": portNo})
+ return true
+ }
+ logger.Infow(ctx, "Service Activate", log.Fields{"Name": vs.Name})
+ vs.IsActivated = true
+ va.ServiceByName.Store(vs.Name, vs)
+ vs.WriteToDb(cntx)
+ // If port is already up send indication to vpv
+ if p.State == PortStateUp {
+ if vpv := va.GetVnetByPort(vs.Port, vs.SVlan, vs.CVlan, vs.UniVlan); vpv != nil {
+ // PortUp call initiates flow addition
+ vpv.PortUpInd(cntx, d, portNo)
+ } else {
+ logger.Warnw(ctx, "VPV does not exists!!!", log.Fields{"Device": deviceID, "port": portNo, "SvcName": vs.Name})
+ }
+ }
+ }
+ return true
+ })
+}
+
+// DeactivateService to activate pre-provisioned service
+func (va *VoltApplication) DeactivateService(cntx context.Context, deviceID, portNo string, sVlan, cVlan of.VlanType, tpID uint16) {
+ logger.Infow(ctx, "Service Deactivate Request ", log.Fields{"Device": deviceID, "Port": portNo})
+ va.ServiceByName.Range(func(key, value interface{}) bool {
+ vs := value.(*VoltService)
+ // If svlan if provided, then the tags and tpID of service has to be matching
+ if (sVlan != of.VlanNone && ( sVlan != vs.SVlan || cVlan != vs.CVlan || tpID != vs.TechProfileID) ) {
+ return true
+ }
+ // If device id is not provided check only port number
+ if deviceID == DeviceAny {
+ deviceID = vs.Device
+ }
+ if deviceID == vs.Device && portNo == vs.Port && vs.IsActivated {
+ vs.IsActivated = false
+ va.ServiceByName.Store(vs.Name, vs)
+ vs.WriteToDb(cntx)
+ d := va.GetDevice(deviceID)
+ if d == nil {
+ logger.Warnw(ctx, "Device Not Found", log.Fields{"Device": deviceID})
+ return true
+ }
+ p := d.GetPort(vs.Port)
+ if p != nil && p.State == PortStateUp {
+ if vpv := va.GetVnetByPort(vs.Port, vs.SVlan, vs.CVlan, vs.UniVlan); vpv != nil {
+ // Port down call internally deletes all the flows
+ vpv.PortDownInd(cntx, deviceID, portNo)
+ if vpv.IgmpEnabled {
+ va.ReceiverDownInd(cntx, deviceID, portNo)
+ }
+ } else {
+ logger.Warnw(ctx, "VPV does not exists!!!", log.Fields{"Device": deviceID, "port": portNo, "SvcName": vs.Name})
+ }
+ }
+ }
+ return true
+ })
+}
+
+/* GetServicePbit to get first set bit in the pbit map
+ returns -1 : If configured to match on all pbits
+ returns 8 : If no pbits are configured
+ returns first pbit if specific pbit is configured */
+func (vs *VoltService) GetServicePbit() int {
+ if vs.IsPbitExist(of.PbitMatchAll) {
+ return -1
+ }
+ for pbit:= 0; pbit < int(of.PbitMatchNone); pbit++ {
+ if vs.IsPbitExist(of.PbitType(pbit)) {
+ return pbit
+ }
+ }
+ return int(of.PbitMatchNone)
+}
diff --git a/internal/pkg/application/vnets.go b/internal/pkg/application/vnets.go
index e11960c..85de6dc 100644
--- a/internal/pkg/application/vnets.go
+++ b/internal/pkg/application/vnets.go
@@ -513,7 +513,7 @@
vpv.printAssociatedVPVs(false)
}
- logger.Infow(ctx, "Associating VPV and Device", log.Fields{"Device": vpv.Device, "Port": vpv.Port, "SVlan": vpv.SVlan})
+ logger.Infow(ctx, "Associating VPV and Device", log.Fields{"Device": device, "Port": vpv.Port, "SVlan": vpv.SVlan})
vpv.Device = device
GetApplication().AssociateVpvsToDevice(device, vpv)
@@ -1036,7 +1036,10 @@
//TODO-COMM: vpv.FlowInstallFailure("VGC processing failure", statusCode, statusMessage)
return
}
-
+ if !svc.IsActivated {
+ logger.Warn(ctx, "Not pushing Service Flows: Service Not activated")
+ return
+ }
//Push Service Flows if DHCP relay is not configured
//or already DHCP flows are configured for the VPV
//to which the serivce is associated
@@ -2244,6 +2247,17 @@
// Add the service that is causing the VNET to be added to the port
vpv.AddSvc(cntx, vs)
+ if !vs.IsActivated {
+ logger.Warn(ctx, "Not Checking port state: Service Not activated")
+ // Process the PORT UP if the port is already up
+ d, err := va.GetDeviceFromPort(port)
+ if err == nil {
+ vpv.setDevice(d.Name)
+ }
+ vpv.WriteToDb(cntx)
+ return vpv
+ }
+
// Process the PORT UP if the port is already up
d, err := va.GetDeviceFromPort(port)
if err == nil {
@@ -3206,3 +3220,17 @@
DhcpPbit: vpv.DhcpPbit,
})
}
+
+func (vpv *VoltPortVnet) IsServiceActivated(cntx context.Context) bool {
+ isActivated := false
+ vpv.services.Range(func(key, value interface{}) bool {
+ svc := value.(*VoltService)
+ if svc.IsActivated {
+ logger.Infow(ctx, "Found activated service on the vpv", log.Fields{"Name": svc.Name})
+ isActivated = true
+ return false //to exit loop
+ }
+ return true
+ })
+ return isActivated
+}
diff --git a/internal/pkg/controller/controller.go b/internal/pkg/controller/controller.go
index f08f7e1..eb48c28 100644
--- a/internal/pkg/controller/controller.go
+++ b/internal/pkg/controller/controller.go
@@ -521,3 +521,44 @@
_, ifPresent := v.BlockedDeviceList.Get(deviceSerialNumber)
return ifPresent
}
+
+// GetFlows returns flow specific to device and flowID
+func (v *VoltController) GetFlow(deviceID string, cookie uint64) (*of.VoltSubFlow, error) {
+ d, err := v.GetDevice(deviceID)
+ if err != nil {
+ logger.Errorw(ctx, "Device Not Found", log.Fields{"Device": deviceID, "Error": err})
+ return nil, err
+ }
+ if flow, ok := d.GetFlow(cookie); ok {
+ return flow, nil
+ }
+ return nil, nil
+}
+
+// GetFlows returns list of flows for a particular device
+func (v *VoltController) GetFlows(deviceID string) ([]*of.VoltSubFlow, error) {
+ d, err := v.GetDevice(deviceID)
+ if err != nil {
+ logger.Errorw(ctx, "Device Not Found", log.Fields{"Device": deviceID, "Error": err})
+ return nil, err
+ }
+ return d.GetAllFlows(), nil
+}
+
+// GetAllFlows returns list of all flows
+func (v *VoltController) GetAllFlows() ([]*of.VoltSubFlow, error) {
+ var flows []*of.VoltSubFlow
+ for _, d := range v.devices {
+ flows = append(flows, d.GetAllFlows()...)
+ }
+ return flows, nil
+}
+
+// GetAllPendingFlows returns list of all flows
+func (v *VoltController) GetAllPendingFlows() ([]*of.VoltSubFlow, error) {
+ var flows []*of.VoltSubFlow
+ for _, d := range v.devices {
+ flows = append(flows, d.GetAllPendingFlows()...)
+ }
+ return flows, nil
+}
diff --git a/internal/pkg/controller/device.go b/internal/pkg/controller/device.go
index b410490..d60855a 100644
--- a/internal/pkg/controller/device.go
+++ b/internal/pkg/controller/device.go
@@ -176,6 +176,32 @@
return flow, ok
}
+// GetAllFlows - Get the flow from device obj
+func (d *Device) GetAllFlows() ([]*of.VoltSubFlow) {
+ d.flowLock.RLock()
+ defer d.flowLock.RUnlock()
+ var flows []*of.VoltSubFlow
+ logger.Infow(ctx, "Get All Flows", log.Fields{"deviceID": d.ID})
+ for _, f := range d.flows {
+ flows = append(flows, f)
+ }
+ return flows
+}
+
+// GetAllPendingFlows - Get the flow from device obj
+func (d *Device) GetAllPendingFlows() ([]*of.VoltSubFlow) {
+ d.flowLock.RLock()
+ defer d.flowLock.RUnlock()
+ var flows []*of.VoltSubFlow
+ logger.Infow(ctx, "Get All Pending Flows", log.Fields{"deviceID": d.ID})
+ for _, f := range d.flows {
+ if f.State == of.FlowAddPending {
+ flows = append(flows, f)
+ }
+ }
+ return flows
+}
+
// AddFlow - Adds the flow to the device and also to the database
func (d *Device) AddFlow(cntx context.Context, flow *of.VoltSubFlow) error {
d.flowLock.Lock()
diff --git a/voltha-go-controller/nbi/rest.go b/voltha-go-controller/nbi/rest.go
index 77e4b71..e768cb4 100644
--- a/voltha-go-controller/nbi/rest.go
+++ b/voltha-go-controller/nbi/rest.go
@@ -22,19 +22,48 @@
"github.com/gorilla/mux"
"voltha-go-controller/log"
+ "voltha-go-controller/voltha-go-controller/onos_nbi"
)
var logger log.CLogger
var ctx = context.TODO()
+const (
+ SubscribersPath string = "/subscribers/{id}"
+ ProfilesPath string = "/profiles/{id}"
+ IgmpProxyPath string = "/igmp-proxy/"
+ MulticastPath string = "/multicast/"
+ FlowsPath string = "/flows/"
+ FlowsPerDeviceIDPath string = "/flows/{deviceId}"
+ FlowPerDeviceIDFlowIDPath string = "/flows/{deviceId}/{flowId}"
+ PendingFlowsPath string = "/flows/pending/"
+ ProgrammedSubscribersPath string = "/programmed-subscribers/"
+ ServiceDevicePortPath string = "/services/{device}/{port}"
+ ServicePortNamePath string = "/services/{portName}"
+ ServicePortStagCtagTpIDPath string = "/services/{portName}/{sTag}/{cTag}/{tpId}"
+ AllocationsPath string = "/allocations/"
+ AllocationsDeviceIDPath string = "/allocations/{deviceId}"
+)
// RestStart to execute for API
func RestStart() {
mu := mux.NewRouter()
logger.Info(ctx, "Rest Server Starting...")
- mu.HandleFunc("/subscribers/{id}", (&SubscriberHandle{}).ServeHTTP)
- mu.HandleFunc("/profiles/{id}", (&ProfileHandle{}).ServeHTTP)
- mu.HandleFunc("/igmp-proxy/", (&IgmpProxyHandle{}).ServeHTTP)
- mu.HandleFunc("/multicast/", (&MulticastHandle{}).ServeHTTP)
+ mu.HandleFunc(SubscribersPath, (&SubscriberHandle{}).ServeHTTP)
+ mu.HandleFunc(ProfilesPath, (&ProfileHandle{}).ServeHTTP)
+ mu.HandleFunc(IgmpProxyPath, (&IgmpProxyHandle{}).ServeHTTP)
+ mu.HandleFunc(MulticastPath, (&MulticastHandle{}).ServeHTTP)
+
+ mu.HandleFunc(FlowsPath, (&onos_nbi.FlowHandle{}).ServeHTTP)
+ mu.HandleFunc(FlowsPerDeviceIDPath, (&onos_nbi.FlowHandle{}).ServeHTTP)
+ mu.HandleFunc(FlowPerDeviceIDFlowIDPath, (&onos_nbi.FlowHandle{}).ServeHTTP)
+ mu.HandleFunc(PendingFlowsPath, (&onos_nbi.PendingFlowHandle{}).ServeHTTP)
+ mu.HandleFunc(ProgrammedSubscribersPath, (&onos_nbi.ServiceAdapter{}).ServeHTTP)
+ mu.HandleFunc(ServiceDevicePortPath, (&onos_nbi.ServiceAdapter{}).ServeHTTP)
+ mu.HandleFunc(ServicePortNamePath, (&onos_nbi.ServiceAdapter{}).ServeHTTPWithPortName)
+ mu.HandleFunc(ServicePortStagCtagTpIDPath, (&onos_nbi.ServiceAdapter{}).ServeHTTPWithPortName)
+ mu.HandleFunc(AllocationsPath, (&onos_nbi.DhcpRelayHandle{}).ServeHTTP)
+ mu.HandleFunc(AllocationsDeviceIDPath, (&onos_nbi.DhcpRelayHandle{}).ServeHTTP)
+
err := http.ListenAndServe(":8181", mu)
logger.Infow(ctx, "Rest Server Started", log.Fields{"Error": err})
}
diff --git a/voltha-go-controller/nbi/sadissubscriber.go b/voltha-go-controller/nbi/sadissubscriber.go
index db27a95..0bfe39c 100644
--- a/voltha-go-controller/nbi/sadissubscriber.go
+++ b/voltha-go-controller/nbi/sadissubscriber.go
@@ -67,6 +67,16 @@
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 {
}
@@ -134,13 +144,17 @@
if vs.IgmpEnabled {
vs.MvlanProfileName = "mvlan" + strconv.Itoa(uniTagInfo.PonSTag)
}
+ if uniTagInfo.UsPonSTagPriority == -1 {
+ vs.Pbits = append(vs.Pbits, of.PbitMatchAll)
// Process the p-bits received in the request
- if uniTagInfo.UsPonSTagPriority < 8 {
- vs.Pbits = append(vs.Pbits, of.PbitType(uniTagInfo.UsPonCTagPriority))
- }
+ } 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))
+ if uniTagInfo.UsPonSTagPriority < 8 && uniTagInfo.UsPonSTagPriority != uniTagInfo.DsPonSTagPriority {
+ vs.Pbits = append(vs.Pbits, of.PbitType(uniTagInfo.DsPonCTagPriority))
+ }
}
/*
diff --git a/voltha-go-controller/onos_nbi/dhcprelayadapter.go b/voltha-go-controller/onos_nbi/dhcprelayadapter.go
new file mode 100644
index 0000000..0c0601d
--- /dev/null
+++ b/voltha-go-controller/onos_nbi/dhcprelayadapter.go
@@ -0,0 +1,80 @@
+/*
+* 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 onos_nbi
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+
+ "github.com/gorilla/mux"
+ "voltha-go-controller/log"
+ app "voltha-go-controller/internal/pkg/application"
+)
+
+var logger log.CLogger
+var ctx = context.TODO()
+
+const DeviceID string = "deviceId"
+
+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)
+ }
+}
+
+// DhcpRelayHandle struct to handle dhcprelay related REST calls
+type DhcpRelayHandle struct {
+}
+
+func (dh *DhcpRelayHandle) 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 "GET":
+ dh.GetAllocations(context.Background(), w, r)
+ default:
+ logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method})
+ }
+}
+
+func (dh *DhcpRelayHandle) GetAllocations(cntx context.Context, w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ deviceID := vars[DeviceID]
+ Allocations, err := app.GetApplication().GetAllocations(cntx, deviceID)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to get dhcp allocations", log.Fields{"Reason": err.Error()})
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ AllocRespJSON, err := json.Marshal(Allocations)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to Marshal dhcp allocation response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Add("Content-Type", "application/json")
+ _, err = w.Write(AllocRespJSON)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to write dhcp allocations response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+
+}
diff --git a/voltha-go-controller/onos_nbi/flowadapter.go b/voltha-go-controller/onos_nbi/flowadapter.go
new file mode 100644
index 0000000..f4a2b2d
--- /dev/null
+++ b/voltha-go-controller/onos_nbi/flowadapter.go
@@ -0,0 +1,149 @@
+/*
+* 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 onos_nbi
+
+import (
+ "context"
+ "encoding/json"
+ "net/http"
+ "strconv"
+
+ "github.com/gorilla/mux"
+ "voltha-go-controller/internal/pkg/of"
+ "voltha-go-controller/log"
+ cntlr "voltha-go-controller/internal/pkg/controller"
+)
+
+// FlowHandle struct to handle flow related REST calls
+type FlowHandle struct {
+}
+
+// FlowHandle struct to handle flow related REST calls
+type PendingFlowHandle struct {
+}
+
+type TrafficSelector struct {
+
+}
+
+type TrafficTreatment struct {
+
+}
+
+/*
+type FlowEntry struct {
+ TrafficSelector
+ TrafficTreatment
+ FlowID int
+ AppID int
+ GroupID int
+ Priority int
+ DeviceID string
+ TimeOut int
+ TableID int
+}*/
+
+func (fh *FlowHandle) 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 "GET":
+ fh.GetFlows(context.Background(), w, r)
+ default:
+ logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method})
+ }
+}
+
+func (pfh *PendingFlowHandle) 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 "GET":
+ pfh.GetPendingFlows(context.Background(), w, r)
+ default:
+ logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method})
+ }
+}
+
+func (pfh *PendingFlowHandle) GetPendingFlows(cntx context.Context, w http.ResponseWriter, r *http.Request) {
+ flows, err := cntlr.GetController().GetAllPendingFlows()
+ if err != nil {
+ logger.Errorw(ctx, "Failed to get Pending flows", log.Fields{"Error": err})
+ return
+ }
+ flowResp := ConvertFlowsToFlowEntry(flows)
+ FlowRespJSON, err := json.Marshal(flowResp)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to marshal pending flow response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Add("Content-Type", "application/json")
+ _, err = w.Write(FlowRespJSON)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to write Pending Flow response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+
+}
+
+func (fh *FlowHandle) GetFlows(cntx context.Context, w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ deviceID := vars["deviceId"]
+ flowIDStr := vars["flowId"]
+ flowID, _ := strconv.ParseUint(flowIDStr, 10, 64)
+ var flowResp FlowEntry
+ if len(deviceID) > 0 && len(flowIDStr) > 0 {
+ flow, err := fh.getFlow(deviceID, flowID)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to Fetch flow", log.Fields{"Error": err})
+ return
+ }
+ flowResp = ConvertFlowToFlowEntry(flow)
+ //flowResp = append(flowResp, flow)
+ } else {
+ flows, err := fh.getAllFlows(deviceID)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to Fetch flows", log.Fields{"Error": err})
+ return
+ }
+ flowResp = ConvertFlowsToFlowEntry(flows)
+ //..flowResp = append(flowResp, flows...)
+ }
+ FlowRespJSON, err := json.Marshal(flowResp)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to marshal flow response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Add("Content-Type", "application/json")
+ _, err = w.Write(FlowRespJSON)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to write flow response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+}
+
+func (fh *FlowHandle) getAllFlows(deviceID string) ([]*of.VoltSubFlow, error) {
+ if len(deviceID) == 0 {
+ return cntlr.GetController().GetAllFlows()
+ }
+ return cntlr.GetController().GetFlows(deviceID)
+}
+
+func (fh *FlowHandle) getFlow(deviceID string, flowID uint64) (*of.VoltSubFlow, error) {
+ return cntlr.GetController().GetFlow(deviceID, flowID)
+}
diff --git a/voltha-go-controller/onos_nbi/models.go b/voltha-go-controller/onos_nbi/models.go
new file mode 100644
index 0000000..101f160
--- /dev/null
+++ b/voltha-go-controller/onos_nbi/models.go
@@ -0,0 +1,576 @@
+/*
+* 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 onos_nbi
+
+import (
+ "strconv"
+ "voltha-go-controller/internal/pkg/of"
+ app "voltha-go-controller/internal/pkg/application"
+)
+
+const (
+ /** Switch input port. */
+ IN_PORT string = "IN_PORT"
+
+ /** Switch physical input port. */
+ IN_PHY_PORT string = "IN_PHY_PORT"
+
+ /** Metadata passed between tables. */
+ METADATA string = "METADATA"
+
+ /** Ethernet destination address. */
+ ETH_DST string = "ETH_DST"
+
+ /** Ethernet destination address with masking. */
+ ETH_DST_MASKED = "ETH_DST_MASKED"
+
+ /** Ethernet source address. */
+ ETH_SRC string = "ETH_SRC"
+
+ /** Ethernet source address with masking. */
+ ETH_SRC_MASKED string = "ETH_SRC_MASKED"
+
+ /** Ethernet frame type. */
+ ETH_TYPE string = "ETH_TYPE"
+
+ /** VLAN id. */
+ VLAN_VID string = "VLAN_VID"
+
+ /** VLAN priority. */
+ VLAN_PCP string = "VLAN_PCP"
+ /**
+ * Inner VLAN id.
+ *
+ * Note: Some drivers may not support this.
+ */
+ INNER_VLAN_VID string = "INNER_VLAN_VID"
+
+ /**
+ * Inner VLAN pcp.
+ *
+ * Note: Some drivers may not support this.
+ */
+ INNER_VLAN_PCP string = "INNER_VLAN_PCP"
+
+ /** IP DSCP (6 bits in ToS field). */
+ IP_DSCP string = "IP_DSCP"
+
+ /** IP ECN (2 bits in ToS field). */
+ IP_ECN string = "IP_ECN"
+
+ /** IP protocol. */
+ IP_PROTO string = "IP_PROTO"
+
+ /** IPv4 source address. */
+ IPV4_SRC string = "IPV4_SRC"
+
+ /** IPv4 destination address. */
+ IPV4_DST string = "IPV4_DST"
+
+ /** TCP source port. */
+ TCP_SRC string = "TCP_SRC"
+
+ /** TCP source port with masking. */
+ TCP_SRC_MASKED string = "TCP_SRC_MASKED"
+
+ /** TCP destination port. */
+ TCP_DST string = "TCP_DST"
+
+ /** TCP destination port with masking. */
+ TCP_DST_MASKED string = "TCP_DST"
+
+ /** UDP source port. */
+ UDP_SRC string = "UDP_SRC"
+
+ /** UDP source port with masking. */
+ UDP_SRC_MASKED string = "UDP_SRC_MASKED"
+
+ /** UDP destination port. */
+ UDP_DST string = "UDP_DST"
+
+ /** UDP destination port with masking. */
+ UDP_DST_MASKED string = "UDP_DST_MASKED"
+
+ /** SCTP source port. */
+ SCTP_SRC string = "SCTP_SRC"
+
+ /** SCTP source port with masking. */
+ SCTP_SRC_MASKED string = "SCTP_SRC_MASKED"
+
+ /** SCTP destination port. */
+ SCTP_DST string = "SCTP_DST"
+
+ /** SCTP destination port with masking. */
+ SCTP_DST_MASKED string = "SCTP_DST_MASKED"
+
+ /** ICMP type. */
+ ICMPV4_TYPE string = "ICMPV4_TYPE"
+
+ /** ICMP code. */
+ ICMPV4_CODE string = "ICMPV4_CODE"
+
+ /** ARP opcode. */
+ ARP_OP string = "ARP_OP"
+
+ /** ARP source IPv4 address. */
+ ARP_SPA string = "ARP_SPA"
+
+ /** ARP target IPv4 address. */
+ ARP_TPA string = "ARP_TPA"
+
+ /** ARP source hardware address. */
+ ARP_THA string = "ARP_THA"
+
+ /** IPv6 source address. */
+ IPV6_SRC string = "IPV6_SRC"
+
+ /** IPv6 destination address. */
+ IPV6_DST string = "IPV6_DST"
+
+ /** IPv6 Flow Label. */
+ IPV6_FLABEL string = "IPV6_FLABEL"
+
+ /** ICMPv6 type. */
+ ICMPV6_TYPE string = "ICMPV6_TYPE"
+
+ /** ICMPv6 code. */
+ ICMPV6_CODE string = "ICMPV6_CODE"
+
+ /** Target address for ND. */
+ IPV6_ND_TARGET string = "IPV6_ND_TARGET"
+
+ /** Source link-layer for ND. */
+ IPV6_ND_SLL string = "IPV6_ND_SLL"
+
+ /** Target link-layer for ND. */
+ IPV6_ND_TLL string = "IPV6_ND_TLL"
+
+ /** MPLS label. */
+ MPLS_LABEL string = "MPLS_LABEL"
+
+ /** MPLS TC. */
+ MPLS_TC string = "MPLS_TC"
+
+ /** MPLS BoS bit. */
+ MPLS_BOS string = "MPLS_BOS"
+
+ /** PBB I-SID. */
+ PBB_ISID string = "PBB_ISID"
+
+ /** Logical Port Metadata. */
+ TUNNEL_ID string = "TUNNEL_ID"
+
+ /** IPv6 Extension Header pseudo-field. */
+ IPV6_EXTHDR string = "IPV6_EXTHDR"
+
+ /** Unassigned value: 40. */
+ UNASSIGNED_40 string = "UNASSIGNED_40"
+
+ /** PBB UCA header field. */
+ PBB_UCA string = "PBB_UCA"
+
+ /** TCP flags. */
+ TCP_FLAGS string = "TCP_FLAGS"
+
+ /** Output port from action set metadata. */
+ ACTSET_OUTPUT string = "ACTSET_OUTPUT"
+
+ /** Packet type value. */
+ PACKET_TYPE string = "PACKET_TYPE"
+
+ //
+ // NOTE: Everything below is defined elsewhere: ONOS-specific,
+ // extensions, etc.
+ //
+ /** Optical channel signal ID (lambda). */
+ OCH_SIGID string = "OCH_SIGID"
+
+ /** Optical channel signal type (fixed or flexible). */
+ OCH_SIGTYPE string = "OCH_SIGTYPE"
+
+ /** ODU (Optical channel Data Unit) signal ID. */
+ ODU_SIGID string = "ODU_SIGID"
+
+ /** ODU (Optical channel Data Unit) signal type. */
+ ODU_SIGTYPE string = "ODU_SIGTYPE"
+
+ /** Protocol-independent. */
+ PROTOCOL_INDEPENDENT string = "PROTOCOL_INDEPENDENT"
+
+ /** Extension criterion. */
+ EXTENSION string = "EXTENSION"
+
+ /** An empty criterion. */
+ DUMMY string = "DUMMY"
+
+ /* OUTPUT instruction */
+ OUTPUT string = "OUTPUT"
+
+ /* METER instruction */
+ METER string = "METER"
+
+ /* L2MODIFICATION instruction type */
+ L2MODIFICATION string = "L2MODIFICATION"
+
+ /* VLAN_PUSH operation */
+ VLAN_PUSH string = "VLAN_PUSH"
+
+ /* VLAN_ID instruction */
+ VLAN_ID string = "VLAN_ID"
+
+ /* VLAN_POP operation */
+ VLAN_POP string = "VLAN_POP"
+
+ /* VLAN_SET operation */
+ VLAN_SET string = "VLAN_SET"
+)
+
+// Selector Critrtion structs
+type Criterion interface{
+ GetType() string
+}
+
+type PortSelector struct {
+ Type string `json:"type"`
+ Port int `json:"port,omitempty"`
+}
+
+func (s PortSelector) GetType() string {
+ return s.Type
+}
+type EthTypeSelector struct {
+ Type string `json:"type"`
+ EthType string `json:"ethType,omitempty"`
+}
+func (s EthTypeSelector) GetType() string {
+ return s.Type
+}
+
+type ProtocolSelector struct {
+ Type string `json:"type"`
+ Protocol int `json:"protocol,omitempty"`
+}
+func (s ProtocolSelector) GetType() string {
+ return s.Type
+}
+
+type UDPPortSelector struct {
+ Type string `json:"type"`
+ UDPPort int `json:"udpPort,omitempty"`
+}
+func (s UDPPortSelector) GetType() string {
+ return s.Type
+}
+
+type VlanSelector struct {
+ Type string `json:"type"`
+ VlanID int `json:"vlanId,omitempty"`
+}
+func (s VlanSelector) GetType() string {
+ return s.Type
+}
+
+type EthSrcSelector struct {
+ Type string `json:"type"`
+ EthSrc string `json:"ethsrc,omitempty"`
+}
+
+func (s EthSrcSelector) GetType() string {
+ return s.Type
+}
+
+type EthDstSelector struct {
+ Type string `json:"type"`
+ DstSrc string `json:"ethdst,omitempty"`
+}
+
+func (s EthDstSelector) GetType() string {
+ return s.Type
+}
+
+type MetaDataSelector struct {
+ Type string `json:"type"`
+ Metadata uint64 `json:"metadata,omitempty"`
+}
+
+func (s MetaDataSelector) GetType() string {
+ return s.Type
+}
+///////// END of selector interfaces
+
+type SelectorInfo struct {
+ Criteria []Criterion `json:"criteria"`
+}
+
+// Instruction structs are defined here
+type Instruction interface {
+ GetInstructionType() string
+}
+
+type PortInstruction struct {
+ Type string `json:"type"`
+ Port string `json:"port"`
+}
+
+func (i PortInstruction) GetInstructionType() string {
+ return i.Type
+}
+
+type PushVlanInstruction struct {
+ Type string `json:"type"`
+ SubType string `json:"subtype"`
+ EthernetType string `json:"ethernetType"`
+}
+
+func (i PushVlanInstruction) GetInstructionType() string {
+ return i.Type
+}
+
+type VlanInstruction struct {
+ Type string `json:"type"`
+ SubType string `json:"subtype"`
+ VlanID int `json:"vlanId"`
+}
+
+func (i VlanInstruction) GetInstructionType() string {
+ return i.Type
+}
+
+type PopVlanInstruction struct {
+ Type string `json:"type"`
+ SubType string `json:"subtype"`
+}
+
+func (i PopVlanInstruction) GetInstructionType() string {
+ return i.Type
+}
+
+type MeterInstruction struct {
+ Type string `json:"type"`
+ MeterID string `json:"meterId"`
+}
+
+func (i MeterInstruction) GetInstructionType() string {
+ return i.Type
+}
+
+type TreatmentInfo struct {
+ Instructions []Instruction `json:"instructions"`
+ Deferred []interface{} `json:"deferred"`
+}
+type Flow struct {
+ GroupID int `json:"groupId"`
+ State string `json:"state"`
+ Life int `json:"life"`
+ LiveType string `json:"liveType"`
+ LastSeen int64 `json:"lastSeen"`
+ Packets int `json:"packets"`
+ Bytes int `json:"bytes"`
+ ID string `json:"id"`
+ AppID string `json:"appId"`
+ Priority int `json:"priority"`
+ Timeout int `json:"timeout"`
+ IsPermanent bool `json:"isPermanent"`
+ DeviceID string `json:"deviceId"`
+ TableID int `json:"tableId"`
+ TableName string `json:"tableName"`
+ Treatment TreatmentInfo `json:"treatment"`
+ Selector SelectorInfo `json:"selector"`
+}
+
+type FlowEntry struct {
+ Flows []Flow `json:"flows"`
+}
+
+func ConvertFlowToFlowEntry (subFlow *of.VoltSubFlow) FlowEntry {
+ var flowEntry FlowEntry
+ flow := ConvertVoltSubFlowToOnosFlow(subFlow)
+ flowEntry.Flows = append(flowEntry.Flows, flow)
+ return flowEntry
+}
+
+func ConvertFlowsToFlowEntry (subFlows []*of.VoltSubFlow) FlowEntry {
+ var flowEntry FlowEntry
+ for _, subFlow := range subFlows {
+ flow := ConvertVoltSubFlowToOnosFlow(subFlow)
+ flowEntry.Flows = append(flowEntry.Flows, flow)
+ }
+ return flowEntry
+}
+
+func ConvertVoltSubFlowToOnosFlow(subFlow *of.VoltSubFlow) Flow {
+ var flow Flow
+ flow.ID = strconv.FormatUint(subFlow.Cookie, 10)
+ flow.TableID = int(subFlow.TableID)
+ flow.Priority = int(subFlow.Priority)
+ //flow.State = subFlow.State
+
+ // Fill Match criteria
+ if subFlow.InPort != 0 {
+ portSelector := PortSelector {
+ Type: IN_PORT,
+ Port: int(subFlow.InPort),
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(portSelector))
+ }
+ if subFlow.MatchVlan != of.VlanNone {
+ vlanSelector := VlanSelector {
+ Type: VLAN_VID,
+ VlanID: int(subFlow.MatchVlan),
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(vlanSelector))
+ }
+ if subFlow.SrcMacMatch {
+ ethSrcSelector := EthSrcSelector {
+ Type: ETH_SRC,
+ EthSrc: subFlow.SrcMacAddr.String(),
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(ethSrcSelector))
+ }
+ if subFlow.DstMacMatch {
+ ethDstSelector := EthDstSelector {
+ Type: ETH_DST,
+ DstSrc: subFlow.DstMacAddr.String(),
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(ethDstSelector))
+ }
+ if subFlow.L3Protocol != of.EtherTypeAny {
+ ethTypeSelector := EthTypeSelector {
+ Type: ETH_TYPE,
+ EthType : strconv.FormatUint(uint64(subFlow.L3Protocol), 16) ,
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(ethTypeSelector))
+ }
+ if subFlow.L4Protocol != of.IPProtocolIgnore {
+ protocolSelector := ProtocolSelector {
+ Type: IP_PROTO,
+ Protocol : int(subFlow.L4Protocol),
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(protocolSelector))
+ }
+ if subFlow.SrcPort != 0 {
+ udpPortSelector := UDPPortSelector {
+ Type: UDP_SRC,
+ UDPPort : int(subFlow.SrcPort) ,
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(udpPortSelector))
+ }
+ if subFlow.DstPort != 0 {
+ udpPortSelector := UDPPortSelector {
+ Type: UDP_DST,
+ UDPPort : int(subFlow.DstPort) ,
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(udpPortSelector))
+ }
+ if subFlow.TableMetadata != 0 {
+ metaDataSelector := MetaDataSelector {
+ Type: METADATA,
+ Metadata : subFlow.TableMetadata,
+ }
+ flow.Selector.Criteria = append(flow.Selector.Criteria, Criterion(metaDataSelector))
+ }
+
+ // Fill actions
+ if subFlow.Output != 0 {
+ portInstruction := PortInstruction {
+ Type: OUTPUT,
+ }
+ switch subFlow.Output {
+ case of.OutputTypeToController:
+ portInstruction.Port = "CONTROLLER"
+ case of.OutputTypeToNetwork:
+ portInstruction.Port = strconv.FormatUint(uint64(subFlow.OutPort) , 10)
+ case of.OutputTypeGoToTable:
+ portInstruction.Port = strconv.FormatUint(uint64(subFlow.GoToTableID) , 10)
+ }
+ flow.Treatment.Instructions = append(flow.Treatment.Instructions, Instruction(portInstruction))
+ }
+ if len(subFlow.PushVlan) != 0 {
+ for _, vlan := range subFlow.PushVlan {
+ if vlan == of.VlanNone {
+ continue
+ }
+ pushVlanInstruction := PushVlanInstruction {
+ Type: L2MODIFICATION,
+ SubType: VLAN_PUSH,
+ EthernetType: "0x8100" ,
+ }
+ flow.Treatment.Instructions = append(flow.Treatment.Instructions, Instruction(pushVlanInstruction))
+ vlanInstruction := VlanInstruction {
+ Type: L2MODIFICATION,
+ SubType: VLAN_ID,
+ VlanID: int(vlan),
+ }
+ flow.Treatment.Instructions = append(flow.Treatment.Instructions, Instruction(vlanInstruction))
+ }
+ }
+ if subFlow.SetVlan != of.VlanNone {
+ vlanInstruction := VlanInstruction {
+ Type: L2MODIFICATION,
+ SubType: VLAN_SET,
+ VlanID: int(subFlow.SetVlan) ,
+ }
+ flow.Treatment.Instructions = append(flow.Treatment.Instructions, Instruction(vlanInstruction))
+ }
+ if subFlow.RemoveVlan != 0 {
+ popVlanInstruction := PopVlanInstruction {
+ Type: L2MODIFICATION,
+ SubType: VLAN_POP,
+ }
+ flow.Treatment.Instructions = append(flow.Treatment.Instructions, Instruction(popVlanInstruction))
+ }
+ if subFlow.MeterID != 0 {
+ meterInstruction := MeterInstruction {
+ Type: METER,
+ MeterID: strconv.FormatUint(uint64(subFlow.MeterID), 10),
+ }
+ flow.Treatment.Instructions = append(flow.Treatment.Instructions, Instruction(meterInstruction))
+ }
+ return flow
+}
+
+func convertServiceToSubscriberInfo(svcs []*app.VoltService) []*SubscriberInfo {
+ var subs []*SubscriberInfo
+ for _, vs := range svcs {
+ pbit := vs.GetServicePbit()
+ sub := &SubscriberInfo{
+ Location : vs.Device,
+ TagInfo : UniTagInformation {
+ UniTagMatch: int(vs.UniVlan),
+ PonCTag: int(vs.CVlan),
+ PonSTag: int(vs.SVlan),
+ UsPonCTagPriority: pbit,
+ UsPonSTagPriority: pbit,
+ DsPonCTagPriority: pbit,
+ DsPonSTagPriority: pbit,
+ TechnologyProfileID: int(vs.TechProfileID),
+ UpstreamBandwidthProfile: vs.UsMeterProfile,
+ DownstreamBandwidthProfile: vs.DsMeterProfile,
+ UpstreamOltBandwidthProfile: vs.UsMeterProfile,
+ DownstreamOltBandwidthProfile: vs.DsMeterProfile,
+ ServiceName: vs.Name,
+ EnableMacLearning: vs.MacLearning == app.Learn,
+ ConfiguredMacAddress: vs.MacAddr.String(),
+ IsDhcpRequired: vs.MacLearning == app.Learn,
+ IsIgmpRequired: vs.IgmpEnabled,
+ IsPppoeRequired: false,
+ },
+ }
+ subs = append(subs, sub)
+ }
+ return subs
+}
+
diff --git a/voltha-go-controller/onos_nbi/oltapprestadapter.go b/voltha-go-controller/onos_nbi/oltapprestadapter.go
new file mode 100644
index 0000000..c9821c6
--- /dev/null
+++ b/voltha-go-controller/onos_nbi/oltapprestadapter.go
@@ -0,0 +1,240 @@
+/*
+* 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 onos_nbi
+
+import (
+ "bytes"
+ "context"
+ "encoding/json"
+ "net/http"
+ "strconv"
+
+ "github.com/gorilla/mux"
+ "voltha-go-controller/internal/pkg/of"
+ app "voltha-go-controller/internal/pkg/application"
+ "voltha-go-controller/log"
+)
+
+const (
+ PORTNAME string = "portName"
+ DEVICE string = "device"
+ STAG string = "sTag"
+ CTAG string = "cTag"
+ TPID string = "tpId"
+)
+// FlowHandle struct to handle flow related REST calls
+type SubscriberInfo struct {
+ Location string `json:"location"`
+ TagInfo UniTagInformation `json:"tagInfo"`
+}
+
+//UniTagInformation - Service information
+type UniTagInformation struct {
+ 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"`
+ UpstreamBandwidthProfile string `json:"upstreamBandwidthProfile"`
+ DownstreamBandwidthProfile string `json:"downstreamBandwidthProfile"`
+ UpstreamOltBandwidthProfile string `json:"upstreamOltBandwidthProfile"`
+ DownstreamOltBandwidthProfile string `json:"downstreamOltBandwidthProfile"`
+ ServiceName string `json:"serviceName"`
+ EnableMacLearning bool `json:"enableMacLearning"`
+ ConfiguredMacAddress string `json:"configuredMacAddress"`
+ IsDhcpRequired bool `json:"isDhcpRequired"`
+ IsIgmpRequired bool `json:"isIgmpRequired"`
+ IsPppoeRequired bool `json:"isPppoeRequired"`
+}
+
+type ServiceAdapter struct {
+}
+
+func (sa *ServiceAdapter) 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 "POST":
+ sa.ActivateService(context.Background(), w, r)
+ case "DELETE":
+ sa.DeactivateService(context.Background(), w, r)
+ case "GET":
+ sa.GetProgrammedSubscribers(context.Background(), w, r)
+ default:
+ logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method})
+ }
+}
+
+func (sa *ServiceAdapter) ServeHTTPWithPortName(w http.ResponseWriter, r *http.Request) {
+ logger.Infow(ctx, "Received-northbound-request", log.Fields{"Method": r.Method, "URL": r.URL})
+ switch r.Method {
+ case "POST":
+ sa.ActivateServiceWithPortName(context.Background(), w, r)
+ case "DELETE":
+ sa.DeactivateServiceWithPortName(context.Background(), w, r)
+ default:
+ logger.Warnw(ctx, "Unsupported Method", log.Fields{"Method": r.Method})
+ }
+}
+
+func (sa *ServiceAdapter) ActivateService(cntx context.Context, w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ deviceID := vars[DEVICE]
+ portNo := vars["port"]
+
+ // Get the payload to process the request
+ d := new(bytes.Buffer)
+ if _, err := d.ReadFrom(r.Body); err != nil {
+ logger.Warnw(ctx, "Error reading buffer", log.Fields{"Reason": err.Error()})
+ return
+ }
+
+ if len(deviceID) > 0 && len(portNo) > 0 {
+ app.GetApplication().ActivateService(cntx, deviceID, portNo, of.VlanNone, of.VlanNone, 0)
+ }
+}
+
+func (sa *ServiceAdapter) DeactivateService(cntx context.Context, w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ deviceID := vars[DEVICE]
+ portNo := vars["port"]
+
+ // Get the payload to process the request
+ d := new(bytes.Buffer)
+ if _, err := d.ReadFrom(r.Body); err != nil {
+ logger.Warnw(ctx, "Error reading buffer", log.Fields{"Reason": err.Error()})
+ return
+ }
+
+ if len(deviceID) > 0 && len(portNo) > 0 {
+ app.GetApplication().DeactivateService(cntx, deviceID, portNo, of.VlanNone, of.VlanNone, 0)
+ }
+}
+
+func (sa *ServiceAdapter) ActivateServiceWithPortName(cntx context.Context, w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ portNo := vars[PORTNAME]
+ sTag := vars[STAG]
+ cTag := vars[CTAG]
+ tpID := vars[TPID]
+ sVlan := of.VlanNone
+ cVlan := of.VlanNone
+ techProfile := uint16(0)
+
+ if len(sTag) > 0 {
+ sv, err := strconv.Atoi(sTag)
+ if err != nil {
+ logger.Warnw(ctx, "Wrong vlan value", log.Fields{"sTag": sTag})
+ return
+ }
+ sVlan = of.VlanType(sv)
+ }
+ if len(cTag) > 0 {
+ cv, err := strconv.Atoi(cTag)
+ if err != nil {
+ logger.Warnw(ctx, "Wrong vlan value", log.Fields{"cTag": cTag})
+ return
+ }
+ cVlan = of.VlanType(cv)
+ }
+ if len(tpID) > 0 {
+ tp, err := strconv.Atoi(tpID)
+ if err != nil {
+ logger.Warnw(ctx, "Wrong tech profile value", log.Fields{"tpID": tpID})
+ return
+ }
+ techProfile = uint16(tp)
+ }
+
+ if len(portNo) > 0 {
+ app.GetApplication().ActivateService(cntx, app.DeviceAny, portNo, sVlan, cVlan, techProfile)
+ }
+}
+
+func (sa *ServiceAdapter) DeactivateServiceWithPortName(cntx context.Context, w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ portNo := vars[PORTNAME]
+ sTag := vars[STAG]
+ cTag := vars[CTAG]
+ tpID := vars[TPID]
+ sVlan := of.VlanNone
+ cVlan := of.VlanNone
+ techProfile := uint16(0)
+
+ if len(sTag) > 0 {
+ sv, err := strconv.Atoi(sTag)
+ if err != nil {
+ logger.Warnw(ctx, "Wrong vlan value", log.Fields{"sTag": sTag})
+ return
+ }
+ sVlan = of.VlanType(sv)
+ }
+ if len(cTag) > 0 {
+ cv, err := strconv.Atoi(cTag)
+ if err != nil {
+ logger.Warnw(ctx, "Wrong vlan value", log.Fields{"cTag": cTag})
+ return
+ }
+ cVlan = of.VlanType(cv)
+ }
+ if len(tpID) > 0 {
+ tp, err := strconv.Atoi(tpID)
+ if err != nil {
+ logger.Warnw(ctx, "Wrong tech profile value", log.Fields{"tpID": tpID})
+ return
+ }
+ techProfile = uint16(tp)
+ }
+
+ // Get the payload to process the request
+ d := new(bytes.Buffer)
+ if _, err := d.ReadFrom(r.Body); err != nil {
+ logger.Warnw(ctx, "Error reading buffer", log.Fields{"Reason": err.Error()})
+ return
+ }
+
+ if len(portNo) > 0 {
+ app.GetApplication().DeactivateService(cntx, app.DeviceAny, portNo, sVlan, cVlan, techProfile)
+ }
+}
+
+func (sa *ServiceAdapter) GetProgrammedSubscribers(cntx context.Context, w http.ResponseWriter, r *http.Request) {
+ vars := mux.Vars(r)
+ deviceID := vars[DEVICE]
+ portNo := vars["port"]
+
+ svcs, err := app.GetApplication().GetProgrammedSubscribers(cntx, deviceID, portNo)
+ if err != nil {
+ logger.Errorw(ctx, "Failed to get subscribers", log.Fields{"Reason": err.Error()})
+ }
+ subs := convertServiceToSubscriberInfo(svcs)
+ subsJSON, err := json.Marshal(subs)
+ if err != nil {
+ logger.Errorw(ctx, "Error occurred while marshaling subscriber response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ return
+ }
+
+ w.Header().Add("Content-Type", "application/json")
+ _, err = w.Write(subsJSON)
+ if err != nil {
+ logger.Errorw(ctx, "error in sending subscriber response", log.Fields{"Error": err})
+ w.WriteHeader(http.StatusInternalServerError)
+ }
+
+}