Add support for reconfiguring interfaces in SDN-IP.

Change-Id: I2ea85d85432e661c3cbdca5e5a8b16678a242369
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
index b558c79..df9aee0 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/PeerConnectivityManager.java
@@ -23,6 +23,8 @@
 import org.onlab.packet.TpPort;
 import org.onosproject.core.ApplicationId;
 import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceEvent;
+import org.onosproject.incubator.net.intf.InterfaceListener;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.config.NetworkConfigEvent;
@@ -44,9 +46,11 @@
 
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 
 import static com.google.common.base.Preconditions.checkNotNull;
 
@@ -77,14 +81,17 @@
     private final InternalNetworkConfigListener configListener
             = new InternalNetworkConfigListener();
 
+    private final InternalInterfaceListener interfaceListener
+            = new InternalInterfaceListener();
+
     /**
      * Creates a new PeerConnectivityManager.
      *
      * @param appId              the application ID
      * @param intentSynchronizer the intent synchronizer
-     * @param configService      the SDN-IP config service
-     * @param interfaceService   the interface service
+     * @param configService      the network config service
      * @param routerAppId        application ID
+     * @param interfaceService   the interface service
      */
     public PeerConnectivityManager(ApplicationId appId,
                                    IntentSynchronizationService intentSynchronizer,
@@ -105,6 +112,7 @@
      */
     public void start() {
         configService.addListener(configListener);
+        interfaceService.addListener(interfaceListener);
         setUpConnectivity();
     }
 
@@ -113,6 +121,7 @@
      */
     public void stop() {
         configService.removeListener(configListener);
+        interfaceService.removeListener(interfaceListener);
     }
 
     /**
@@ -122,14 +131,18 @@
     private void setUpConnectivity() {
         BgpConfig config = configService.getConfig(routerAppId, RoutingService.CONFIG_CLASS);
 
+        Set<BgpConfig.BgpSpeakerConfig> bgpSpeakers;
+
         if (config == null) {
-            log.warn("No BgpConfig found");
-            return;
+            log.warn("No BGP config available");
+            bgpSpeakers = Collections.emptySet();
+        } else {
+            bgpSpeakers = config.bgpSpeakers();
         }
 
         Map<Key, PointToPointIntent> existingIntents = new HashMap<>(peerIntents);
 
-        for (BgpConfig.BgpSpeakerConfig bgpSpeaker : config.bgpSpeakers()) {
+        for (BgpConfig.BgpSpeakerConfig bgpSpeaker : bgpSpeakers) {
             log.debug("Start to set up BGP paths for BGP speaker: {}",
                     bgpSpeaker);
 
@@ -409,4 +422,19 @@
         }
     }
 
+    private class InternalInterfaceListener implements InterfaceListener {
+        @Override
+        public void event(InterfaceEvent event) {
+            switch (event.type()) {
+            case INTERFACE_ADDED:
+            case INTERFACE_UPDATED:
+            case INTERFACE_REMOVED:
+                setUpConnectivity();
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
 }
diff --git a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
index a68d76c..d41d3a7 100644
--- a/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
+++ b/apps/sdnip/src/main/java/org/onosproject/sdnip/SdnIpFib.java
@@ -17,6 +17,7 @@
 package org.onosproject.sdnip;
 
 import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Sets;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -30,6 +31,8 @@
 import org.onosproject.core.ApplicationId;
 import org.onosproject.core.CoreService;
 import org.onosproject.incubator.net.intf.Interface;
+import org.onosproject.incubator.net.intf.InterfaceEvent;
+import org.onosproject.incubator.net.intf.InterfaceListener;
 import org.onosproject.incubator.net.intf.InterfaceService;
 import org.onosproject.net.ConnectPoint;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -75,6 +78,7 @@
     protected RoutingService routingService;
 
     private final InternalFibListener fibListener = new InternalFibListener();
+    private final InternalInterfaceListener interfaceListener = new InternalInterfaceListener();
 
     private static final int PRIORITY_OFFSET = 100;
     private static final int PRIORITY_MULTIPLIER = 5;
@@ -90,12 +94,15 @@
     public void activate() {
         appId = coreService.getAppId(SdnIp.SDN_IP_APP);
 
+        interfaceService.addListener(interfaceListener);
+
         routingService.addFibListener(fibListener);
         routingService.start();
     }
 
     @Deactivate
     public void deactivate() {
+        interfaceService.removeListener(interfaceListener);
         // TODO remove listener
         routingService.stop();
     }
@@ -240,6 +247,51 @@
                 .build();
     }
 
+    private void updateInterface(Interface intf) {
+        synchronized (this) {
+            for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry : routeIntents.entrySet()) {
+                MultiPointToSinglePointIntent intent = entry.getValue();
+                Set<ConnectPoint> ingress = Sets.newHashSet(intent.ingressPoints());
+                ingress.add(intf.connectPoint());
+
+                MultiPointToSinglePointIntent newIntent =
+                        MultiPointToSinglePointIntent.builder(intent)
+                                .ingressPoints(ingress)
+                                .build();
+
+                routeIntents.put(entry.getKey(), newIntent);
+                intentSynchronizer.submit(newIntent);
+            }
+        }
+    }
+
+    private void removeInterface(Interface intf) {
+        synchronized (this) {
+            for (Map.Entry<IpPrefix, MultiPointToSinglePointIntent> entry : routeIntents.entrySet()) {
+                MultiPointToSinglePointIntent intent = entry.getValue();
+                if (intent.egressPoint().equals(intf.connectPoint())) {
+                    // This intent just lost its head. Remove it and let
+                    // higher layer routing reroute.
+                    intentSynchronizer.withdraw(routeIntents.remove(entry.getKey()));
+                } else {
+                    if (intent.ingressPoints().contains(intf.connectPoint())) {
+
+                        Set<ConnectPoint> ingress = Sets.newHashSet(intent.ingressPoints());
+                        ingress.remove(intf.connectPoint());
+
+                        MultiPointToSinglePointIntent newIntent =
+                                MultiPointToSinglePointIntent.builder(intent)
+                                .ingressPoints(ingress)
+                                .build();
+
+                        routeIntents.put(entry.getKey(), newIntent);
+                        intentSynchronizer.submit(newIntent);
+                    }
+                }
+            }
+        }
+    }
+
     private class InternalFibListener implements FibListener {
         @Override
         public void update(Collection<FibUpdate> updates, Collection<FibUpdate> withdraws) {
@@ -247,4 +299,23 @@
         }
     }
 
+    private class InternalInterfaceListener implements InterfaceListener {
+
+        @Override
+        public void event(InterfaceEvent event) {
+            switch (event.type()) {
+            case INTERFACE_ADDED:
+                updateInterface(event.subject());
+                break;
+            case INTERFACE_UPDATED:
+                break;
+            case INTERFACE_REMOVED:
+                removeInterface(event.subject());
+                break;
+            default:
+                break;
+            }
+        }
+    }
+
 }