Fix for service activation and audit issues

Change-Id: I1517f9be1532f384f5236e8d6328c8fda93c1776
diff --git a/internal/pkg/controller/audittables.go b/internal/pkg/controller/audittables.go
index 079ff2f..334ce41 100644
--- a/internal/pkg/controller/audittables.go
+++ b/internal/pkg/controller/audittables.go
@@ -84,6 +84,12 @@
 	var errInfo error
 	var err error
 
+	// Audit ports
+	if err = att.AuditPorts(); err != nil {
+		logger.Errorw(ctx, "Audit Ports Failed", log.Fields{"Reason": err.Error()})
+		errInfo = err
+	}
+
 	// Audit the meters
 	if err = att.AuditMeters(); err != nil {
 		logger.Errorw(ctx, "Audit Meters Failed", log.Fields{"Reason": err.Error()})
@@ -530,3 +536,121 @@
 		}
 	}
 }
+
+func (att *AuditTablesTask) AuditPorts() error {
+
+        if att.stop {
+                return tasks.ErrTaskCancelError
+        }
+
+        var vc voltha.VolthaServiceClient
+        if vc = att.device.VolthaClient(); vc == nil {
+                logger.Error(ctx, "Flow Audit Failed: Voltha Client Unavailable")
+                return nil
+        }
+        ofpps, err := vc.ListLogicalDevicePorts(att.ctx, &common.ID{Id: att.device.ID})
+        if err != nil {
+                return err
+        }
+
+        // Compute the difference between the ports received and ports at VGC
+        // First build a map of all the received ports under missing ports. We
+        // will eliminate the ports that are in the device from the missing ports
+        // so that the elements remaining are missing ports. The ones that are
+        // not in missing ports are added to excess ports which should be deleted
+        // from the VGC.
+        missingPorts := make(map[uint32]*ofp.OfpPort)
+        for _, ofpp := range ofpps.Items {
+                missingPorts[ofpp.OfpPort.PortNo] = ofpp.OfpPort
+        }
+
+        var excessPorts []uint32
+        processPortState := func(id uint32, vgcPort *DevicePort) {
+                logger.Debugw(ctx, "Process Port State Ind", log.Fields{"Port No": vgcPort.ID, "Port Name": vgcPort.Name})
+
+                if ofpPort, ok := missingPorts[id]; ok {
+                        if ((vgcPort.State == PortStateDown) && (ofpPort.State == uint32(ofp.OfpPortState_OFPPS_LIVE))) || ((vgcPort.State == PortStateUp) && (ofpPort.State != uint32(ofp.OfpPortState_OFPPS_LIVE))) {
+                                // This port exists in the received list and the map at
+                                // VGC. This is common so delete it
+                                logger.Infow(ctx, "Port State Mismatch", log.Fields{"Port": vgcPort.ID, "OfpPort": ofpPort.PortNo, "ReceivedState": ofpPort.State, "CurrentState": vgcPort.State})
+                                att.device.ProcessPortState(ctx, ofpPort.PortNo, ofpPort.State)
+                        }
+                        delete(missingPorts, id)
+                } else {
+                        // This port is missing from the received list. This is an
+                        // excess port at VGC. This must be added to excess ports
+                        excessPorts = append(excessPorts, id)
+                }
+                logger.Debugw(ctx, "Processed Port State Ind", log.Fields{"Port No": vgcPort.ID, "Port Name": vgcPort.Name})
+
+        }
+        // 1st process the NNI port before all other ports so that the device state can be updated.
+        if vgcPort, ok := att.device.PortsByID[NNIPortID]; ok {
+                logger.Info(ctx, "Processing NNI port state")
+                processPortState(NNIPortID, vgcPort)
+        }
+
+        for id, vgcPort := range att.device.PortsByID {
+                if id == NNIPortID {
+                        //NNI port already processed
+                        continue
+                }
+                if att.stop {
+                        break
+                }
+                processPortState(id, vgcPort)
+        }
+
+	if att.stop {
+                logger.Errorw(ctx, "Audit Device Task Cancelled", log.Fields{"Context": att.ctx, "Task": att.taskID})
+                return tasks.ErrTaskCancelError
+        }
+        att.AddMissingPorts(ctx, missingPorts)
+        att.DelExcessPorts(ctx, excessPorts)
+	return nil
+}
+
+// AddMissingPorts to add the missing ports
+func (att *AuditTablesTask) AddMissingPorts(cntx context.Context, mps map[uint32]*ofp.OfpPort) {
+        logger.Debugw(ctx, "Device Audit - Add Missing Ports", log.Fields{"NumPorts": len(mps)})
+
+        addMissingPort := func(mp *ofp.OfpPort) {
+                logger.Debugw(ctx, "Process Port Add Ind", log.Fields{"Port No": mp.PortNo, "Port Name": mp.Name})
+
+                // Error is ignored as it only drops duplicate ports
+                logger.Infow(ctx, "Calling AddPort", log.Fields{"No": mp.PortNo, "Name": mp.Name})
+                if err := att.device.AddPort(cntx, mp); err != nil {
+                        logger.Warnw(ctx, "AddPort Failed", log.Fields{"No": mp.PortNo, "Name": mp.Name, "Reason": err})
+                }
+                if mp.State == uint32(ofp.OfpPortState_OFPPS_LIVE) {
+                        att.device.ProcessPortState(cntx, mp.PortNo, mp.State)
+                }
+                logger.Debugw(ctx, "Processed Port Add Ind", log.Fields{"Port No": mp.PortNo, "Port Name": mp.Name})
+
+        }
+
+        // 1st process the NNI port before all other ports so that the flow provisioning for UNIs can be enabled
+        if mp, ok := mps[NNIPortID]; ok {
+                logger.Info(ctx, "Adding Missing NNI port")
+                addMissingPort(mp)
+        }
+
+        for portNo, mp := range mps {
+                if portNo != NNIPortID {
+                        addMissingPort(mp)
+                }
+        }
+}
+
+// DelExcessPorts to delete the excess ports
+func (att *AuditTablesTask) DelExcessPorts(cntx context.Context, eps []uint32) {
+        logger.Debugw(ctx, "Device Audit - Delete Excess Ports", log.Fields{"NumPorts": len(eps)})
+        for _, id := range eps {
+                // Now delete the port from the device @ VGC
+                logger.Infow(ctx, "Device Audit - Deleting Port", log.Fields{"PortId": id})
+                if err := att.device.DelPort(cntx, id); err != nil {
+                        logger.Warnw(ctx, "DelPort Failed", log.Fields{"PortId": id, "Reason": err})
+                }
+        }
+}
+
diff --git a/internal/pkg/controller/controller.go b/internal/pkg/controller/controller.go
index 7f5d9f1..3ed1c04 100644
--- a/internal/pkg/controller/controller.go
+++ b/internal/pkg/controller/controller.go
@@ -48,14 +48,6 @@
 
 var db database.DBIntf
 
