gt:                         true, ip4Address.get(), add.getMacAddress().getValue());
context: /*
   * Copyright (c) 2016 Ericsson India Global Services Pvt Ltd. and others.  All rights reserved.
   *
   * This program and the accompanying materials are made available under the
   * terms of the Eclipse Public License v1.0 which accompanies this distribution,
   * and is available at http://www.eclipse.org/legal/epl-v10.html
   */
  package org.opendaylight.netvirt.dhcpservice;
  
  import static org.opendaylight.mdsal.binding.util.Datastore.CONFIGURATION;
  import static org.opendaylight.mdsal.binding.util.Datastore.OPERATIONAL;
  
  import java.util.ArrayList;
  import java.util.Collections;
  import java.util.List;
  import java.util.Locale;
  import java.util.function.Consumer;
  import javax.annotation.PreDestroy;
  import javax.inject.Inject;
  import javax.inject.Named;
  import javax.inject.Singleton;
  import org.opendaylight.genius.interfacemanager.interfaces.IInterfaceManager;
  import org.opendaylight.genius.mdsalutil.NwConstants;
  import org.opendaylight.infrautils.jobcoordinator.JobCoordinator;
  import org.opendaylight.infrautils.utils.concurrent.Executors;
  import org.opendaylight.mdsal.binding.api.DataBroker;
  import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunner;
  import org.opendaylight.mdsal.binding.util.ManagedNewTransactionRunnerImpl;
  import org.opendaylight.mdsal.common.api.LogicalDatastoreType;
  import org.opendaylight.netvirt.dhcpservice.api.DhcpMConstants;
  import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput;
  import org.opendaylight.netvirt.elan.arp.responder.ArpResponderInput.ArpReponderInputBuilder;
  import org.opendaylight.netvirt.elan.arp.responder.ArpResponderUtil;
  import org.opendaylight.netvirt.elanmanager.api.ElanHelper;
  import org.opendaylight.netvirt.elanmanager.api.IElanService;
  import org.opendaylight.netvirt.neutronvpn.api.utils.NeutronConstants;
  import org.opendaylight.serviceutils.tools.listener.AbstractClusteredAsyncDataTreeChangeListener;
  import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev130715.Uuid;
  import org.opendaylight.yang.gen.v1.urn.opendaylight.genius.itm.rpcs.rev160406.ItmRpcService;
  import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.binding.rev150712.PortBindingExtension;
  import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.port.attributes.FixedIps;
  import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.Ports;
  import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.ports.rev150712.ports.attributes.ports.Port;
  import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.rev150712.Neutron;
  import org.opendaylight.yang.gen.v1.urn.opendaylight.neutron.subnets.rev150712.subnets.attributes.subnets.Subnet;
  import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.dhcpservice.config.rev150710.DhcpserviceConfig;
  import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
  import org.opendaylight.yangtools.yang.common.Uint64;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  @Singleton
  public class DhcpNeutronPortListener extends AbstractClusteredAsyncDataTreeChangeListener<Port> {
  
      private static final Logger LOG = LoggerFactory.getLogger(DhcpNeutronPortListener.class);
      private final DhcpExternalTunnelManager dhcpExternalTunnelManager;
      private final IElanService elanService;
      private final DataBroker broker;
      private final ManagedNewTransactionRunner txRunner;
      private final DhcpserviceConfig config;
      private final IInterfaceManager interfaceManager;
      private final JobCoordinator jobCoordinator;
      private final DhcpManager dhcpManager;
      private final ItmRpcService itmRpcService;
  
      @Inject
      public DhcpNeutronPortListener(DataBroker db, DhcpExternalTunnelManager dhcpExternalTunnelManager,
              @Named("elanService") IElanService ielanService, IInterfaceManager interfaceManager,
              DhcpserviceConfig config, final JobCoordinator jobCoordinator, DhcpManager dhcpManager,
              ItmRpcService itmRpcService) {
          super(db, LogicalDatastoreType.CONFIGURATION, InstanceIdentifier.create(Neutron.class).child(Ports.class)
                  .child(Port.class),
                  Executors.newListeningSingleThreadExecutor("DhcpNeutronPortListener", LOG));
          this.dhcpExternalTunnelManager = dhcpExternalTunnelManager;
          this.elanService = ielanService;
          this.interfaceManager = interfaceManager;
          this.broker = db;
          this.txRunner = new ManagedNewTransactionRunnerImpl(db);
          this.config = config;
          this.jobCoordinator = jobCoordinator;
          this.dhcpManager = dhcpManager;
          this.itmRpcService = itmRpcService;
          init();
      }
  
      public void init() {
          if (config.isControllerDhcpEnabled()) {
              LOG.info("{} init", getClass().getSimpleName());
          }
      }
  
      @Override
      @PreDestroy
      public void close() {
          super.close();
          Executors.shutdownAndAwaitTermination(getExecutorService());
          LOG.debug("DhcpNeutronPortListener Listener Closed");
      }
  
      @Override
      public void remove(InstanceIdentifier<Port> identifier, Port del) {
          if (!config.isControllerDhcpEnabled()) {
              return;
          }
          LOG.trace("Port removed: {}", del);
          if (NeutronConstants.IS_ODL_DHCP_PORT.test(del)) {
              jobCoordinator.enqueueJob(getJobKey(del),
                  () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
                      java.util.Optional<String> ip4Address = DhcpServiceUtils.getIpV4Address(del);
                      if (ip4Address.isPresent()) {
                          dhcpExternalTunnelManager.addOrRemoveDhcpArpFlowforElan(del.getNetworkId().getValue(),
                                  false, ip4Address.get(), del.getMacAddress().getValue());
                      }
                      DhcpServiceUtils.removeSubnetDhcpPortData(del, tx::delete);
                      processArpResponderForElanDpns(del, arpInput -> {
                          LOG.trace("Removing ARPResponder Flows  for dhcp port {} with ipaddress {} with mac {} "
                                          + " on dpn {}. ",arpInput.getInterfaceName(), arpInput.getSpa(),
                                          arpInput.getSha(), arpInput.getDpId());
                          elanService.removeArpResponderFlow(arpInput);
                      });
                  })));
          }
          if (isVnicTypeDirectOrMacVtap(del)) {
              removePort(del);
          }
      }
  
      private String getJobKey(Port port) {
          return "PORT- " + port.getUuid().getValue();
      }
  
      @Override
      public void update(InstanceIdentifier<Port> identifier, Port original, Port update) {
          if (!config.isControllerDhcpEnabled()) {
              return;
          }
          LOG.trace("Port changed to {}", update);
          //With Ipv6 changes we can get ipv4 subnets later. The below check is to support such scenario.
          if (original.nonnullFixedIps().size() < update.nonnullFixedIps().size()) {
              final String interfaceName = update.getUuid().getValue();
              List<FixedIps> updatedFixedIps = new ArrayList<>(update.nonnullFixedIps().values());
              // Need to check only the newly added fixed ip.
              updatedFixedIps.removeAll(original.nonnullFixedIps().values());
              Subnet subnet = dhcpManager.getNeutronSubnet(updatedFixedIps);
              if (null == subnet || !subnet.isEnableDhcp()) {
                  LOG.trace("Subnet is null/not ipv4 or not enabled {}", subnet);
                  return;
              }
              // Binding the DHCP service for an existing port because of subnet change.
              jobCoordinator.enqueueJob(DhcpServiceUtils.getJobKey(interfaceName),
                  () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
                      LOG.debug("Binding DHCP service for interface {}", interfaceName);
                      DhcpServiceUtils.bindDhcpService(interfaceName, NwConstants.DHCP_TABLE, tx);
                  })), DhcpMConstants.RETRY_COUNT);
              jobCoordinator.enqueueJob(DhcpServiceUtils.getJobKey(interfaceName), () -> {
                  Uint64 dpnId = interfaceManager.getDpnForInterface(interfaceName);
                  if (dpnId == null || dpnId.equals(DhcpMConstants.INVALID_DPID)) {
                      LOG.trace("Unable to install the DHCP flow since dpn is not available");
                      return Collections.emptyList();
                  }
                  String vmMacAddress = txRunner.applyWithNewReadWriteTransactionAndSubmit(OPERATIONAL,
                      tx -> DhcpServiceUtils.getAndUpdateVmMacAddress(tx, interfaceName, dhcpManager)).get();
                  return Collections.singletonList(txRunner.callWithNewReadWriteTransactionAndSubmit(CONFIGURATION,
                      tx -> dhcpManager.installDhcpEntries(dpnId, vmMacAddress, tx)));
              }, DhcpMConstants.RETRY_COUNT);
          }
          if (!isVnicTypeDirectOrMacVtap(update)) {
              LOG.trace("Port updated is normal {}", update.getUuid());
              if (isVnicTypeDirectOrMacVtap(original)) {
                  LOG.trace("Original Port was direct/macvtap {} so removing flows and cache entry if any",
                          update.getUuid());
                  removePort(original);
              }
              return;
          }
          if (!isVnicTypeDirectOrMacVtap(original)) {
              LOG.trace("Original port was normal and updated is direct. Calling addPort()");
              addPort(update);
              return;
          }
          String macOriginal = getMacAddress(original);
          String macUpdated = getMacAddress(update);
          String segmentationIdOriginal = DhcpServiceUtils.getSegmentationId(original.getNetworkId(), broker);
          String segmentationIdUpdated = DhcpServiceUtils.getSegmentationId(update.getNetworkId(), broker);
          if (macOriginal != null && !macOriginal.equalsIgnoreCase(macUpdated) && segmentationIdOriginal != null
                  && !segmentationIdOriginal.equalsIgnoreCase(segmentationIdUpdated)) {
              LOG.trace("Mac/segment id has changed");
              dhcpExternalTunnelManager.removeVniMacToPortCache(Uint64.valueOf(segmentationIdOriginal), macOriginal);
              dhcpExternalTunnelManager.updateVniMacToPortCache(Uint64.valueOf(segmentationIdUpdated),
                      macUpdated, update);
          }
      }
  
      @Override
      public void add(InstanceIdentifier<Port> identifier, Port add) {
          if (!config.isControllerDhcpEnabled()) {
              return;
          }
          LOG.trace("Port added {}", add);
          if (NeutronConstants.IS_ODL_DHCP_PORT.test(add)) {
              jobCoordinator.enqueueJob(getJobKey(add),
                  () -> Collections.singletonList(txRunner.callWithNewWriteOnlyTransactionAndSubmit(CONFIGURATION, tx -> {
                      DhcpServiceUtils.createSubnetDhcpPortData(add, tx::put);
                      processArpResponderForElanDpns(add, arpInput -> {
                          LOG.trace(
                                  "Installing ARP RESPONDER Flows  for dhcp port {} ipaddress {} with mac {} on dpn {}",
                                  arpInput.getInterfaceName(), arpInput.getSpa(), arpInput.getSha(),
                                  arpInput.getDpId());
                          ArpReponderInputBuilder builder = new ArpReponderInputBuilder(arpInput);
                          builder.setInstructions(ArpResponderUtil.getInterfaceInstructions(interfaceManager,
                                  arpInput.getInterfaceName(), arpInput.getSpa(), arpInput.getSha(), itmRpcService));
                          elanService.addArpResponderFlow(builder.buildForInstallFlow());
                      });
                  })));
              java.util.Optional<String> ip4Address = DhcpServiceUtils.getIpV4Address(add);
              if (ip4Address.isPresent()) {
                  dhcpExternalTunnelManager.addOrRemoveDhcpArpFlowforElan(add.getNetworkId().getValue(),
  