[CORD-634]Add dynamic config update support for interfaces

Change-Id: I065ef5df908864f10f60c5491db3ff9e502c4101
diff --git a/src/main/java/org/onosproject/segmentrouting/HostHandler.java b/src/main/java/org/onosproject/segmentrouting/HostHandler.java
index 539af62..07f6d8d 100644
--- a/src/main/java/org/onosproject/segmentrouting/HostHandler.java
+++ b/src/main/java/org/onosproject/segmentrouting/HostHandler.java
@@ -17,6 +17,7 @@
 package org.onosproject.segmentrouting;
 
 import org.onlab.packet.IpAddress;
+import org.onlab.packet.IpPrefix;
 import org.onlab.packet.MacAddress;
 import org.onlab.packet.VlanId;
 import org.onosproject.net.ConnectPoint;
@@ -400,4 +401,125 @@
             srManager.defaultRoutingHandler.populateRoute(deviceId, ip.toIpPrefix(), mac, vlanId, port);
         }
     }
+
+    /**
+     * Populate or revoke a bridging rule on given deviceId that matches given vlanId,
+     * and hostMAC connected to given port, and output to given port only when
+     * vlan information is valid.
+     *
+     * @param deviceId device ID that host attaches to
+     * @param portNum port number that host attaches to
+     * @param hostMac mac address of the host connected to the switch port
+     * @param vlanId Vlan ID configured on the switch port
+     * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
+     * @param install true to populate the objective, false to revoke
+     */
+    private void updateBridgingRule(DeviceId deviceId, PortNumber portNum, MacAddress hostMac,
+                            VlanId vlanId, boolean popVlan, boolean install) {
+        // Create host selector
+        TrafficSelector.Builder sbuilder = DefaultTrafficSelector.builder();
+        sbuilder.matchEthDst(hostMac);
+
+        // Create host meta
+        TrafficSelector.Builder mbuilder = DefaultTrafficSelector.builder();
+
+        sbuilder.matchVlanId(vlanId);
+        mbuilder.matchVlanId(vlanId);
+
+        // Create host treatment
+        TrafficTreatment.Builder tbuilder = DefaultTrafficTreatment.builder();
+        tbuilder.immediate().setOutput(portNum);
+
+        if (popVlan) {
+            tbuilder.immediate().popVlan();
+        }
+
+        int portNextObjId = srManager.getPortNextObjectiveId(deviceId, portNum,
+                                                             tbuilder.build(), mbuilder.build(), install);
+        if (portNextObjId != -1) {
+            ForwardingObjective.Builder fob = DefaultForwardingObjective.builder()
+                    .withFlag(ForwardingObjective.Flag.SPECIFIC)
+                    .withSelector(sbuilder.build())
+                    .nextStep(portNextObjId)
+                    .withPriority(100)
+                    .fromApp(srManager.appId)
+                    .makePermanent();
+
+            ObjectiveContext context = new DefaultObjectiveContext(
+                    (objective) -> log.debug("Brigding rule for {}/{} {}", hostMac, vlanId,
+                                             install ? "populated" : "revoked"),
+                    (objective, error) -> log.warn("Failed to {} bridging rule for {}/{}: {}",
+                                                   install ? "populate" : "revoke", hostMac, vlanId, error));
+            flowObjectiveService.forward(deviceId, install ? fob.add(context) : fob.remove(context));
+        } else {
+            log.warn("Failed to retrieve next objective for {}/{}", hostMac, vlanId);
+        }
+    }
+
+    /**
+     * Update forwarding objective for unicast bridging and unicast routing.
+     * Also check the validity of updated interface configuration on VLAN.
+     *
+     * @param deviceId device ID that host attaches to
+     * @param portNum port number that host attaches to
+     * @param vlanId Vlan ID configured on the switch port
+     * @param popVlan true to pop Vlan tag at TrafficTreatment, false otherwise
+     * @param install true to populate the objective, false to revoke
+     */
+    void processIntfVlanUpdatedEvent(DeviceId deviceId, PortNumber portNum, VlanId vlanId,
+                                 boolean popVlan, boolean install) {
+        ConnectPoint connectPoint = new ConnectPoint(deviceId, portNum);
+        Set<Host> hosts = hostService.getConnectedHosts(connectPoint);
+
+        if (hosts == null || hosts.size() == 0) {
+            return;
+        }
+
+        hosts.forEach(host -> {
+            MacAddress mac = host.mac();
+            VlanId hostVlanId = host.vlan();
+
+            // Check whether the host vlan is valid for new interface configuration
+            if ((!popVlan && hostVlanId.equals(vlanId)) ||
+                    (popVlan && hostVlanId.equals(VlanId.NONE))) {
+                updateBridgingRule(deviceId, portNum, mac, vlanId, popVlan, install);
+                // Update Forwarding objective and corresponding simple Next objective
+                // for each host and IP address connected to given port
+                host.ipAddresses().forEach(ipAddress ->
+                    srManager.routingRulePopulator.updateFwdObj(deviceId, portNum, ipAddress.toIpPrefix(),
+                                                                mac, vlanId, popVlan, install)
+                );
+            }
+        });
+    }
+
+    /**
+     * Populate or revoke routing rule for each host, according to the updated
+     * subnet configuration on the interface.
+     * @param cp connect point of the updated interface
+     * @param ipPrefixSet IP Prefixes added or removed
+     * @param install true if IP Prefixes added, false otherwise
+     */
+    void processIntfIpUpdatedEvent(ConnectPoint cp, Set<IpPrefix> ipPrefixSet, boolean install) {
+        Set<Host> hosts = hostService.getConnectedHosts(cp);
+
+        if (hosts == null || hosts.size() == 0) {
+            log.warn("processIntfIpUpdatedEvent: No hosts connected to {}", cp);
+            return;
+        }
+
+        // Check whether the host IP address is in the interface's subnet
+        hosts.forEach(host ->
+            host.ipAddresses().forEach(hostIpAddress -> {
+                ipPrefixSet.forEach(ipPrefix -> {
+                    if (install && ipPrefix.contains(hostIpAddress)) {
+                            srManager.routingRulePopulator.populateRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
+                                                                         host.mac(), host.vlan(), cp.port());
+                    } else if (!install && ipPrefix.contains(hostIpAddress)) {
+                            srManager.routingRulePopulator.revokeRoute(cp.deviceId(), hostIpAddress.toIpPrefix(),
+                                                                       host.mac(), host.vlan(), cp.port());
+                    }
+                });
+            }));
+    }
 }