Update to ciena cordigmp app to add a cord port map for igmp tests
diff --git a/src/test/apps/ciena-cordigmp-1.0-SNAPSHOT.oar b/src/test/apps/ciena-cordigmp-1.0-SNAPSHOT.oar
index c2bd76c..15f70ed 100644
--- a/src/test/apps/ciena-cordigmp-1.0-SNAPSHOT.oar
+++ b/src/test/apps/ciena-cordigmp-1.0-SNAPSHOT.oar
Binary files differ
diff --git a/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/CordIgmp.java b/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/CordIgmp.java
index d3a515a..f3e51e7 100644
--- a/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/CordIgmp.java
+++ b/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/CordIgmp.java
@@ -81,6 +81,7 @@
 import java.util.Dictionary;
 import java.util.List;
 import java.util.Map;
+import java.util.Collection;
 import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.atomic.AtomicBoolean;
@@ -144,6 +145,9 @@
     //TODO: move this to a ec map
     private Map<IpAddress, Integer> groups = Maps.newConcurrentMap();
 
+    //Map of IGMP groups to port
+    private Map<IpAddress, IgmpPortPair> cordIgmpTranslateTable = Maps.newConcurrentMap();
+
     //TODO: move this to distributed atomic long
     private AtomicInteger channels = new AtomicInteger(0);
 
@@ -187,18 +191,19 @@
 
     private Map<DeviceId, Boolean> deviceAvailability = new ConcurrentHashMap<>();
 
-    private static final Class<AccessDeviceConfig> CONFIG_CLASS =
-            AccessDeviceConfig.class;
+    private static final Class<CordIgmpTranslateConfig> CORD_IGMP_TRANSLATE_CONFIG_CLASS =
+            CordIgmpTranslateConfig.class;
 
-    private ConfigFactory<DeviceId, AccessDeviceConfig> configFactory =
-            new ConfigFactory<DeviceId, AccessDeviceConfig>(
-                    SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessDevice") {
+    private ConfigFactory<ApplicationId, CordIgmpTranslateConfig> cordIgmpTranslateConfigFactory =
+            new ConfigFactory<ApplicationId, CordIgmpTranslateConfig>(
+                    SubjectFactories.APP_SUBJECT_FACTORY, CORD_IGMP_TRANSLATE_CONFIG_CLASS, "cordIgmpTranslate") {
                 @Override
-                public AccessDeviceConfig createConfig() {
-                    return new AccessDeviceConfig();
+                public CordIgmpTranslateConfig createConfig() {
+                    return new CordIgmpTranslateConfig();
                 }
             };
 
+
     @Activate
     public void activate(ComponentContext context) {
         componentConfigService.registerProperties(getClass());
@@ -208,7 +213,7 @@
 
         clearRemoteRoutes();
 
-        networkConfig.registerConfigFactory(configFactory);
+        networkConfig.registerConfigFactory(cordIgmpTranslateConfigFactory);
         networkConfig.addListener(configListener);
 
         networkConfig.getSubjects(DeviceId.class, AccessDeviceConfig.class).forEach(
@@ -221,6 +226,15 @@
                 }
         );
 
+        CordIgmpTranslateConfig cordIgmpTranslateConfig = networkConfig.getConfig(appId, CordIgmpTranslateConfig.class);
+
+        if(cordIgmpTranslateConfig != null) {
+            Collection<McastPorts> translations = cordIgmpTranslateConfig.getCordIgmpTranslations();
+            for(McastPorts port: translations) {
+                cordIgmpTranslateTable.put(port.group(), 
+                                           port.portPair());
+            }
+        }
 
         mcastService.addListener(listener);
 
@@ -240,7 +254,7 @@
         componentConfigService.unregisterProperties(getClass(), false);
         deviceService.removeListener(deviceListener);
         mcastService.removeListener(listener);
-        networkConfig.unregisterConfigFactory(configFactory);
+        networkConfig.unregisterConfigFactory(cordIgmpTranslateConfigFactory);
         networkConfig.removeListener(configListener);
         deviceAvailability.clear();
         log.info("Stopped");
@@ -401,8 +415,13 @@
             return;
         }
         log.warn("Unknown OLT device for unprovisioning. Assuming OVS {}", loc.deviceId());
-        final PortNumber inPort = PortNumber.portNumber(inputPort);
-        final PortNumber outPort = PortNumber.portNumber(outputPort);
+        final IgmpPortPair portPair = cordIgmpTranslateTable.get(info.route().group());
+        if(portPair == null) {
+            log.warn("Ignoring unprovisioning for group " + info.route().group() + " with no port map");
+            return;
+        }
+        final PortNumber inPort = PortNumber.portNumber(portPair.inputPort());
+        final PortNumber outPort = PortNumber.portNumber(portPair.outputPort());
         TrafficSelector.Builder mcast = DefaultTrafficSelector.builder()
             .matchInPort(inPort)
             .matchEthType(Ethernet.TYPE_IPV4)
@@ -432,10 +451,14 @@
             log.warn("Ignoring provisioning mcast route for OLT device: " + sink.deviceId());
             return;
         } 
