CORD-348 multicast support in SegmentRouting and vRouter

In this submission:
* Setup/teardown multicast route according to SinkAdded/SinkRemoved event
    - ingressVlan and egressVlan is configurable through network config
* Change behavior of OFDPA VLAN assignment
    - Always use the VLAN in metadata if present
* Bugfix of writing immutable object

NOT in this submission (coming soon):
* Error handling (e.g. link/device failure recovery)

Change-Id: I9be11af04eb2d6456b865c7e59e96cc02370f846
diff --git a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
index be35026..97751ec 100644
--- a/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
+++ b/drivers/default/src/main/java/org/onosproject/driver/pipeline/Ofdpa2Pipeline.java
@@ -313,31 +313,22 @@
         }
 
         VlanId assignedVlan = null;
-        // For VLAN cross-connect packets, use the configured VLAN
         if (vidCriterion != null) {
-            if (vidCriterion.vlanId() != VlanId.NONE) {
+            // Use the VLAN in metadata whenever a metadata is provided
+            if (filt.meta() != null) {
+                assignedVlan = readVlanFromTreatment(filt.meta());
+            // Use the VLAN in criterion if metadata is not present and the traffic is tagged
+            } else if (!vidCriterion.vlanId().equals(VlanId.NONE)) {
                 assignedVlan = vidCriterion.vlanId();
+            }
 
-            // For untagged packets, assign a VLAN ID
-            } else {
-                if (filt.meta() == null) {
-                    log.error("Missing metadata in filtering objective required " +
-                            "for vlan assignment in dev {}", deviceId);
-                    fail(filt, ObjectiveError.BADPARAMS);
-                    return;
-                }
-                for (Instruction i : filt.meta().allInstructions()) {
-                    if (i instanceof ModVlanIdInstruction) {
-                        assignedVlan = ((ModVlanIdInstruction) i).vlanId();
-                    }
-                }
-                if (assignedVlan == null) {
-                    log.error("Driver requires an assigned vlan-id to tag incoming "
-                            + "untagged packets. Not processing vlan filters on "
-                            + "device {}", deviceId);
-                    fail(filt, ObjectiveError.BADPARAMS);
-                    return;
-                }
+            if (assignedVlan == null) {
+                log.error("Driver fails to extract VLAN information. "
+                        + "Not proccessing VLAN filters on device {}.", deviceId);
+                log.debug("VLAN ID in criterion={}, metadata={}",
+                        readVlanFromTreatment(filt.meta()), vidCriterion.vlanId());
+                fail(filt, ObjectiveError.BADPARAMS);
+                return;
             }
         }
 
@@ -457,22 +448,14 @@
         TrafficSelector.Builder preSelector = null;
         TrafficTreatment.Builder preTreatment = null;
 
-
         treatment.transition(TMAC_TABLE);
 
-        VlanId storeVlan = null;
         if (vidCriterion.vlanId() == VlanId.NONE) {
             // untagged packets are assigned vlans
             OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(VlanId.NONE);
             selector.extension(ofdpaMatchVlanVid, deviceId);
             OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan);
             treatment.extension(ofdpaSetVlanVid, deviceId);
-            // ofdpa requires an additional vlan match rule for the assigned vlan
-            // and it does not require the push when setting the assigned vlan.
-            // It also requires the extra rule to be sent to the switch before we
-            // send the untagged match rule.
-            // None of this in compliance with OF standard.
-            storeVlan = assignedVlan;
 
             preSelector = DefaultTrafficSelector.builder();
             OfdpaMatchVlanVid preOfdpaMatchVlanVid = new OfdpaMatchVlanVid(assignedVlan);
@@ -482,7 +465,11 @@
         } else {
             OfdpaMatchVlanVid ofdpaMatchVlanVid = new OfdpaMatchVlanVid(vidCriterion.vlanId());
             selector.extension(ofdpaMatchVlanVid, deviceId);
-            storeVlan = vidCriterion.vlanId();
+
+            if (!assignedVlan.equals(vidCriterion.vlanId())) {
+                OfdpaSetVlanVid ofdpaSetVlanVid = new OfdpaSetVlanVid(assignedVlan);
+                treatment.extension(ofdpaSetVlanVid, deviceId);
+            }
         }
 
         // ofdpa cannot match on ALL portnumber, so we need to use separate
@@ -499,17 +486,6 @@
         }
 
         for (PortNumber pnum : portnums) {
-            // update storage
-            groupHandler.port2Vlan.put(pnum, storeVlan);
-            Set<PortNumber> vlanPorts = groupHandler.vlan2Port.get(storeVlan);
-            if (vlanPorts == null) {
-                vlanPorts = Collections.newSetFromMap(
-                                    new ConcurrentHashMap<PortNumber, Boolean>());
-                vlanPorts.add(pnum);
-                groupHandler.vlan2Port.put(storeVlan, vlanPorts);
-            } else {
-                vlanPorts.add(pnum);
-            }
             // create rest of flowrule
             selector.matchInPort(pnum);
             FlowRule rule = DefaultFlowRule.builder()
@@ -1112,4 +1088,13 @@
         Criterion criterion = selector.getCriterion(Criterion.Type.IPV4_DST);
         return (criterion == null) ? null : ((IPCriterion) criterion).ip();
     }
+
+    private static VlanId readVlanFromTreatment(TrafficTreatment treatment) {
+        for (Instruction i : treatment.allInstructions()) {
+            if (i instanceof ModVlanIdInstruction) {
+                return ((ModVlanIdInstruction) i).vlanId();
+            }
+        }
+        return null;
+    }
 }