Directly refer to patch port of tenant bridge from device events

Do not remove tenant bridge in case other tenant networks use it

Change-Id: I5a37a8acbb8616ab6d66bb9ea4e574df2acee785
(cherry picked from commit fb3bdbcb649d2765180f5795718daf2616986045)
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubernetesExternalLbArpHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubernetesExternalLbArpHandler.java
index 5af9834..f310dcb 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubernetesExternalLbArpHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubernetesExternalLbArpHandler.java
@@ -341,7 +341,7 @@
     private void retrievePeerMac(IpAddress srcIp, MacAddress srcMac,
                                  IpAddress peerIp, KubevirtNode gatewayNode,
                                  PortNumber portNumber) {
-        log.trace("Sending ARP request to the peer {} to retrieve the MAC address.",
+        log.debug("Sending ARP request to the peer {} to retrieve the MAC address.",
                 peerIp.getIp4Address().toString());
 
         Ethernet ethRequest = ARP.buildArpRequest(srcMac.toBytes(),
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
index b82aa3c..f1d88c4 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtNetworkHandler.java
@@ -16,6 +16,7 @@
 package org.onosproject.kubevirtnetworking.impl;
 
 import com.google.common.collect.Lists;
+import org.apache.commons.lang.StringUtils;
 import org.onlab.packet.ARP;
 import org.onlab.packet.EthType;
 import org.onlab.packet.Ethernet;
@@ -65,6 +66,8 @@
 import org.onosproject.net.behaviour.InterfaceConfig;
 import org.onosproject.net.behaviour.PatchDescription;
 import org.onosproject.net.device.DeviceAdminService;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
 import org.onosproject.net.flow.DefaultTrafficTreatment;
@@ -77,6 +80,7 @@
 import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
+import java.util.Comparator;
 import java.util.List;
 import java.util.Objects;
 import java.util.Set;
@@ -117,6 +121,7 @@
 import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterForKubevirtPort;
 import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.getRouterMacAddress;
 import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.kubernetesElbMac;
+import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.numOfDupSegNetworks;
 import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.portNumber;
 import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.resolveHostname;
 import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.segmentIdHex;
@@ -195,6 +200,8 @@
     private final InternalRouterEventListener kubevirtRouterlistener =
             new InternalRouterEventListener();
 
+    private final DeviceListener bridgeListener = new InternalBridgeListener();
+
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler"));
 
@@ -209,6 +216,7 @@
 
         networkService.addListener(networkListener);
         nodeService.addListener(nodeListener);
+        deviceService.addListener(bridgeListener);
         kubevirtPortService.addListener(portListener);
         kubevirtRouterService.addListener(kubevirtRouterlistener);
 
@@ -219,6 +227,7 @@
     protected void deactivate() {
         networkService.removeListener(networkListener);
         nodeService.removeListener(nodeListener);
+        deviceService.removeListener(bridgeListener);
         kubevirtPortService.removeListener(portListener);
         kubevirtRouterService.removeListener(kubevirtRouterlistener);
         leadershipService.withdraw(appId.name());
@@ -231,7 +240,7 @@
 
         Device tenantBridge = deviceService.getDevice(network.tenantDeviceId(node.hostname()));
         if (tenantBridge != null && deviceService.isAvailable(tenantBridge.id())) {
-            log.warn("The tenant bridge {} already exists at node {}",
+            log.info("The tenant bridge {} already exists at node {}",
                     network.tenantBridgeName(), node.hostname());
             return;
         }
@@ -335,57 +344,9 @@
 
     private void removeAllFlows(KubevirtNode node, KubevirtNetwork network) {
         DeviceId deviceId = network.tenantDeviceId(node.hostname());
-        removeIngressRules(network);
         flowService.purgeRules(deviceId);
     }
 
-    private void removeIngressRules(KubevirtNetwork network) {
-        if (network == null) {
-            return;
-        }
-
-        if (network.type() == FLAT || network.type() == VLAN) {
-            return;
-        }
-
-        if (network.segmentId() == null) {
-            return;
-        }
-
-        for (KubevirtNode localNode : kubevirtNodeService.completeNodes(WORKER)) {
-
-            while (true) {
-                KubevirtNode updatedNode = kubevirtNodeService.node(localNode.hostname());
-                if (tunnelToTenantPort(deviceService, updatedNode, network) != null) {
-                    break;
-                } else {
-                    log.info("Waiting for tunnel to tenant patch port creation " +
-                            "on ingress rule setup on node {}", updatedNode);
-                    waitFor(3);
-                }
-            }
-
-            PortNumber patchPortNumber = tunnelToTenantPort(deviceService, localNode, network);
-
-            TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                    .matchTunnelId(Long.parseLong(network.segmentId()));
-
-            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                    .setOutput(patchPortNumber);
-
-            flowService.setRule(
-                    appId,
-                    localNode.tunBridge(),
-                    sBuilder.build(),
-                    tBuilder.build(),
-                    PRIORITY_TUNNEL_RULE,
-                    TUNNEL_DEFAULT_TABLE,
-                    false);
-
-            log.debug("Set ingress rules for segment ID {}", network.segmentId());
-        }
-    }
-
     private void removePatchInterface(KubevirtNode node, KubevirtNetwork network) {
         Device device = deviceService.getDevice(node.ovsdb());
 
@@ -469,7 +430,6 @@
         setForwardingRule(deviceId, true);
 
         // security group related rules
-        setTenantIngressTransitionRule(network, network.tenantDeviceId(node.hostname()), true);
         setTenantEgressTransitionRule(network.tenantDeviceId(node.hostname()), true);
 
         log.info("Install default flow rules for tenant bridge {}", network.tenantBridgeName());
@@ -606,25 +566,6 @@
                 install);
     }
 
-    private void setTenantIngressTransitionRule(KubevirtNetwork network,
-                                                DeviceId deviceId, boolean install) {
-        TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
-        sBuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort())
-                .matchInPort(network.tenantToTunnelPort(deviceId));
-
-        TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
-        tBuilder.transition(TENANT_ACL_INGRESS_TABLE);
-
-        flowService.setRule(appId,
-                deviceId,
-                sBuilder.build(),
-                tBuilder.build(),
-                PRIORITY_IP_INGRESS_RULE,
-                TENANT_ICMP_TABLE,
-                install
-        );
-    }
-
     private void setTenantEgressTransitionRule(DeviceId deviceId, boolean install) {
         TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
         sBuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort());
@@ -649,7 +590,7 @@
         MacAddress routerMacAddress = getRouterMacAddress(router);
 
         if (routerMacAddress == null) {
-            log.warn("Setting gateway default eggress rule to gateway for tenant internal network because " +
+            log.warn("Setting gateway default egress rule to gateway for tenant internal network because " +
                     "there's no br-int port for device {}", gwDeviceId);
             return;
         }
@@ -657,7 +598,7 @@
         KubevirtNode gwNode = kubevirtNodeService.node(gwDeviceId);
 
         if (gwNode == null) {
-            log.warn("Setting gateway default eggress rule to gateway for tenant internal network because " +
+            log.warn("Setting gateway default egress rule to gateway for tenant internal network because " +
                     "there's no gateway node for device {}", gwDeviceId);
             return;
         }
@@ -1299,7 +1240,9 @@
                 return;
             }
 
-            nodeService.completeNodes(WORKER).forEach(n -> {
+            nodeService.completeNodes(WORKER).stream()
+                    .sorted(Comparator.comparing(KubevirtNode::hostname))
+                    .forEach(n -> {
                 createBridge(n, network);
                 createPatchTenantInterface(n, network);
                 setDefaultRulesForTenantNetwork(n, network);
@@ -1311,12 +1254,15 @@
                 return;
             }
 
-            nodeService.completeNodes(WORKER).forEach(n -> {
+            if (numOfDupSegNetworks(networkService, network) > 0) {
+                return;
+            }
+            nodeService.completeNodes(WORKER).stream()
+                    .sorted(Comparator.comparing(KubevirtNode::hostname))
+                    .forEach(n -> {
                 removeAllFlows(n, network);
                 removePatchInterface(n, network);
-
                 waitFor(5);
-
                 removeBridge(n, network);
             });
         }
@@ -1461,6 +1407,63 @@
         }
     }
 
+    private class InternalBridgeListener implements DeviceListener {
+
+        @Override
+        public boolean isRelevant(DeviceEvent event) {
+            return event.subject().type() == Device.Type.SWITCH;
+        }
+
+        private boolean isRelevantHelper() {
+            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+        }
+
+        @Override
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            Port port = event.port();
+
+            switch (event.type()) {
+                case PORT_ADDED:
+                    eventExecutor.execute(() -> {
+                        if (!isRelevantHelper()) {
+                            return;
+                        }
+                        installTenantIngressTransitionRule(device, port);
+                    });
+                    break;
+                case PORT_REMOVED:
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+
+        private void installTenantIngressTransitionRule(Device device, Port port) {
+
+            String portName = port.annotations().value(PORT_NAME);
+            if (!StringUtils.startsWithIgnoreCase(portName, TENANT_TO_TUNNEL_PREFIX)) {
+                return;
+            }
+
+            TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder();
+            sBuilder.matchEthType(EthType.EtherType.IPV4.ethType().toShort())
+                    .matchInPort(port.number());
+
+            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder();
+            tBuilder.transition(TENANT_ACL_INGRESS_TABLE);
+
+            flowService.setRule(appId,
+                    device.id(),
+                    sBuilder.build(),
+                    tBuilder.build(),
+                    PRIORITY_IP_INGRESS_RULE,
+                    TENANT_ICMP_TABLE,
+                    true
+            );
+        }
+    }
+
     private class InternalKubevirtPortListener implements KubevirtPortListener {
 
         private boolean isRelevantHelper() {
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingArpHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingArpHandler.java
index 9ba10b4..4ff5c97 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingArpHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtRoutingArpHandler.java
@@ -148,7 +148,7 @@
      */
     private void retrievePeerRouterMac(KubevirtRouter router, IpAddress peerRouterIp) {
 
-        log.info("Sending ARP request to the peer router {} to retrieve the MAC address.",
+        log.debug("Sending ARP request to the peer router {} to retrieve the MAC address.",
                 peerRouterIp.getIp4Address().toString());
         String routerSnatIp = router.external().keySet().stream().findAny().orElse(null);
 
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSecurityGroupHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSecurityGroupHandler.java
index b51af25..9c71f54 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSecurityGroupHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSecurityGroupHandler.java
@@ -16,6 +16,7 @@
 package org.onosproject.kubevirtnetworking.impl;
 
 import com.google.common.collect.Sets;
+import org.apache.commons.lang.StringUtils;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.IPv4;
 import org.onlab.packet.Ip4Address;
@@ -51,8 +52,12 @@
 import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
 import org.onosproject.kubevirtnode.api.KubevirtNodeService;
 import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
 import org.onosproject.net.DeviceId;
+import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -71,7 +76,9 @@
 import org.osgi.service.component.annotations.ReferenceCardinality;
 import org.slf4j.Logger;
 
+import java.util.Comparator;
 import java.util.Dictionary;
+import java.util.List;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
@@ -97,6 +104,7 @@
 import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_ACL_INGRESS_TABLE;
 import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_ACL_RECIRC_TABLE;
 import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_FORWARDING_TABLE;
+import static org.onosproject.kubevirtnetworking.api.Constants.TENANT_TO_TUNNEL_PREFIX;
 import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.FLAT;
 import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VLAN;
 import static org.onosproject.kubevirtnetworking.impl.OsgiPropertyConstants.USE_SECURITY_GROUP;
@@ -108,6 +116,7 @@
 import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.computeCtStateFlag;
 import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.niciraConnTrackTreatmentBuilder;
 import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.WORKER;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -205,6 +214,8 @@
     private final KubevirtNetworkListener networkListener =
             new InternalNetworkListener();
 
+    private final DeviceListener bridgeListener = new InternalBridgeListener();
+
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler"));
 
@@ -219,6 +230,7 @@
         portService.addListener(portListener);
         networkService.addListener(networkListener);
         configService.registerProperties(getClass());
+        deviceService.addListener(bridgeListener);
         nodeService.addListener(nodeListener);
 
         log.info("Started");
@@ -228,6 +240,7 @@
     protected void deactivate() {
         securityGroupService.removeListener(securityGroupListener);
         portService.removeListener(portListener);
+        deviceService.removeListener(bridgeListener);
         configService.unregisterProperties(getClass(), false);
         nodeService.removeListener(nodeListener);
         networkService.removeListener(networkListener);
@@ -295,23 +308,14 @@
         initializeAclTable(deviceId, ACL_RECIRC_TABLE, PortNumber.NORMAL, install);
     }
 
-    private void initializeTenantAclTable(KubevirtNetwork network,
-                                          DeviceId deviceId, boolean install) {
-        // FIXME: in bridge initialization phase, some patch ports may not be
-        // available until they are created, we wait for a while ensure all
-        // patch ports are created via network bootstrap
-        while (true) {
-            if (network.tenantToTunnelPort(deviceId) != null) {
-                break;
-            } else {
-                log.info("Wait for tenant patch ports creation for device {} " +
-                        "and network {}", deviceId, network.networkId());
-                waitFor(5);
+    private void initializeTenantAclTable(DeviceId deviceId, boolean install) {
+        for (Port port : deviceService.getPorts(deviceId)) {
+            String portName = port.annotations().value(PORT_NAME);
+            if (StringUtils.startsWithIgnoreCase(portName, TENANT_TO_TUNNEL_PREFIX)) {
+                initializeAclTable(deviceId, TENANT_ACL_RECIRC_TABLE, port.number(), install);
+                return;
             }
         }
-
-        PortNumber patchPort = network.tenantToTunnelPort(deviceId);
-        initializeAclTable(deviceId, TENANT_ACL_RECIRC_TABLE, patchPort, install);
     }
 
     private void initializeAclTable(DeviceId deviceId, int recircTable,
@@ -379,11 +383,11 @@
         initializeProviderAclTable(node.intgBridge(), install);
     }
 
-    private void initializeTenantPipeline(KubevirtNetwork network,
-                                          KubevirtNode node, boolean install) {
+    private DeviceId initializeTenantPipelineWithoutAcl(KubevirtNetwork network,
+                                                        KubevirtNode node, boolean install) {
         DeviceId deviceId = network.tenantDeviceId(node.hostname());
         if (deviceId == null) {
-            return;
+            return null;
         }
 
         // we check whether the given device is available from the store
@@ -401,7 +405,17 @@
         initializeTenantIngressTable(deviceId, install);
         initializeTenantEgressTable(deviceId, install);
         initializeTenantConnTrackTable(deviceId, install);
-        initializeTenantAclTable(network, deviceId, install);
+
+        return deviceId;
+    }
+
+    private void initializeTenantPipeline(KubevirtNetwork network,
+                                          KubevirtNode node, boolean install) {
+        DeviceId deviceId = initializeTenantPipelineWithoutAcl(network, node, install);
+        if (deviceId == null) {
+            return;
+        }
+        initializeTenantAclTable(deviceId, install);
     }
 
     private void updateSecurityGroupRule(KubevirtPort port,
@@ -1011,6 +1025,50 @@
         }
     }
 
+    private class InternalBridgeListener implements DeviceListener {
+
+        @Override
+        public boolean isRelevant(DeviceEvent event) {
+            return event.subject().type() == Device.Type.SWITCH;
+        }
+
+        private boolean isRelevantHelper() {
+            return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
+        }
+
+        @Override
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            Port port = event.port();
+
+            switch (event.type()) {
+                case PORT_ADDED:
+                    eventExecutor.execute(() -> {
+                        if (!isRelevantHelper()) {
+                            return;
+                        }
+                        initializeTenantAclTable(device, port);
+                    });
+                    break;
+                case PORT_REMOVED:
+                    break;
+                default:
+                    // do nothing
+                    break;
+            }
+        }
+
+        private void initializeTenantAclTable(Device device, Port port) {
+
+            String portName = port.annotations().value(PORT_NAME);
+            if (!StringUtils.startsWithIgnoreCase(portName, TENANT_TO_TUNNEL_PREFIX)) {
+                return;
+            }
+
+            initializeAclTable(device.id(), TENANT_ACL_RECIRC_TABLE, port.number(), true);
+        }
+    }
+
     private class InternalNetworkListener implements KubevirtNetworkListener {
 
         private boolean isRelevantHelper() {
@@ -1036,7 +1094,9 @@
                 return;
             }
 
-            Set<KubevirtNode> nodes = nodeService.completeNodes(WORKER);
+            List<KubevirtNode> nodes = nodeService.completeNodes(WORKER)
+                    .stream().sorted(Comparator.comparing(KubevirtNode::hostname))
+                    .collect(Collectors.toList());
 
             if (nodes.size() > 0) {
                 // now we wait 5s for all tenant bridges are created,
@@ -1045,7 +1105,7 @@
                 waitFor(5);
 
                 for (KubevirtNode node : nodes) {
-                    initializeTenantPipeline(network, node, true);
+                    initializeTenantPipelineWithoutAcl(network, node, true);
                 }
             }
         }
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSwitchingTenantHandler.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSwitchingTenantHandler.java
index dcfb4f4..010f675 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSwitchingTenantHandler.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/impl/KubevirtSwitchingTenantHandler.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.kubevirtnetworking.impl;
 
+import org.apache.commons.lang.StringUtils;
 import org.onlab.packet.Ethernet;
 import org.onlab.packet.Ip4Address;
 import org.onlab.packet.IpPrefix;
@@ -25,8 +26,6 @@
 import org.onosproject.core.CoreService;
 import org.onosproject.kubevirtnetworking.api.KubevirtFlowRuleService;
 import org.onosproject.kubevirtnetworking.api.KubevirtNetwork;
-import org.onosproject.kubevirtnetworking.api.KubevirtNetworkEvent;
-import org.onosproject.kubevirtnetworking.api.KubevirtNetworkListener;
 import org.onosproject.kubevirtnetworking.api.KubevirtNetworkService;
 import org.onosproject.kubevirtnetworking.api.KubevirtPodService;
 import org.onosproject.kubevirtnetworking.api.KubevirtPort;
@@ -38,7 +37,11 @@
 import org.onosproject.kubevirtnode.api.KubevirtNodeListener;
 import org.onosproject.kubevirtnode.api.KubevirtNodeService;
 import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.Device;
+import org.onosproject.net.Port;
 import org.onosproject.net.PortNumber;
+import org.onosproject.net.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
 import org.onosproject.net.device.DeviceService;
 import org.onosproject.net.driver.DriverService;
 import org.onosproject.net.flow.DefaultTrafficSelector;
@@ -54,12 +57,15 @@
 
 import java.util.Objects;
 import java.util.concurrent.ExecutorService;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import static java.util.concurrent.Executors.newSingleThreadExecutor;
 import static org.onlab.util.Tools.groupedThreads;
 import static org.onosproject.kubevirtnetworking.api.Constants.KUBEVIRT_NETWORKING_APP_ID;
 import static org.onosproject.kubevirtnetworking.api.Constants.PRIORITY_TUNNEL_RULE;
 import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_DEFAULT_TABLE;
+import static org.onosproject.kubevirtnetworking.api.Constants.TUNNEL_TO_TENANT_PREFIX;
 import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.FLAT;
 import static org.onosproject.kubevirtnetworking.api.KubevirtNetwork.Type.VLAN;
 import static org.onosproject.kubevirtnetworking.util.KubevirtNetworkingUtil.tunnelPort;
@@ -68,6 +74,7 @@
 import static org.onosproject.kubevirtnetworking.util.RulePopulatorUtil.buildExtension;
 import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.MASTER;
 import static org.onosproject.kubevirtnode.api.KubevirtNode.Type.WORKER;
+import static org.onosproject.net.AnnotationKeys.PORT_NAME;
 import static org.slf4j.LoggerFactory.getLogger;
 
 /**
@@ -103,13 +110,13 @@
     private final ExecutorService eventExecutor = newSingleThreadExecutor(
             groupedThreads(this.getClass().getSimpleName(), "event-handler"));
 
-    private final InternalKubevirtNetworkListener kubevirtNetworkListener =
-            new InternalKubevirtNetworkListener();
     private final InternalKubevirtPortListener kubevirtPortListener =
             new InternalKubevirtPortListener();
     private final InternalKubevirtNodeListener kubevirtNodeListener =
             new InternalKubevirtNodeListener();
 
+    private final DeviceListener bridgeListener = new InternalBridgeListener();
+
     private ApplicationId appId;
     private NodeId localNodeId;
 
@@ -119,15 +126,15 @@
         localNodeId = clusterService.getLocalNode().id();
         leadershipService.runForLeadership(appId.name());
         kubevirtPortService.addListener(kubevirtPortListener);
-        kubevirtNetworkService.addListener(kubevirtNetworkListener);
         kubevirtNodeService.addListener(kubevirtNodeListener);
+        deviceService.addListener(bridgeListener);
 
         log.info("Started");
     }
 
     @Deactivate
     protected void deactivate() {
-        kubevirtNetworkService.removeListener(kubevirtNetworkListener);
+        deviceService.removeListener(bridgeListener);
         kubevirtPortService.removeListener(kubevirtPortListener);
         kubevirtNodeService.removeListener(kubevirtNodeListener);
         leadershipService.withdraw(appId.name());
@@ -136,58 +143,6 @@
         log.info("Stopped");
     }
 
-    private void setIngressRules(KubevirtNetwork network, boolean install) {
-        // FIXME: due to the event ordering issue, we remove all ingress rules from KubevirtNetworkHandler class.
-        if (!install) {
-            return;
-        }
-
-        if (network == null) {
-            return;
-        }
-
-        if (network.type() == FLAT || network.type() == VLAN) {
-            return;
-        }
-
-        if (network.segmentId() == null) {
-            return;
-        }
-
-        for (KubevirtNode localNode : kubevirtNodeService.completeNodes(WORKER)) {
-
-            while (true) {
-                KubevirtNode updatedNode = kubevirtNodeService.node(localNode.hostname());
-                if (tunnelToTenantPort(deviceService, updatedNode, network) != null) {
-                    break;
-                } else {
-                    log.info("Waiting for tunnel to tenant patch port creation " +
-                             "on ingress rule setup on node {}", updatedNode);
-                    waitFor(3);
-                }
-            }
-
-            PortNumber patchPortNumber = tunnelToTenantPort(deviceService, localNode, network);
-
-            TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
-                    .matchTunnelId(Long.parseLong(network.segmentId()));
-
-            TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
-                    .setOutput(patchPortNumber);
-
-            flowRuleService.setRule(
-                    appId,
-                    localNode.tunBridge(),
-                    sBuilder.build(),
-                    tBuilder.build(),
-                    PRIORITY_TUNNEL_RULE,
-                    TUNNEL_DEFAULT_TABLE,
-                    install);
-
-            log.debug("Set ingress rules for segment ID {}", network.segmentId());
-        }
-    }
-
     private void setIngressRules(KubevirtNode node, boolean install) {
         for (KubevirtNetwork network : kubevirtNetworkService.tenantNetworks()) {
 
@@ -195,15 +150,9 @@
                 return;
             }
 
-            while (true) {
-                KubevirtNode updatedNode = kubevirtNodeService.node(node.hostname());
-                if (tunnelToTenantPort(deviceService, updatedNode, network) != null) {
-                    break;
-                } else {
-                    log.info("Waiting for tunnel to tenant patch port creation " +
-                             "on ingress rule setup on node {}", updatedNode);
-                    waitFor(3);
-                }
+            KubevirtNode updatedNode = kubevirtNodeService.node(node.hostname());
+            if (tunnelToTenantPort(deviceService, updatedNode, network) == null) {
+                continue;
             }
 
             PortNumber patchPortNumber = tunnelToTenantPort(deviceService, node, network);
@@ -351,19 +300,40 @@
         }
     }
 
-    private class InternalKubevirtNetworkListener implements KubevirtNetworkListener {
+    private class InternalBridgeListener implements DeviceListener {
+
+        @Override
+        public boolean isRelevant(DeviceEvent event) {
+            return event.subject().type() == Device.Type.SWITCH;
+        }
+
         private boolean isRelevantHelper() {
             return Objects.equals(localNodeId, leadershipService.getLeader(appId.name()));
         }
 
         @Override
-        public void event(KubevirtNetworkEvent event) {
+        public void event(DeviceEvent event) {
+            Device device = event.subject();
+            Port port = event.port();
+
             switch (event.type()) {
-                case KUBEVIRT_NETWORK_CREATED:
-                    eventExecutor.execute(() -> processNetworkAddition(event.subject()));
+                case PORT_ADDED:
+                    eventExecutor.execute(() -> {
+                        if (!isRelevantHelper()) {
+                            return;
+                        }
+
+                        setIngressRules(device, port, true);
+                    });
                     break;
-                case KUBEVIRT_NETWORK_REMOVED:
-                    eventExecutor.execute(() -> processNetworkRemoval(event.subject()));
+                case PORT_REMOVED:
+                    eventExecutor.execute(() -> {
+                        if (!isRelevantHelper()) {
+                            return;
+                        }
+
+                        setIngressRules(device, port, false);
+                    });
                     break;
                 default:
                     // do nothing
@@ -371,23 +341,51 @@
             }
         }
 
-        private void processNetworkAddition(KubevirtNetwork network) {
-            if (!isRelevantHelper()) {
+        private void setIngressRules(Device device, Port port, boolean install) {
+            boolean foundTunBridge = false;
+
+            for (KubevirtNode node : kubevirtNodeService.nodes()) {
+                if (device.id().equals(node.tunBridge())) {
+                    foundTunBridge = true;
+                }
+            }
+
+            if (!foundTunBridge) {
                 return;
             }
 
-            setIngressRules(network, true);
-        }
-
-        private void processNetworkRemoval(KubevirtNetwork network) {
-            if (!isRelevantHelper()) {
+            String portName = port.annotations().value(PORT_NAME);
+            if (!StringUtils.startsWithIgnoreCase(portName, TUNNEL_TO_TENANT_PREFIX)) {
                 return;
             }
 
-            setIngressRules(network, false);
+            Pattern pattern = Pattern.compile("(?i)[0-9a-f]+");
+            Matcher matcher = pattern.matcher(portName);
+
+            // Find the first occurrence of a hexadecimal digit sequence
+            if (matcher.find()) {
+                // Convert the hexadecimal digits to an integer
+                int value = Integer.parseInt(matcher.group(), 16);
+
+                TrafficSelector.Builder sBuilder = DefaultTrafficSelector.builder()
+                        .matchTunnelId(Long.parseLong(Integer.toString(value)));
+
+                TrafficTreatment.Builder tBuilder = DefaultTrafficTreatment.builder()
+                        .setOutput(port.number());
+
+                flowRuleService.setRule(
+                        appId,
+                        device.id(),
+                        sBuilder.build(),
+                        tBuilder.build(),
+                        PRIORITY_TUNNEL_RULE,
+                        TUNNEL_DEFAULT_TABLE,
+                        install);
+
+                log.debug("Set ingress rules for segment ID {}", value);
+            }
         }
     }
-
     private class InternalKubevirtPortListener implements KubevirtPortListener {
 
         @Override
diff --git a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/util/KubevirtNetworkingUtil.java b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/util/KubevirtNetworkingUtil.java
index d1ec93f..d60800a 100644
--- a/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/util/KubevirtNetworkingUtil.java
+++ b/apps/kubevirt-networking/app/src/main/java/org/onosproject/kubevirtnetworking/util/KubevirtNetworkingUtil.java
@@ -457,6 +457,19 @@
     }
 
     /**
+     * Gets the number of tenant networks which have the identical segmentation ID of the given network.
+     *
+     * @param networkService    network service
+     * @param network           kubevirt network
+     * @return number of tenant networks
+     */
+    public static long numOfDupSegNetworks(KubevirtNetworkService networkService, KubevirtNetwork network) {
+        return networkService.networks().stream()
+                .filter(n -> Objects.equals(network.segmentId(), n.segmentId()))
+                .filter(n -> !Objects.equals(network.networkId(), n.networkId())).count();
+    }
+
+    /**
      * Obtains the tunnel bridge to tenant bridge patch port number.
      *
      * @param deviceService device service