VOL-2294 Flow-decomposer should not decompose multicast flows;
it should leave it as it is.

Change-Id: I6020b66257ed6f19145298dde34c2bae7b338650
diff --git a/rw_core/core/device_manager.go b/rw_core/core/device_manager.go
index 7bb869f..0094264 100755
--- a/rw_core/core/device_manager.go
+++ b/rw_core/core/device_manager.go
@@ -762,7 +762,7 @@
 }
 
 func (dMgr *DeviceManager) addFlowsAndGroups(deviceID string, flows []*ofp.OfpFlowStats, groups []*ofp.OfpGroupEntry, flowMetadata *voltha.FlowMetadata) error {
-	log.Debugw("addFlowsAndGroups", log.Fields{"deviceid": deviceID, "flowMetadata": flowMetadata})
+	log.Debugw("addFlowsAndGroups", log.Fields{"deviceid": deviceID, "groups:": groups, "flowMetadata": flowMetadata})
 	if agent := dMgr.getDeviceAgent(deviceID); agent != nil {
 		return agent.addFlowsAndGroups(flows, groups, flowMetadata)
 	}
diff --git a/rw_core/core/logical_device_agent.go b/rw_core/core/logical_device_agent.go
index b85d572..5da6d40 100644
--- a/rw_core/core/logical_device_agent.go
+++ b/rw_core/core/logical_device_agent.go
@@ -1772,3 +1772,16 @@
 	}
 	return 0, status.Error(codes.NotFound, "No NNI port found")
 }
+
+//GetNNIPorts returns NNI ports.
+func (agent *LogicalDeviceAgent) GetNNIPorts() []uint32 {
+	agent.lockLogicalPortsNo.RLock()
+	defer agent.lockLogicalPortsNo.RUnlock()
+	nniPorts := make([]uint32, 0)
+	for portNo, nni := range agent.logicalPortsNo {
+		if nni {
+			nniPorts = append(nniPorts, portNo)
+		}
+	}
+	return nniPorts
+}
diff --git a/rw_core/coreif/logical_device_agent_if.go b/rw_core/coreif/logical_device_agent_if.go
index 167d309..c9dbe6c 100644
--- a/rw_core/coreif/logical_device_agent_if.go
+++ b/rw_core/coreif/logical_device_agent_if.go
@@ -31,4 +31,5 @@
 	GetDeviceGraph() *graph.DeviceGraph
 	GetWildcardInputPorts(excludePort ...uint32) []uint32
 	GetRoute(ingressPortNo uint32, egressPortNo uint32) []graph.RouteHop
+	GetNNIPorts() []uint32
 }
diff --git a/rw_core/flowdecomposition/flow_decomposer.go b/rw_core/flowdecomposition/flow_decomposer.go
index 330a4af..1d8d0a7 100644
--- a/rw_core/flowdecomposition/flow_decomposer.go
+++ b/rw_core/flowdecomposition/flow_decomposer.go
@@ -406,75 +406,12 @@
 		log.Warnw("Group-or-desc-nil", log.Fields{"grpId": grpID, "grp": grp})
 		return deviceRules
 	}
