[VOL-5536] - VGC recent fixes

Change-Id: Id6f0e647b37baac827230afbb45d132df8a62b68
Signed-off-by: Sridhar Ravindra <sridhar.ravindra@radisys.com>
diff --git a/internal/pkg/controller/audittables.go b/internal/pkg/controller/audittables.go
index d98888c..9c4c712 100644
--- a/internal/pkg/controller/audittables.go
+++ b/internal/pkg/controller/audittables.go
@@ -17,10 +17,9 @@
 
 import (
 	"context"
-	"strconv"
+	"errors"
 	"time"
 
-	"voltha-go-controller/internal/pkg/intf"
 	"voltha-go-controller/internal/pkg/of"
 	"voltha-go-controller/internal/pkg/tasks"
 	"voltha-go-controller/internal/pkg/util"
@@ -235,19 +234,21 @@
 		return err
 	}
 
-	defaultSuccessFlowStatus := intf.FlowStatus{
-		Device:      att.device.ID,
-		FlowModType: of.CommandAdd,
-		Status:      0,
-		Reason:      "",
-	}
+	// defaultSuccessFlowStatus := intf.FlowStatus{
+	// 	Device:      att.device.ID,
+	// 	FlowModType: of.CommandAdd,
+	// 	Status:      0,
+	// 	Reason:      "",
+	// }
 
 	// Build the map for easy and faster processing
 	rcvdFlows := make(map[uint64]*ofp.OfpFlowStats)
+	volthaFlows := make(map[uint64]*ofp.OfpFlowStats)
 	flowsToAdd := &of.VoltFlow{}
 	flowsToAdd.SubFlows = make(map[uint64]*of.VoltSubFlow)
 	for _, flow := range f.Items {
 		rcvdFlows[flow.Cookie] = flow
+		volthaFlows[flow.Cookie] = flow
 	}
 
 	att.device.flowLock.Lock()
@@ -269,10 +270,68 @@
 				// Update flow delete count since we are retrying the flow delete due to failure
 				att.device.UpdateFlowCount(cntx, flow.Cookie)
 			}
-			defaultSuccessFlowStatus.Cookie = strconv.FormatUint(flow.Cookie, 10)
+			// defaultSuccessFlowStatus.Cookie = strconv.FormatUint(flow.Cookie, 10)
 		} else {
+			// Do not add the flow to device whose state was marked as delete failure
+			// Remove the flow from DB as it is no longer reported by voltha
+			if flow.State == of.FlowDelFailure {
+				delete(att.device.flows, flow.Cookie)
+				att.device.DelFlowFromDb(cntx, flow.Cookie)
+				logger.Warnw(ctx, "Found flow with state DelFailure while adding to device, will remove from DB", log.Fields{"Cookie": flow.Cookie})
+				continue
+			}
 			// The flow exists at the controller but not at the device
 			// Push the flow to the device
+
+			// If UST0 flow is missing in voltha but the UST1 flow is present in voltha,
+			// then delete the UST1 flow from voltha and add both US flows to voltha
+			if att.device.IsUSTable0Flow(ctx, flow) {
+				logger.Debugw(ctx, "UST0 flow found, checking for UST1 flow", log.Fields{"Cookie": flow.Cookie})
+				ust1Flow := att.device.GetDeviceFlow(ctx, flow, att.device.SerialNum, att.device.ID, false)
+				if ust1Flow != nil {
+					flowToDelete, ok := volthaFlows[ust1Flow.Cookie]
+					if ok {
+						// Sometimes the audit would happen even before all flows are installed for a service.
+						// If the UST0 flow is still missing in voltha on second retry attempt, then remove the UST1 flow from voltha.
+						if flow.FlowCount == 0 {
+							att.device.UpdateFlowCount(cntx, flow.Cookie)
+							continue
+						}
+						logger.Infow(ctx, "UST1 flow already present in Voltha, delete and install US flows", log.Fields{"UST1Flow": ust1Flow.Cookie, "UST0Flow": flow.Cookie})
+						err = att.DeleteDeviceFlow(ctx, flowToDelete)
+						if err == nil {
+							flowsToAdd.SubFlows[ust1Flow.Cookie] = ust1Flow
+						} else {
+							logger.Warnw(ctx, "UST1 flow delete failed", log.Fields{"Cookie": ust1Flow.Cookie, "Reason": err.Error()})
+						}
+					}
+				}
+			}
+
+			// If DST0 flow is missing in voltha but the DST1 flow is present in voltha,
+			// then delete the DST1 flow from voltha and add both DS flows to voltha
+			if att.device.IsDSTable0Flow(ctx, flow) {
+				logger.Debugw(ctx, "DST0 flow found, checking for DST1 flow", log.Fields{"Cookie": flow.Cookie})
+				dst1Flow := att.device.GetDeviceFlow(ctx, flow, att.device.SerialNum, att.device.ID, true)
+				if dst1Flow != nil {
+					flowToDelete, ok := volthaFlows[dst1Flow.Cookie]
+					if ok {
+						// Sometimes the audit would happen even before all flows are installed for a service.
+						// If the DST0 flow is still missing in voltha on second retry attempt, then remove the DST1 flow from voltha.
+						if flow.FlowCount == 0 {
+							att.device.UpdateFlowCount(cntx, flow.Cookie)
+							continue
+						}
+						logger.Infow(ctx, "DST1 flow already present in Voltha, delete and install DS flows", log.Fields{"DST1Flow": dst1Flow.Cookie, "DST0Flow": flow.Cookie})
+						err = att.DeleteDeviceFlow(ctx, flowToDelete)
+						if err == nil {
+							flowsToAdd.SubFlows[dst1Flow.Cookie] = dst1Flow
+						} else {
+							logger.Warnw(ctx, "DST1 flow delete failed", log.Fields{"Cookie": dst1Flow.Cookie, "Reason": err.Error()})
+						}
+					}
+				}
+			}
 			logger.Debugw(ctx, "Adding Flow To Missing Flows", log.Fields{"Cookie": flow.Cookie})
 			if !att.device.IsFlowAddThresholdReached(flow.FlowCount, flow.Cookie) {
 				flowsToAdd.SubFlows[flow.Cookie] = flow
@@ -380,6 +439,44 @@
 	}
 }
 
