Add Transport VLAN ID configuration.

Change-Id: Ib9ee37a33138b614a468a63ea02e5636ab1630c9
diff --git a/ecord/carrierethernet/example/netcfg.json b/ecord/carrierethernet/example/netcfg.json
new file mode 100644
index 0000000..861e41a
--- /dev/null
+++ b/ecord/carrierethernet/example/netcfg.json
@@ -0,0 +1,10 @@
+{
+    "ports": {
+        "rest:10.128.14.32:443/20": {
+            "portVlan": {
+                "tag" : 100,
+                "transport-tag" : 101
+            }
+        }
+    }
+}
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetProvisioner.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetProvisioner.java
index 86e6306..e60f08d 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetProvisioner.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetProvisioner.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.ecord.carrierethernet.app;
 
+import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableList;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -51,10 +52,12 @@
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
+import java.util.Objects;
 import java.util.Optional;
 import java.util.Set;
 import java.util.List;
 import java.util.ArrayList;
+import java.util.EnumSet;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.stream.Stream;
 
@@ -100,7 +103,8 @@
             });
 
     // Map of connect points and corresponding VLAN tag
-    private Map<ConnectPoint, VlanId> portVlanMap = new ConcurrentHashMap<>();
+    private final Map<ConnectPoint, VlanId> portVlanMap = new ConcurrentHashMap<>();
+    private final Map<ConnectPoint, VlanId> transportVlanMap = new ConcurrentHashMap<>();
 
     private OpticalPathListener opticalEventListener = new OpticalEventListener();
 
@@ -205,6 +209,10 @@
                                 log.info("VLAN ID {} is assigned to CE service {}", vlan, evc.id());
                                 evc.setVlanId(vlan);
                             });
+                            getTransportVlanTag(links).ifPresent(vlan -> {
+                                log.info("Transport VLAN ID {} is assigned to CE service {}", vlan, evc.id());
+                                evc.setTransportVlanId(vlan);
+                            });
                         });
                     }
                 }
