Changing the default pw transport vlan to 4090 instead of 4093.

Also adding component config options for pw transport vlan and default internal vlan.

Change-Id: I8bb48530e46db8534b825eecd9aea781fe3f0de1
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
index d00a258..8e0eb6a 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/RoutingRulePopulator.java
@@ -28,6 +28,7 @@
 import org.onlab.packet.MplsLabel;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.Device;
 import org.onosproject.net.flowobjective.DefaultObjectiveContext;
 import org.onosproject.net.flowobjective.Objective;
 import org.onosproject.net.flowobjective.ObjectiveContext;
@@ -76,11 +77,8 @@
 import static org.onlab.packet.ICMP6.ROUTER_ADVERTISEMENT;
 import static org.onlab.packet.ICMP6.ROUTER_SOLICITATION;
 import static org.onlab.packet.IPv6.PROTOCOL_ICMP6;
-import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
 import static org.onosproject.segmentrouting.SegmentRoutingService.DEFAULT_PRIORITY;
 
-import static org.onosproject.segmentrouting.SegmentRoutingManager.PSEUDOWIRE_VLAN;
-
 /**
  * Populator of segment routing flow rules.
  */
@@ -567,7 +565,7 @@
         // if needed by the switch pipeline. Since neighbor sets are always to
         // other neighboring routers, there is no subnet assigned on those ports.
         TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
-        metabuilder.matchVlanId(SegmentRoutingManager.INTERNAL_VLAN);
+        metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
         DefaultGroupHandler grpHandler = srManager.getGroupHandler(targetSw);
         if (grpHandler == null) {
             log.warn("populateIPRuleForRouter: groupHandler for device {} "
@@ -729,7 +727,7 @@
         // if needed by the switch pipeline. Since mpls next-hops are always to
         // other neighboring routers, there is no subnet assigned on those ports.
         TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder(selector);
-        metabuilder.matchVlanId(SegmentRoutingManager.INTERNAL_VLAN);
+        metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
 
         if (nextHops.size() == 1 && destSwId.equals(nextHops.toArray()[0])) {
             // If the next hop is the destination router for the segment, do pop
@@ -970,11 +968,15 @@
             }
         } else if (!hasIPConfiguration(connectPoint)) {
             // Filter for unconfigured upstream port, using INTERNAL_VLAN
-            if (!processSinglePortFiltersInternal(deviceId, portnum, true, INTERNAL_VLAN, install)) {
+            if (!processSinglePortFiltersInternal(deviceId, portnum, true,
+                                                  srManager.getDefaultInternalVlan(),
+                                                  install)) {
                 return false;
             }
             // Filter for receiveing pseudowire traffic
-            if (!processSinglePortFiltersInternal(deviceId, portnum, false, PSEUDOWIRE_VLAN, install)) {
+            if (!processSinglePortFiltersInternal(deviceId, portnum, false,
+                                                  srManager.getPwTransportVlan(),
+                                                  install)) {
                 return false;
             }
         }
@@ -1612,7 +1614,7 @@
             srManager.interfaceService.getTaggedVlanId(cp).contains(vlanId) ||
             // Given vlanId is INTERNAL_VLAN and the interface is not configured
             (srManager.interfaceService.getTaggedVlanId(cp).isEmpty() && srManager.getInternalVlanId(cp) == null &&
-                    vlanId.equals(INTERNAL_VLAN)) ||
+                    vlanId.equals(srManager.getDefaultInternalVlan())) ||
             // interface is configured and either vlan-untagged or vlan-native matches given vlanId
             (srManager.getInternalVlanId(cp) != null && srManager.getInternalVlanId(cp).equals(vlanId))
         );
@@ -1753,4 +1755,30 @@
         Set<Interface> interfaces = srManager.interfaceService.getInterfacesByPort(cp);
         return interfaces.stream().anyMatch(intf -> intf.ipAddressesList().size() > 0);
     }