+func (att *AuditTablesTask) DeleteDeviceFlow(cntx context.Context, flow *ofp.OfpFlowStats) error {
+	logger.Debugw(ctx, "Deleting Flow", log.Fields{"Cookie": flow.Cookie})
+
+	// Create the flowMod structure and fill it out
+	flowMod := &ofp.OfpFlowMod{}
+	flowMod.Cookie = flow.Cookie
+	flowMod.TableId = flow.TableId
+	flowMod.Command = ofp.OfpFlowModCommand_OFPFC_DELETE_STRICT
+	flowMod.IdleTimeout = flow.IdleTimeout
+	flowMod.HardTimeout = flow.HardTimeout
+	flowMod.Priority = flow.Priority
+	flowMod.BufferId = of.DefaultBufferID
+	flowMod.OutPort = of.DefaultOutPort
+	flowMod.OutGroup = of.DefaultOutGroup
+	flowMod.Flags = flow.Flags
+	flowMod.Match = flow.Match
+	flowMod.Instructions = flow.Instructions
+
+	// Create FlowTableUpdate
+	flowUpdate := &ofp.FlowTableUpdate{
+		Id:      att.device.ID,
+		FlowMod: flowMod,
+	}
+
+	var err error
+	var vc voltha.VolthaServiceClient
+	if vc = att.device.VolthaClient(); vc == nil {
+		logger.Error(ctx, "Delete flow failed: Voltha Client Unavailable")
+		return errors.New("voltha client unavailable")
+	}
+
+	if _, err = vc.UpdateLogicalDeviceFlowTable(att.ctx, flowUpdate); err != nil {
+		logger.Errorw(ctx, "Flow delete failed", log.Fields{"Reason": err.Error()})
+		return err
+	}
+	return nil
+}
+
 // AuditGroups audit the groups which includes fetching the existing groups at the
 // voltha and identifying the delta between the ones held here and the
 // ones held at VOLTHA. The delta must be cleaned up to keep both the
@@ -582,7 +679,7 @@
 				// 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, ofpPort.Name)
+				att.device.ProcessPortState(ctx, ofpPort.PortNo, ofpPort.State, ofpPort.Name, false)
 			}
 			delete(missingPorts, id)
 		} else {
@@ -593,13 +690,15 @@
 		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.Debugw(ctx, "Processing NNI port state", log.Fields{"Port ID": vgcPort.ID, "Port Name": vgcPort.Name})
-		processPortState(NNIPortID, vgcPort)
+	for id, vgcPort := range att.device.PortsByID {
+		if util.IsNniPort(id) {
+			logger.Debugw(ctx, "Processing NNI port state", log.Fields{"Port ID": vgcPort.ID, "Port Name": vgcPort.Name})
+			processPortState(id, vgcPort)
+		}
 	}
 
 	for id, vgcPort := range att.device.PortsByID {
-		if id == NNIPortID {
+		if util.IsNniPort(id) {
 			// NNI port already processed
 			continue
 		}
@@ -629,19 +728,21 @@
 			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, mp.Name)
+			att.device.ProcessPortState(cntx, mp.PortNo, mp.State, mp.Name, false)
 		}
 		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.Debugw(ctx, "Adding Missing NNI port", log.Fields{"PortNo": mp.PortNo})
-		addMissingPort(mp)
+	for portNo, mp := range mps {
+		if util.IsNniPort(portNo) {
+			logger.Debugw(ctx, "Adding Missing NNI port", log.Fields{"PortNo": mp.PortNo, "Port Name": mp.Name, "Port Status": mp.State})
+			addMissingPort(mp)
+		}
 	}
 
 	for portNo, mp := range mps {
-		if portNo != NNIPortID {
+		if !util.IsNniPort(portNo) {
 			addMissingPort(mp)
 		}
 	}