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