+
+    /**
+     * Updates filtering rules for unconfigured ports on all devices for which
+     * this controller instance is master.
+     *
+     * @param pushVlan true if the filtering rule requires a push vlan action
+     * @param oldVlanId the vlanId to be removed
+     * @param newVlanId the vlanId to be added
+     */
+    void updateSpecialVlanFilteringRules(boolean pushVlan, VlanId oldVlanId,
+                                         VlanId newVlanId) {
+        for (Device dev : srManager.deviceService.getAvailableDevices()) {
+            if (srManager.mastershipService.isLocalMaster(dev.id())) {
+                for (Port p : srManager.deviceService.getPorts(dev.id())) {
+                    if (!hasIPConfiguration(new ConnectPoint(dev.id(), p.number()))
+                            && p.isEnabled()) {
+                        updateSinglePortFilters(dev.id(), p.number(), pushVlan,
+                                                oldVlanId, false);
+                        updateSinglePortFilters(dev.id(), p.number(), pushVlan,
+                                                newVlanId, true);
+                    }
+                }
+            }
+        }
+    }
+
 }
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
index 5905275..bd5c8c4 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/SegmentRoutingManager.java
@@ -251,6 +251,16 @@
             label = "Program flows and groups to pop and route double tagged hosts")
     boolean routeDoubleTaggedHosts = false;
 
+    private static final int DEFAULT_INTERNAL_VLAN = 4094;
+    @Property(name = "defaultInternalVlan", intValue = DEFAULT_INTERNAL_VLAN,
+            label = "internal vlan assigned by default to unconfigured ports")
+    private int defaultInternalVlan = DEFAULT_INTERNAL_VLAN;
+
+    private static final int PW_TRANSPORT_VLAN = 4090;
+    @Property(name = "pwTransportVlan", intValue = PW_TRANSPORT_VLAN,
+            label = "vlan used for transport of pseudowires between switches")
+    private int pwTransportVlan = PW_TRANSPORT_VLAN;
+
     ArpHandler arpHandler = null;
     IcmpHandler icmpHandler = null;
     IpHandler ipHandler = null;
@@ -369,14 +379,6 @@
      * Segment Routing App ID.
      */
     public static final String APP_NAME = "org.onosproject.segmentrouting";