@@ -404,7 +412,7 @@
                 .setupConnectivity(ingress, egress, bandwidth, latency);
         if (opticalConnectId != null) {
             long startTime = System.currentTimeMillis();
-            while (((System.currentTimeMillis() - startTime) < (long) OPTICAL_CONNECT_TIMEOUT_MILLIS) &&
+            while (((System.currentTimeMillis() - startTime) < OPTICAL_CONNECT_TIMEOUT_MILLIS) &&
                     (opticalConnectStatusMap.get(opticalConnectId) != OpticalPathEvent.Type.PATH_INSTALLED)) {
             }
         }
@@ -415,7 +423,7 @@
         if (opticalConnectId != null) {
             opticalPathService.removeConnectivity(opticalConnectId);
             long startTime = System.currentTimeMillis();
-            while (((System.currentTimeMillis() - startTime) < (long) OPTICAL_CONNECT_TIMEOUT_MILLIS) &&
+            while (((System.currentTimeMillis() - startTime) < OPTICAL_CONNECT_TIMEOUT_MILLIS) &&
                     (opticalConnectStatusMap.get(opticalConnectId) != OpticalPathEvent.Type.PATH_REMOVED)) {
             }
         }
@@ -443,23 +451,63 @@
         return Optional.empty();
     }
 
+    /**
+     * Returns transport VLAN tag assigned to given path.
+     * @param links List of links that composes path
+     * @return VLAN transport tag if found any. empty if not found.
+     */
+    @Beta
+    private Optional<VlanId> getTransportVlanTag(List<Link> links) {
+        checkNotNull(links);
+        return links.stream().flatMap(l -> Stream.of(l.src(), l.dst()))
+                .map(transportVlanMap::get)
+                .filter(Objects::nonNull)
+                .findAny();
+    }
+
     private class InternalNetworkConfigListener implements NetworkConfigListener {
 
+        /**
+         * Negative events.
+         */
+        private final EnumSet<NetworkConfigEvent.Type> negative
+            = EnumSet.of(NetworkConfigEvent.Type.CONFIG_UNREGISTERED,
+                         NetworkConfigEvent.Type.CONFIG_REMOVED);
+
+        @Override
+        public boolean isRelevant(NetworkConfigEvent event) {
+            return event.configClass().equals(PortVlanConfig.class);
+        }
+
         @Override
         public void event(NetworkConfigEvent event) {
-            if (!event.configClass().equals(PortVlanConfig.class)) {
-                return;
-            }
 
             ConnectPoint cp = (ConnectPoint) event.subject();
             PortVlanConfig config = networkConfigService.getConfig(cp, PortVlanConfig.class);
-            if (config != null && config.portVlanId().isPresent()) {
+
+            if (config == null) {
+                log.info("VLAN tag config is removed from port {}", cp);
+                portVlanMap.remove(cp);
+                transportVlanMap.remove(cp);
+                return;
+            }
+
+            if (config.portVlanId().isPresent() && !negative.contains(event.type())) {
                 log.info("VLAN tag {} is assigned to port {}", config.portVlanId().get(), cp);
                 portVlanMap.put(cp, config.portVlanId().get());
             } else {
                 log.info("VLAN tag is removed from port {}", cp);
                 portVlanMap.remove(cp);
             }
+
+            if (config.transportVlanId().isPresent() && !negative.contains(event.type())) {
+                log.info("transport VLAN tag {} is assigned to port {}",
+                         config.transportVlanId().get(), cp);
+                transportVlanMap.put(cp, config.transportVlanId().get());
+            } else {
+                log.info("transport VLAN tag is removed from port {}", cp);
+                transportVlanMap.remove(cp);
+            }
         }
 
     }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetVirtualConnection.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetVirtualConnection.java
index 854c818..81c7aed 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetVirtualConnection.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/CarrierEthernetVirtualConnection.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.ecord.carrierethernet.app;
 
+import com.google.common.annotations.Beta;
 import com.google.common.collect.ImmutableSet;
 import org.onlab.packet.VlanId;
 import org.onosproject.newoptical.api.OpticalConnectivityId;
@@ -112,13 +113,13 @@
     protected State evcState;
     protected ActiveState evcActiveState;
     protected VlanId vlanId;
+    private VlanId transportVlanId;
     protected boolean isVirtual;
     protected Integer maxNumUni;
     protected Set<CarrierEthernetNetworkInterface> niSet;
     protected Duration latency;
     protected CarrierEthernetMetroConnectivity metroConnectivity;
     protected boolean congruentPaths;
-
     // Set to true if both directions should use the same path
     private static final boolean CONGRUENT_PATHS = true;
 
@@ -198,6 +199,16 @@
     }
 
     /**
+     * Returns Transport Vlan ID.
+     *
+     * @return Transport Vlan ID.
+     */
+    @Beta
+    public VlanId transportVlanId() {
+        return transportVlanId;
+    }
+
+    /**
      * Returns the Virtual status of the service (i.e. if all UNIs have CE-VLAN ids).
      *
      * @return true if service is virtual, false otherwise
@@ -300,6 +311,16 @@
     }
 
     /**
+     * Sets the vlanId to be used by the transport.
+     *
+     * @param vlan the vlanId to set
+     */
+    @Beta
+    public void setTransportVlanId(VlanId vlan) {
+        this.transportVlanId = vlan;
+    }
+
+    /**
      * Sets the Virtual status of the service.
      *
      * @param isVirtual boolean value with the status to set
@@ -335,13 +356,16 @@
         this.metroConnectivity.setStatus(status);
     }
 
+    @Override
     public String toString() {
 
         return toStringHelper(this)
+                .omitNullValues()
                 .add("id", evcId)
                 .add("cfgId", evcCfgId)
                 .add("type", evcType)
                 .add("vlanId", vlanId)
+                .add("transportVlanId", transportVlanId)
                 .add("metroConnectId", (metroConnectivity.id() == null ? "null" : metroConnectivity.id().id()))
                 .add("NIs", niSet).toString();
     }
diff --git a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/PortVlanConfig.java b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/PortVlanConfig.java
index 15566ea..c0c3d71 100644
--- a/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/PortVlanConfig.java
+++ b/ecord/carrierethernet/src/main/java/org/onosproject/ecord/carrierethernet/app/PortVlanConfig.java
@@ -19,6 +19,8 @@
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.config.Config;
 
+import com.google.common.annotations.Beta;
+
 import java.util.Optional;
 
 /**
@@ -29,6 +31,9 @@
 
     public static final String CE_VLAN_TAG_KEY = "tag";
 
+    @Beta
+    public static final String TRANSPORT_VLAN_TAG_KEY = "transport-tag";
+
     public Optional<VlanId> portVlanId() {
         String s = get(CE_VLAN_TAG_KEY, null);
         if (s == null) {
@@ -43,4 +48,22 @@
         }
         return (PortVlanConfig) setOrClear(CE_VLAN_TAG_KEY, String.valueOf(vlanId.toShort()));
     }
+
+    @Beta
+    public Optional<VlanId> transportVlanId() {
+        String s = get(TRANSPORT_VLAN_TAG_KEY, null);
+        if (s == null) {
+            return Optional.empty();
+        }
+        return Optional.of(VlanId.vlanId(Short.valueOf(s)));
+    }
+
+    @Beta
+    public PortVlanConfig transportVlanId(VlanId vlanId) {
+        if (vlanId == null) {
+            return (PortVlanConfig) setOrClear(TRANSPORT_VLAN_TAG_KEY, (String) null);
+        }
+        return (PortVlanConfig) setOrClear(TRANSPORT_VLAN_TAG_KEY, String.valueOf(vlanId.toShort()));
+    }
+
 }