-
+        final IgmpPortPair portPair = cordIgmpTranslateTable.get(route.group());
+        if(portPair == null) {
+            log.warn("Ports for Group " + route.group() + " not found in cord igmp map. Skipping provisioning.");
+            return;
+        }
         Integer ret = groups.computeIfAbsent(route.group(), (g) -> {
-            final PortNumber inPort = PortNumber.portNumber(inputPort);
-            final PortNumber outPort = PortNumber.portNumber(outputPort);
+            final PortNumber inPort = PortNumber.portNumber(portPair.inputPort());
+            final PortNumber outPort = PortNumber.portNumber(portPair.outputPort());
             TrafficSelector.Builder mcast = DefaultTrafficSelector.builder()
                     .matchInPort(inPort)
                     .matchEthType(Ethernet.TYPE_IPV4)
@@ -550,18 +573,20 @@
 
                 case CONFIG_ADDED:
                 case CONFIG_UPDATED:
-                    AccessDeviceConfig config =
-                            networkConfig.getConfig((DeviceId) event.subject(), CONFIG_CLASS);
-                    if (config != null) {
-                        oltData.put(config.getOlt().deviceId(), config.getOlt());
+                    if (event.configClass().equals(CORD_IGMP_TRANSLATE_CONFIG_CLASS)) {
+                        CordIgmpTranslateConfig config =
+                                networkConfig.getConfig((ApplicationId) event.subject(),
+                                        CORD_IGMP_TRANSLATE_CONFIG_CLASS);
+                        if (config != null) {
+                            cordIgmpTranslateTable.clear();
+                            config.getCordIgmpTranslations().forEach(
+                                                                     mcastPorts -> cordIgmpTranslateTable.put(mcastPorts.group(), mcastPorts.portPair()));
+                        }
                     }
-
                     break;
                 case CONFIG_REGISTERED:
                 case CONFIG_UNREGISTERED:
-                    break;
                 case CONFIG_REMOVED:
-                    oltData.remove(event.subject());
                     break;
                 default:
                     break;
@@ -570,7 +595,7 @@
 
         @Override
         public boolean isRelevant(NetworkConfigEvent event) {
-            return event.configClass().equals(CONFIG_CLASS);
+            return event.configClass().equals(CORD_IGMP_TRANSLATE_CONFIG_CLASS);
         }
 
 
diff --git a/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/CordIgmpTranslateConfig.java b/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/CordIgmpTranslateConfig.java
new file mode 100644
index 0000000..e55cf11
--- /dev/null
+++ b/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/CordIgmpTranslateConfig.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2016 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.ciena.cordigmp;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import org.onlab.packet.IpAddress;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.net.config.Config;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * IGMP SSM translate configuration.
+ */
+public class CordIgmpTranslateConfig extends Config<ApplicationId> {
+
+    private static final String GROUP = "group";
+    private static final String INPUT_PORT = "inputPort";
+    private static final String OUTPUT_PORT = "outputPort";
+
+    @Override
+    public boolean isValid() {
+        for (JsonNode node : array) {
+            if (!hasOnlyFields((ObjectNode) node, GROUP, INPUT_PORT, OUTPUT_PORT)) {
+                return false;
+            }
+
+            if (!(isIpAddress((ObjectNode) node, GROUP, FieldPresence.MANDATORY) && 
+                  node.get(INPUT_PORT).isInt() && node.get(OUTPUT_PORT).isInt())) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Gets the list of CordIgmp translations.
+     *
+     * @return CordIgmp translations
+     */
+    public List<McastPorts> getCordIgmpTranslations() {
+        List<McastPorts> translations = new ArrayList();
+        for (JsonNode node : array) {
+            translations.add(
+                    new McastPorts(
+                            IpAddress.valueOf(node.path(GROUP).asText().trim()),
+                            Integer.valueOf(node.path(INPUT_PORT).asText().trim()),
+                            Integer.valueOf(node.path(OUTPUT_PORT).asText().trim())));
+        }
+        return translations;
+    }
+}
diff --git a/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/IgmpPortPair.java b/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/IgmpPortPair.java
new file mode 100644
index 0000000..176cac9
--- /dev/null
+++ b/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/IgmpPortPair.java
@@ -0,0 +1,20 @@
+package org.ciena.cordigmp;
+
+public class IgmpPortPair {
+    private final Integer inputPort;
+    private final Integer outputPort;
+
+    public IgmpPortPair(Integer inputPort, Integer outputPort) {
+        this.inputPort = inputPort;
+        this.outputPort = outputPort;
+    }
+
+    public Integer inputPort() {
+        return inputPort;
+    }
+
+    public Integer outputPort() {
+        return outputPort;
+    }
+}
+
diff --git a/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/McastPorts.java b/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/McastPorts.java
new file mode 100644
index 0000000..e0cd622
--- /dev/null
+++ b/src/test/apps/ciena-cordigmp/src/main/java/org/ciena/cordigmp/McastPorts.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.ciena.cordigmp;
+
+import com.google.common.annotations.Beta;
+import com.google.common.base.Objects;
+import org.onlab.packet.IpAddress;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * An entity representing a multicast group and its input and output ports
+ */
+@Beta
+public class McastPorts {
+
+    private final IpAddress group;
+    private final IgmpPortPair portPair;
+
+    public McastPorts(IpAddress group, Integer inputPort, Integer outputPort) {
+        checkNotNull(group, "Multicast route must specify a group address");
+        checkNotNull(inputPort, "Must indicate input port");
+        checkNotNull(outputPort, "Must indicate output port");
+        this.group = group;
+        this.portPair = new IgmpPortPair(inputPort, outputPort);
+    }
+
+    /**
+     * Fetches the group address of this route.
+     *
+     * @return an ip address
+     */
+    public IpAddress group() {
+        return group;
+    }
+
+    public Integer inputPort() {
+        return portPair.inputPort();
+    }
+
+    public Integer outputPort() {
+        return portPair.outputPort();
+    }
+
+    public IgmpPortPair portPair() {
+        return portPair;
+    }
+
+    @Override
+    public String toString() {
+        return toStringHelper(this)
+                .add("group", group)
+                .add("inputPort", inputPort())
+                .add("outputPort", outputPort())
+                .toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+        if (o == null || getClass() != o.getClass()) {
+            return false;
+        }
+        McastPorts that = (McastPorts) o;
+        return Objects.equal(group, that.group) &&
+               Objects.equal(inputPort(), that.inputPort()) &&
+               Objects.equal(outputPort(), that.outputPort());
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hashCode(group, inputPort(), outputPort());
+    }
+
+}