-    /**
-     * The default VLAN ID assigned to the interfaces without subnet config.
-     */
-    public static final VlanId INTERNAL_VLAN = VlanId.vlanId((short) 4094);
-    /**
-     * The Vlan id used to transport pseudowire traffic across the network.
-     */
-    public static final VlanId PSEUDOWIRE_VLAN = VlanId.vlanId((short) 4093);
 
     /**
      * Minumum and maximum value of dummy VLAN ID to be allocated.
@@ -646,6 +648,94 @@
                 hostHandler.revokeAllDoubleTaggedHost();
             }
         }
+
+        String strDefaultInternalVlan = Tools.get(properties, "defaultInternalVlan");
+        int defIntVlan = Integer.parseInt(strDefaultInternalVlan);
+        if (defIntVlan != defaultInternalVlan) {
+            if (canUseVlanId(defIntVlan)) {
+                log.warn("Default internal vlan value changed from {} to {}.. "
+                        + "re-programming filtering rules, but NOT any groups already "
+                        + "created with the former value", defaultInternalVlan, defIntVlan);
+                VlanId oldDefIntVlan = VlanId.vlanId((short) defaultInternalVlan);
+                defaultInternalVlan = defIntVlan;
+                routingRulePopulator
+                .updateSpecialVlanFilteringRules(true, oldDefIntVlan,
+                                                 VlanId.vlanId((short) defIntVlan));
+            } else {
+                log.warn("Cannot change default internal vlan to unusable "
+                        + "value {}", defIntVlan);
+            }
+        }
+
+        String strPwTxpVlan = Tools.get(properties, "pwTransportVlan");
+        int pwTxpVlan = Integer.parseInt(strPwTxpVlan);
+        if (pwTxpVlan != pwTransportVlan) {
+            if (canUseVlanId(pwTxpVlan)) {
+                log.warn("Pseudowire transport vlan value changed from {} to {}.. "
+                        + "re-programming filtering rules, but NOT any groups already "
+                        + "created with the former value", pwTransportVlan,
+                        pwTxpVlan);
+                VlanId oldPwTxpVlan = VlanId.vlanId((short) pwTransportVlan);
+                pwTransportVlan = pwTxpVlan;
+                routingRulePopulator
+                .updateSpecialVlanFilteringRules(false, oldPwTxpVlan,
+                                                 VlanId.vlanId((short) pwTxpVlan));
+            } else {
+                log.warn("Cannot change pseudowire transport vlan to unusable "
+                        + "value {}", pwTxpVlan);
+            }
+        }
+
+    }
+
+    /**
+     * Returns true if given vlan id is not being used in the system currently,
+     * either as one of the default system wide vlans or as one of the
+     * configured interface vlans.
+     *
+     * @param vlanId given vlan id
+     * @return true if vlan is not currently in use
+     */
+    public boolean canUseVlanId(int vlanId) {
+        if (vlanId >= 4095 || vlanId <= 1) {
+            log.error("Vlan id {} value is not in valid range 2 <--> 4094",
+                      vlanId);
+            return false;
+        }
+
+       VlanId vid = VlanId.vlanId((short) vlanId);
+        if (getDefaultInternalVlan().equals(vid) || getPwTransportVlan().equals(vid)) {
+            log.warn("Vlan id {} value is already in use system-wide. "
+                    + "DefaultInternalVlan:{} PwTransportVlan:{} ", vlanId,
+                     getDefaultInternalVlan(), getPwTransportVlan());
+            return false;
+        }
+
+        if (interfaceService.inUse(vid)) {
+            log.warn("Vlan id {} value is already in use on a configured "
+                    + "interface in the system", vlanId);
+            return false;
+        }
+        return true;
+    }
+
+    /**
+     * Returns the VlanId assigned internally by default to unconfigured ports.
+     *
+     * @return the default internal vlan id
+     */
+    public VlanId getDefaultInternalVlan() {
+        return VlanId.vlanId((short) defaultInternalVlan);
+    }
+
+    /**
+     * Returns the Vlan id used to transport pseudowire traffic across the
+     * network.
+     *
+     * @return the pseudowire transport vlan id
+     */
+    public VlanId getPwTransportVlan() {
+        return VlanId.vlanId((short) pwTransportVlan);
     }
 
     @Override
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
index 1ae24e5..5f32f25 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/grouphandler/DefaultGroupHandler.java
@@ -66,7 +66,6 @@
 import static com.google.common.base.Preconditions.checkNotNull;
 import static java.util.concurrent.Executors.newScheduledThreadPool;
 import static org.onlab.util.Tools.groupedThreads;
-import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -358,7 +357,7 @@
         // if needed by the switch pipeline. Since hashed next-hops are always to
         // other neighboring routers, there is no subnet assigned on those ports.
         TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
-        metabuilder.matchVlanId(INTERNAL_VLAN);
+        metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
         NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
                 .withId(nextId)
                 .withType(NextObjective.Type.HASHED)
@@ -988,7 +987,7 @@
                     // Set VLAN ID for PW transport. Otherwise pop vlan
                     if ((ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.SWAP_NOT_BOS) ||
                          (ds.getTypeOfDstSet() == DestinationSet.DestinationSetType.POP_NOT_BOS)) {
-                        tBuilder.setVlanId(srManager.PSEUDOWIRE_VLAN);
+                        tBuilder.setVlanId(srManager.getPwTransportVlan());
                     } else {
                         tBuilder.popVlan();
                     }
@@ -1475,7 +1474,7 @@
                     log.trace("bkt-corr: dsNextObjStore for device {}: {}",
                               deviceId, dsKey, next);
                     TrafficSelector.Builder metabuilder = DefaultTrafficSelector.builder();
-                    metabuilder.matchVlanId(INTERNAL_VLAN);
+                    metabuilder.matchVlanId(srManager.getDefaultInternalVlan());
                     NextObjective.Builder nextObjBuilder = DefaultNextObjective.builder()
                             .withId(nid)
                             .withType(NextObjective.Type.HASHED)
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
index 604f868..0793984 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/mcast/McastUtils.java
@@ -60,7 +60,6 @@
 import java.util.stream.Collectors;
 
 import static org.onosproject.net.flow.criteria.Criterion.Type.VLAN_VID;