-	for _, bucket := range grp.Desc.Buckets {
-		otherActions := make([]*ofp.OfpAction, 0)
-		for _, action := range bucket.Actions {
-			if action.Type == fu.OUTPUT {
-				outPortNo = action.GetOutput().Port
-			} else if action.Type != fu.POP_VLAN {
-				otherActions = append(otherActions, action)
-			}
-		}
 
-		route2 := agent.GetRoute(inPortNo, outPortNo)
-		switch len(route2) {
-		case 0:
-			log.Errorw("mc-no-route", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo, "comment": "deleting flow"})
-			//	TODO: Delete flow
-			return deviceRules
-		case 2:
-			log.Debugw("route-found", log.Fields{"ingressHop": route2[0], "egressHop": route2[1]})
-		default:
-			log.Errorw("invalid-route-length", log.Fields{"routeLen": len(route)})
-			return deviceRules
-		}
-
-		ingressHop := route[0]
-		ingressHop2 := route2[0]
-		egressHop := route2[1]
-
-		if ingressHop.Ingress != ingressHop2.Ingress {
-			log.Errorw("mc-ingress-hop-hop2-mismatch", log.Fields{"inPortNo": inPortNo, "outPortNo": outPortNo, "comment": "ignoring flow"})
-			return deviceRules
-		}
-		// Set the parent device flow
-		fa := &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				fu.InPort(ingressHop.Ingress),
-			},
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, fu.GetOfbFields(flow, fu.IN_PORT)...)
-
-		// Augment the Actions
-		filteredAction := fu.GetActions(flow, fu.GROUP)
-		filteredAction = append(filteredAction, fu.PopVlan())
-		filteredAction = append(filteredAction, fu.Output(route2[1].Ingress))
-		fa.Actions = filteredAction
-
-		fg := fu.NewFlowsAndGroups()
-		fg.AddFlow(fu.MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(ingressHop.DeviceID, fg)
-
-		// Set the child device flow
-		fa = &fu.FlowArgs{
-			KV: fu.OfpFlowModArgs{"priority": uint64(flow.Priority), "cookie": flow.Cookie},
-			MatchFields: []*ofp.OfpOxmOfbField{
-				fu.InPort(egressHop.Ingress),
-			},
-		}
-		// Augment the matchfields with the ofpfields from the flow
-		fa.MatchFields = append(fa.MatchFields, fu.GetOfbFields(flow, fu.IN_PORT, fu.VLAN_VID, fu.VLAN_PCP)...)
-
-		// Augment the Actions
-		otherActions = append(otherActions, fu.Output(egressHop.Egress))
-		fa.Actions = otherActions
-
-		fg = fu.NewFlowsAndGroups()
-		fg.AddFlow(fu.MkFlowStat(fa))
-		deviceRules.AddFlowsAndGroup(egressHop.DeviceID, fg)
-	}
+	deviceRules.CreateEntryIfNotExist(route[0].DeviceID)
+	fg := fu.NewFlowsAndGroups()
+	fg.AddFlow(flow)
+	//return the multicast flow without decomposing it
+	deviceRules.AddFlowsAndGroup(route[0].DeviceID, fg)
 	return deviceRules
 }
 