-var deviceTableSyncDuration = 15 * time.Minute
-
-//SetDeviceTableSyncDuration - sets interval between device table sync up activity
-//  duration - in minutes
-func SetDeviceTableSyncDuration(duration int) {
-	deviceTableSyncDuration = time.Duration(duration) * time.Minute
-}
-
 // VoltController structure
 type VoltController struct {
 	rebootLock              sync.Mutex
@@ -68,6 +60,7 @@
 	RebootFlow              bool
 	BlockedDeviceList       *util.ConcurrentMap
 	deviceTaskQueue         *util.ConcurrentMap
+	deviceTableSyncDuration time.Duration
 }
 
 var vcontroller *VoltController
@@ -88,6 +81,17 @@
 	return &controller
 }
 
+//SetDeviceTableSyncDuration - sets interval between device table sync up activity
+//  duration - in minutes
+func (v *VoltController) SetDeviceTableSyncDuration(duration int) {
+	v.deviceTableSyncDuration = time.Duration(duration) * time.Second
+}
+
+//GetDeviceTableSyncDuration - returns configured device table sync duration
+func (v *VoltController) GetDeviceTableSyncDuration() time.Duration {
+	return v.deviceTableSyncDuration
+}
+
 // AddDevice to add device
 func (v *VoltController) AddDevice(cntx context.Context, config *intf.VPClientCfg) intf.IVPClient {
 
diff --git a/internal/pkg/controller/device.go b/internal/pkg/controller/device.go
index 9fbe172..ea4ccb3 100644
--- a/internal/pkg/controller/device.go
+++ b/internal/pkg/controller/device.go
@@ -668,7 +668,7 @@
 
 func (d *Device) synchronizeDeviceTables() {
 
-	tick := time.NewTicker(deviceTableSyncDuration)
+	tick := time.NewTicker(GetController().GetDeviceTableSyncDuration())
 loop:
 	for {
 		select {