-import static org.onosproject.segmentrouting.SegmentRoutingManager.INTERNAL_VLAN;
 
 /**
  * Utility class for Multicast Handler.
@@ -229,10 +228,11 @@
         // Reuse unicast VLAN if the port has subnet configured
         if (cp != null) {
             VlanId untaggedVlan = srManager.getInternalVlanId(cp);
-            return (untaggedVlan != null) ? untaggedVlan : INTERNAL_VLAN;
+            return (untaggedVlan != null) ? untaggedVlan
+                                          : srManager.getDefaultInternalVlan();
         }
         // Use DEFAULT_VLAN if none of the above matches
-        return SegmentRoutingManager.INTERNAL_VLAN;
+        return srManager.getDefaultInternalVlan();
     }
 
     /**
diff --git a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
index 9c9fb8f..fbefb76 100644
--- a/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
+++ b/apps/segmentrouting/app/src/main/java/org/onosproject/segmentrouting/pwaas/DefaultL2TunnelHandler.java
@@ -483,7 +483,7 @@
             revNextHop = reverseLink(path.get(path.size() - 1));
 
             pw.l2Tunnel().setPath(path);
-            pw.l2Tunnel().setTransportVlan(srManager.PSEUDOWIRE_VLAN);
+            pw.l2Tunnel().setTransportVlan(srManager.getPwTransportVlan());
 
             // next hops for next objectives
             log.info("Deploying process : Establishing forward direction for pseudowire {}", l2TunnelId);
diff --git a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java
index 4f5f226..c8afeef 100644
--- a/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java
+++ b/apps/segmentrouting/app/src/test/java/org/onosproject/segmentrouting/RoutingRulePopulatorTest.java
@@ -62,7 +62,7 @@
 
     private final VlanId v10 = VlanId.vlanId((short) 10);
     private final VlanId v20 = VlanId.vlanId((short) 20);
-    private final VlanId vInt = SegmentRoutingManager.INTERNAL_VLAN;
+    private VlanId vInt;
 
     private final Interface u10 = new Interface(null, new ConnectPoint(devId1, p1),
             null, null, null, v10, null, null);
@@ -80,6 +80,7 @@
         srManager.deviceConfiguration =  EasyMock.createMock(DeviceConfiguration.class);
         srManager.interfaceService = interfaceService;
         srManager.deviceService = deviceService;
+        vInt = srManager.getDefaultInternalVlan();
         rrp = new RoutingRulePopulator(srManager);
     }
 
diff --git a/core/api/src/main/java/org/onosproject/net/intf/InterfaceService.java b/core/api/src/main/java/org/onosproject/net/intf/InterfaceService.java
index 199bf06..2aae8d0 100644
--- a/core/api/src/main/java/org/onosproject/net/intf/InterfaceService.java
+++ b/core/api/src/main/java/org/onosproject/net/intf/InterfaceService.java
@@ -139,4 +139,15 @@
     default boolean isConfigured(ConnectPoint connectPoint) {
         throw new NotImplementedException("isConfigured is not implemented");
     }
+
+    /**
+     * Returns true if given vlanId is in use due to configuration on any of the
+     * interfaces in the system.
+     *
+     * @param vlanId the vlan id being queried
+     * @return true if vlan is configured on any interface
+     */
+    default boolean inUse(VlanId vlanId) {
+        throw new NotImplementedException("isConfigured is not implemented");
+    }
 }
diff --git a/core/net/src/main/java/org/onosproject/net/intf/impl/InterfaceManager.java b/core/net/src/main/java/org/onosproject/net/intf/impl/InterfaceManager.java
index bcb7644..6f77a98 100644
--- a/core/net/src/main/java/org/onosproject/net/intf/impl/InterfaceManager.java
+++ b/core/net/src/main/java/org/onosproject/net/intf/impl/InterfaceManager.java
@@ -240,6 +240,21 @@
         return false;
     }
 
+    @Override
+    public boolean inUse(VlanId vlanId) {
+        for (Set<Interface> intfs : interfaces.values()) {
+            for (Interface intf : intfs) {
+                if (intf.vlan().equals(vlanId)
+                        || intf.vlanNative().equals(vlanId)
+                        || intf.vlanUntagged().equals(vlanId)
+                        || intf.vlanTagged().contains(vlanId)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
     private void updateInterfaces(InterfaceConfig intfConfig) {
         try {
             Set<Interface> old = interfaces.put(intfConfig.subject(),