[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)
+	}
+
+}