@@ -483,6 +420,15 @@
 	groupMap map[uint32]*ofp.OfpGroupEntry) *fu.DeviceRules {
 
 	inPortNo := fu.GetInPort(flow)
+	if fu.HasGroup(flow) && inPortNo == 0 {
+		//if no in-port specified for a multicast flow, put NNI port as in-port
+		//so that a valid route can be found for the flow
+		nniPorts := agent.GetNNIPorts()
+		if len(nniPorts) > 0 {
+			inPortNo = nniPorts[0]
+			log.Debugw("Assigning NNI port as in-port for the multicast flow", log.Fields{"nni": nniPorts[0], "flow:": flow})
+		}
+	}
 	outPortNo := fu.GetOutPort(flow)
 	deviceRules := fu.NewDeviceRules()
 	route := agent.GetRoute(inPortNo, outPortNo)
diff --git a/rw_core/flowdecomposition/flow_decomposer_test.go b/rw_core/flowdecomposition/flow_decomposer_test.go
index cf05549..dac2e6b 100644
--- a/rw_core/flowdecomposition/flow_decomposer_test.go
+++ b/rw_core/flowdecomposition/flow_decomposer_test.go
@@ -17,7 +17,6 @@
 
 import (
 	"errors"
-
 	"github.com/opencord/voltha-go/rw_core/graph"
 	"github.com/opencord/voltha-go/rw_core/mocks"
 	fu "github.com/opencord/voltha-lib-go/v2/pkg/flows"
@@ -114,12 +113,13 @@
 }
 
 type testFlowDecomposer struct {
-	dMgr         *testDeviceManager
-	logicalPorts map[uint32]*voltha.LogicalPort
-	routes       map[graph.OFPortLink][]graph.RouteHop
-	defaultRules *fu.DeviceRules
-	deviceGraph  *graph.DeviceGraph
-	fd           *FlowDecomposer
+	dMgr           *testDeviceManager
+	logicalPorts   map[uint32]*voltha.LogicalPort
+	routes         map[graph.OFPortLink][]graph.RouteHop
+	defaultRules   *fu.DeviceRules
+	deviceGraph    *graph.DeviceGraph
+	fd             *FlowDecomposer
+	logicalPortsNo map[uint32]bool
 }
 
 func newTestFlowDecomposer(deviceMgr *testDeviceManager) *testFlowDecomposer {
@@ -127,14 +127,19 @@
 	tfd.dMgr = deviceMgr
 
 	tfd.logicalPorts = make(map[uint32]*voltha.LogicalPort)
+	tfd.logicalPortsNo = make(map[uint32]bool)
 	// Go protobuf interpreted absence of a port as 0, so we can't use port #0 as an openflow
 	// port
 	tfd.logicalPorts[10] = &voltha.LogicalPort{Id: "10", DeviceId: "olt", DevicePortNo: 2}
+	tfd.logicalPorts[65536] = &voltha.LogicalPort{Id: "65536", DeviceId: "olt", DevicePortNo: 65536}
 	tfd.logicalPorts[1] = &voltha.LogicalPort{Id: "1", DeviceId: "onu1", DevicePortNo: 2}
 	tfd.logicalPorts[2] = &voltha.LogicalPort{Id: "2", DeviceId: "onu2", DevicePortNo: 2}
 	tfd.logicalPorts[3] = &voltha.LogicalPort{Id: "3", DeviceId: "onu3", DevicePortNo: 2}
 	tfd.logicalPorts[4] = &voltha.LogicalPort{Id: "4", DeviceId: "onu4", DevicePortNo: 2}
 
+	tfd.logicalPortsNo[10] = false
+	tfd.logicalPortsNo[65536] = true // nni
+
 	tfd.routes = make(map[graph.OFPortLink][]graph.RouteHop)
 
 	//DOWNSTREAM ROUTES
@@ -449,6 +454,16 @@
 	return nil
 }
 
+func (tfd *testFlowDecomposer) GetNNIPorts() []uint32 {
+	nniPorts := make([]uint32, 0)
+	for portNo, nni := range tfd.logicalPortsNo {
+		if nni {
+			nniPorts = append(nniPorts, portNo)
+		}
+	}
+	return nniPorts
+}
+
 func TestEapolReRouteRuleVlanDecomposition(t *testing.T) {
 
 	fa := &fu.FlowArgs{
@@ -968,43 +983,24 @@
 	tfd := newTestFlowDecomposer(newTestDeviceManager())
 
 	deviceRules := tfd.fd.DecomposeRules(tfd, flows, groups)
-	onu1FlowAndGroup := deviceRules.Rules["onu1"]
 	oltFlowAndGroup := deviceRules.Rules["olt"]
-	assert.Equal(t, 1, onu1FlowAndGroup.Flows.Len())
-	assert.Equal(t, 0, onu1FlowAndGroup.Groups.Len())
 	assert.Equal(t, 1, oltFlowAndGroup.Flows.Len())
 	assert.Equal(t, 0, oltFlowAndGroup.Groups.Len())
 
 	fa = &fu.FlowArgs{
 		KV: fu.OfpFlowModArgs{"priority": 500},
 		MatchFields: []*ofp.OfpOxmOfbField{
-			fu.InPort(2),
+			fu.InPort(10),
 			fu.VlanVid(uint32(ofp.OfpVlanId_OFPVID_PRESENT) | 170),
 			fu.VlanPcp(0),
 			fu.EthType(0x800),
 			fu.Ipv4Dst(0xe00a0a0a),
 		},
 		Actions: []*ofp.OfpAction{
-			fu.PopVlan(),
-			fu.Output(1),
+			fu.Group(10),
 		},
 	}
 	expectedOltFlow := fu.MkFlowStat(fa)
 	derivedFlow := oltFlowAndGroup.GetFlow(0)
 	assert.Equal(t, expectedOltFlow.String(), derivedFlow.String())
-
-	fa = &fu.FlowArgs{
-		KV: fu.OfpFlowModArgs{"priority": 500},
-		MatchFields: []*ofp.OfpOxmOfbField{
-			fu.InPort(1),
-			fu.EthType(0x800),
-			fu.Ipv4Dst(0xe00a0a0a),
-		},
-		Actions: []*ofp.OfpAction{
-			fu.Output(2),
-		},
-	}
-	expectedOnu1Flow := fu.MkFlowStat(fa)
-	derivedFlow = onu1FlowAndGroup.GetFlow(0)
-	assert.Equal(t, expectedOnu1Flow.String(), derivedFlow.String())
 }