This is an automated email from the ASF dual-hosted git repository.

rohit pushed a commit to branch 4.11
in repository https://gitbox.apache.org/repos/asf/cloudstack.git


The following commit(s) were added to refs/heads/4.11 by this push:
     new c9ce3e2  router: Persistent DHCP leases file on VRs and cleanup 
/etc/hosts on VM deletion (#3351)
c9ce3e2 is described below

commit c9ce3e2344ab863dae09e63bf783331097a660cb
Author: Nicolas Vazquez <nicovazque...@gmail.com>
AuthorDate: Mon Jun 3 08:34:16 2019 -0300

    router: Persistent DHCP leases file on VRs and cleanup /etc/hosts on VM 
deletion (#3351)
    
    Since the CloudStack virtual router was redesigned on version 4.6 it has 
been observed that the DHCP leases file is not persistent across network 
operations. This causes conflicts on guest VMs static IPs, causing these static 
IPs to not be renewed by the DHCP server running on isolated and VPC networks' 
virtual routers (dnsmasq). On stopping or destroying a VM, its dhcp/dns records 
are not removed from the virtual router causing ghost effects.
    
    Fixes #3272
    Fixes #3354
    
    Signed-off-by: Rohit Yadav <rohit.ya...@shapeblue.com>
---
 .../cloud/network/element/DhcpServiceProvider.java |  2 ++
 .../cloud/agent/api/routing/DhcpEntryCommand.java  |  9 ++++++
 .../agent/resource/virtualnetwork/VRScripts.java   |  1 -
 .../virtualnetwork/VirtualRoutingResource.java     |  2 +-
 .../virtualnetwork/facade/DhcpEntryConfigItem.java |  2 +-
 .../virtualnetwork/model/VmDhcpConfig.java         | 14 ++++++++-
 .../service/NetworkOrchestrationService.java       |  4 +++
 .../engine/orchestration/NetworkOrchestrator.java  | 28 +++++++++++++++++
 .../networkservice/BaremetalDhcpElement.java       |  5 ++++
 .../contrail/management/ContrailElementImpl.java   |  5 ++++
 .../com/cloud/network/element/NuageVspElement.java |  5 ++++
 .../network/element/VirtualRouterElement.java      | 28 +++++++++++++++++
 .../cloud/network/router/CommandSetupHelper.java   |  5 ++--
 .../com/cloud/network/rules/DhcpEntryRules.java    | 10 +++++++
 server/src/com/cloud/vm/UserVmManagerImpl.java     | 15 +++++++---
 .../network/topology/AdvancedNetworkTopology.java  | 15 ++++++++++
 .../network/topology/AdvancedNetworkVisitor.java   |  3 +-
 .../network/topology/BasicNetworkTopology.java     | 21 +++++++++++++
 .../network/topology/BasicNetworkVisitor.java      |  3 +-
 .../network/topology/NetworkTopology.java          |  2 ++
 .../test/com/cloud/vpc/MockNetworkManagerImpl.java |  4 +++
 systemvm/debian/opt/cloud/bin/cs/CsDhcp.py         | 35 ++++++++++++++++++----
 systemvm/debian/opt/cloud/bin/cs_dhcp.py           |  4 +++
 23 files changed, 205 insertions(+), 17 deletions(-)

diff --git a/api/src/com/cloud/network/element/DhcpServiceProvider.java 
b/api/src/com/cloud/network/element/DhcpServiceProvider.java
index 12830f8..3f530c2 100644
--- a/api/src/com/cloud/network/element/DhcpServiceProvider.java
+++ b/api/src/com/cloud/network/element/DhcpServiceProvider.java
@@ -37,4 +37,6 @@ public interface DhcpServiceProvider extends NetworkElement {
     boolean removeDhcpSupportForSubnet(Network network) throws 
ResourceUnavailableException;
 
     boolean setExtraDhcpOptions(Network network, long nicId, Map<Integer, 
String> dhcpOptions);
+
+    boolean removeDhcpEntry(Network network, NicProfile nic, 
VirtualMachineProfile vmProfile) throws ResourceUnavailableException;
 }
diff --git a/core/src/com/cloud/agent/api/routing/DhcpEntryCommand.java 
b/core/src/com/cloud/agent/api/routing/DhcpEntryCommand.java
index b942806..7fb65fe 100644
--- a/core/src/com/cloud/agent/api/routing/DhcpEntryCommand.java
+++ b/core/src/com/cloud/agent/api/routing/DhcpEntryCommand.java
@@ -35,6 +35,15 @@ public class DhcpEntryCommand extends NetworkElementCommand {
     String duid;
     private boolean isDefault;
     boolean executeInSequence = false;
+    boolean remove;
+
+    public boolean isRemove() {
+        return remove;
+    }
+
+    public void setRemove(boolean remove) {
+        this.remove = remove;
+    }
 
     protected DhcpEntryCommand() {
 
diff --git a/core/src/com/cloud/agent/resource/virtualnetwork/VRScripts.java 
b/core/src/com/cloud/agent/resource/virtualnetwork/VRScripts.java
index 838f087..bd108a9 100644
--- a/core/src/com/cloud/agent/resource/virtualnetwork/VRScripts.java
+++ b/core/src/com/cloud/agent/resource/virtualnetwork/VRScripts.java
@@ -68,5 +68,4 @@ public class VRScripts {
     public static final String UPDATE_HOST_PASSWD = "update_host_passwd.sh";
 
     public static final String VR_CFG = "vr_cfg.sh";
-
 }
\ No newline at end of file
diff --git 
a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java 
b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
index 1367218..8dd0620 100644
--- 
a/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
+++ 
b/core/src/com/cloud/agent/resource/virtualnetwork/VirtualRoutingResource.java
@@ -22,7 +22,6 @@ package com.cloud.agent.resource.virtualnetwork;
 import java.io.IOException;
 import java.net.InetSocketAddress;
 import java.nio.channels.SocketChannel;
-import org.joda.time.Duration;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -41,6 +40,7 @@ import org.apache.cloudstack.ca.SetupKeyStoreCommand;
 import org.apache.cloudstack.ca.SetupKeystoreAnswer;
 import org.apache.cloudstack.utils.security.KeyStoreUtils;
 import org.apache.log4j.Logger;
+import org.joda.time.Duration;
 
 import com.cloud.agent.api.Answer;
 import com.cloud.agent.api.CheckRouterAnswer;
diff --git 
a/core/src/com/cloud/agent/resource/virtualnetwork/facade/DhcpEntryConfigItem.java
 
b/core/src/com/cloud/agent/resource/virtualnetwork/facade/DhcpEntryConfigItem.java
index 77d436d..0710ecc 100644
--- 
a/core/src/com/cloud/agent/resource/virtualnetwork/facade/DhcpEntryConfigItem.java
+++ 
b/core/src/com/cloud/agent/resource/virtualnetwork/facade/DhcpEntryConfigItem.java
@@ -35,7 +35,7 @@ public class DhcpEntryConfigItem extends 
AbstractConfigItemFacade {
         final DhcpEntryCommand command = (DhcpEntryCommand) cmd;
 
         final VmDhcpConfig vmDhcpConfig = new 
VmDhcpConfig(command.getVmName(), command.getVmMac(), command.getVmIpAddress(), 
command.getVmIp6Address(), command.getDuid(), command.getDefaultDns(),
-                command.getDefaultRouter(), command.getStaticRoutes(), 
command.isDefault());
+                command.getDefaultRouter(), command.getStaticRoutes(), 
command.isDefault(), command.isRemove());
 
         return generateConfigItems(vmDhcpConfig);
     }
diff --git 
a/core/src/com/cloud/agent/resource/virtualnetwork/model/VmDhcpConfig.java 
b/core/src/com/cloud/agent/resource/virtualnetwork/model/VmDhcpConfig.java
index 150b8cf..d9cb8b0 100644
--- a/core/src/com/cloud/agent/resource/virtualnetwork/model/VmDhcpConfig.java
+++ b/core/src/com/cloud/agent/resource/virtualnetwork/model/VmDhcpConfig.java
@@ -30,12 +30,15 @@ public class VmDhcpConfig extends ConfigBase {
     private String staticRoutes;
     private boolean defaultEntry;
 
+    // Indicate if the entry should be removed when set to true
+    private boolean remove;
+
     public VmDhcpConfig() {
         super(VM_DHCP);
     }
 
     public VmDhcpConfig(String hostName, String macAddress, String 
ipv4Address, String ipv6Address, String ipv6Duid, String dnsAddresses, String 
defaultGateway,
-            String staticRoutes, boolean defaultEntry) {
+            String staticRoutes, boolean defaultEntry, boolean remove) {
         super(VM_DHCP);
         this.hostName = hostName;
         this.macAddress = macAddress;
@@ -46,6 +49,7 @@ public class VmDhcpConfig extends ConfigBase {
         this.defaultGateway = defaultGateway;
         this.staticRoutes = staticRoutes;
         this.defaultEntry = defaultEntry;
+        this.remove = remove;
     }
 
     public String getHostName() {
@@ -64,6 +68,14 @@ public class VmDhcpConfig extends ConfigBase {
         this.macAddress = macAddress;
     }
 
+    public boolean isRemove() {
+        return remove;
+    }
+
+    public void setRemove(boolean remove) {
+        this.remove = remove;
+    }
+
     public String getIpv4Address() {
         return ipv4Address;
     }
diff --git 
a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
 
b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
index fa7601d..94a4259 100644
--- 
a/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
+++ 
b/engine/api/src/org/apache/cloudstack/engine/orchestration/service/NetworkOrchestrationService.java
@@ -309,4 +309,8 @@ public interface NetworkOrchestrationService {
      */
     boolean areRoutersRunning(final List<? extends VirtualRouter> routers);
 
+    /**
+     * Remove entry from /etc/dhcphosts and /etc/hosts on virtual routers
+     */
+    void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile 
vmProfile, NicProfile nicProfile);
 }
diff --git 
a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
 
b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
index 9f4778e..9cceee8 100644
--- 
a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
+++ 
b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java
@@ -2913,6 +2913,34 @@ public class NetworkOrchestrator extends ManagerBase 
implements NetworkOrchestra
     }
 
     /**
+     * Cleanup entry on VR file specified by type
+     */
+    @Override
+    public void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile 
vmProfile, NicProfile nicProfile) {
+
+        final List<Provider> networkProviders = 
getNetworkProviders(network.getId());
+        for (final NetworkElement element : networkElements) {
+            if (networkProviders.contains(element.getProvider())) {
+                if 
(!_networkModel.isProviderEnabledInPhysicalNetwork(_networkModel.getPhysicalNetworkId(network),
 element.getProvider().getName())) {
+                    throw new CloudRuntimeException("Service provider " + 
element.getProvider().getName() + " either doesn't exist or is not enabled in 
physical network id: "
+                            + network.getPhysicalNetworkId());
+                }
+                if (vmProfile.getType() == Type.User && element.getProvider() 
!= null) {
+                    if 
(_networkModel.areServicesSupportedInNetwork(network.getId(), Service.Dhcp)
+                            && 
_networkModel.isProviderSupportServiceInNetwork(network.getId(), Service.Dhcp, 
element.getProvider()) && element instanceof DhcpServiceProvider) {
+                        final DhcpServiceProvider sp = (DhcpServiceProvider) 
element;
+                        try {
+                            sp.removeDhcpEntry(network, nicProfile, vmProfile);
+                        } catch (ResourceUnavailableException e) {
+                            s_logger.error("Failed to remove dhcp-dns entry 
due to: ", e);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * rollingRestartRouters performs restart of routers of a network by first
      * deploying a new VR and then destroying old VRs in rolling fashion. For
      * non-redundant network, it will re-program the new router as final step
diff --git 
a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
 
b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
index 5696048..807babc 100644
--- 
a/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
+++ 
b/plugins/hypervisors/baremetal/src/com/cloud/baremetal/networkservice/BaremetalDhcpElement.java
@@ -185,4 +185,9 @@ public class BaremetalDhcpElement extends AdapterBase 
implements DhcpServiceProv
         return false;
     }
 
+    @Override
+    public boolean removeDhcpEntry(Network network, NicProfile nic, 
VirtualMachineProfile vmProfile) throws ResourceUnavailableException {
+        return false;
+    }
+
 }
diff --git 
a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
 
b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
index 62ca29c..4771441 100644
--- 
a/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
+++ 
b/plugins/network-elements/juniper-contrail/src/org/apache/cloudstack/network/contrail/management/ContrailElementImpl.java
@@ -379,4 +379,9 @@ public class ContrailElementImpl extends AdapterBase
     public boolean setExtraDhcpOptions(Network network, long nicId, 
Map<Integer, String> dhcpOptions) {
         return false;
     }
+
+    @Override
+    public boolean removeDhcpEntry(Network network, NicProfile nic, 
VirtualMachineProfile vmProfile) {
+        return false;
+    }
 }
diff --git 
a/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java
 
b/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java
index 48c3d88..0f6bff9 100644
--- 
a/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java
+++ 
b/plugins/network-elements/nuage-vsp/src/com/cloud/network/element/NuageVspElement.java
@@ -542,6 +542,11 @@ public class NuageVspElement extends AdapterBase 
implements ConnectivityProvider
     }
 
     @Override
+    public boolean removeDhcpEntry(Network network, NicProfile nic, 
VirtualMachineProfile vmProfile) {
+        return false;
+    }
+
+    @Override
     public boolean applyStaticNats(Network config, List<? extends StaticNat> 
rules) throws ResourceUnavailableException {
         List<VspStaticNat> vspStaticNatDetails = new ArrayList<VspStaticNat>();
         for (StaticNat staticNat : rules) {
diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java 
b/server/src/com/cloud/network/element/VirtualRouterElement.java
index 58d9343..11a553c 100644
--- a/server/src/com/cloud/network/element/VirtualRouterElement.java
+++ b/server/src/com/cloud/network/element/VirtualRouterElement.java
@@ -947,6 +947,34 @@ NetworkMigrationResponder, AggregatedCommandExecutor, 
RedundantResource, DnsServ
     }
 
     @Override
+    public boolean removeDhcpEntry(Network network, NicProfile nic, 
VirtualMachineProfile vmProfile) throws ResourceUnavailableException {
+        boolean result = true;
+        if (canHandle(network, Service.Dhcp)) {
+            if (vmProfile.getType() != VirtualMachine.Type.User) {
+                return false;
+            }
+
+            final List<DomainRouterVO> routers = 
_routerDao.listByNetworkAndRole(network.getId(), 
VirtualRouter.Role.VIRTUAL_ROUTER);
+
+            if (CollectionUtils.isEmpty(routers)) {
+                throw new ResourceUnavailableException("Can't find at least 
one router!", DataCenter.class, network.getDataCenterId());
+            }
+
+            final DataCenterVO dcVO = 
_dcDao.findById(network.getDataCenterId());
+            final NetworkTopology networkTopology = 
networkTopologyContext.retrieveNetworkTopology(dcVO);
+
+            for (final DomainRouterVO domainRouterVO : routers) {
+                if (domainRouterVO.getState() != VirtualMachine.State.Running) 
{
+                    continue;
+                }
+
+                result = result && networkTopology.removeDhcpEntry(network, 
nic, vmProfile, domainRouterVO);
+            }
+        }
+        return result;
+    }
+
+    @Override
     public boolean removeDnsSupportForSubnet(Network network) throws 
ResourceUnavailableException {
         // Ignore if virtual router is already dhcp provider
         if (_networkModel.isProviderSupportServiceInNetwork(network.getId(), 
Service.Dhcp, getProvider())) {
diff --git a/server/src/com/cloud/network/router/CommandSetupHelper.java 
b/server/src/com/cloud/network/router/CommandSetupHelper.java
index c8d85fe..fe231c3 100644
--- a/server/src/com/cloud/network/router/CommandSetupHelper.java
+++ b/server/src/com/cloud/network/router/CommandSetupHelper.java
@@ -211,7 +211,7 @@ public class CommandSetupHelper {
         cmds.addCommand("users", cmd);
     }
 
-    public void createDhcpEntryCommand(final VirtualRouter router, final 
UserVm vm, final NicVO nic, final Commands cmds) {
+    public void createDhcpEntryCommand(final VirtualRouter router, final 
UserVm vm, final NicVO nic, boolean remove, final Commands cmds) {
         final DhcpEntryCommand dhcpCommand = new 
DhcpEntryCommand(nic.getMacAddress(), nic.getIPv4Address(), vm.getHostName(), 
nic.getIPv6Address(),
                 _networkModel.getExecuteInSeqNtwkElmtCmd());
 
@@ -229,6 +229,7 @@ public class CommandSetupHelper {
         dhcpCommand.setDefaultDns(ipaddress);
         dhcpCommand.setDuid(NetUtils.getDuidLL(nic.getMacAddress()));
         dhcpCommand.setDefault(nic.isDefaultNic());
+        dhcpCommand.setRemove(remove);
 
         dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, 
_routerControlHelper.getRouterControlIp(router.getId()));
         dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, 
router.getInstanceName());
@@ -622,7 +623,7 @@ public class CommandSetupHelper {
             final NicVO nic = 
_nicDao.findByNtwkIdAndInstanceId(guestNetworkId, vm.getId());
             if (nic != null) {
                 s_logger.debug("Creating dhcp entry for vm " + vm + " on domR 
" + router + ".");
-                createDhcpEntryCommand(router, vm, nic, cmds);
+                createDhcpEntryCommand(router, vm, nic, false, cmds);
             }
         }
     }
diff --git a/server/src/com/cloud/network/rules/DhcpEntryRules.java 
b/server/src/com/cloud/network/rules/DhcpEntryRules.java
index c4a91f4..530cf0a 100644
--- a/server/src/com/cloud/network/rules/DhcpEntryRules.java
+++ b/server/src/com/cloud/network/rules/DhcpEntryRules.java
@@ -36,6 +36,8 @@ public class DhcpEntryRules extends RuleApplier {
     private final VirtualMachineProfile _profile;
     private final DeployDestination _destination;
 
+    private boolean remove;
+
     private NicVO _nicVo;
     private UserVmVO _userVM;
 
@@ -77,4 +79,12 @@ public class DhcpEntryRules extends RuleApplier {
     public UserVmVO getUserVM() {
         return _userVM;
     }
+
+    public boolean isRemove() {
+        return remove;
+    }
+
+    public void setRemove(boolean remove) {
+        this.remove = remove;
+    }
 }
\ No newline at end of file
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java 
b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 8857fe9..5e0bb1b 100644
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -302,6 +302,7 @@ import com.cloud.vm.dao.VMInstanceDao;
 import com.cloud.vm.snapshot.VMSnapshotManager;
 import com.cloud.vm.snapshot.VMSnapshotVO;
 import com.cloud.vm.snapshot.dao.VMSnapshotDao;
+import com.google.common.base.Strings;
 
 
 public class UserVmManagerImpl extends ManagerBase implements UserVmManager, 
VirtualMachineGuru, UserVmService, Configurable {
@@ -4377,10 +4378,16 @@ public class UserVmManagerImpl extends ManagerBase 
implements UserVmManager, Vir
             }
         }
 
-        List<NicVO> nics = _nicDao.listByVmId(vm.getId());
-        for (NicVO nic : nics) {
-            NetworkVO network = _networkDao.findById(nic.getNetworkId());
-            if (network.getTrafficType() == TrafficType.Guest) {
+        final List<NicVO> nics = _nicDao.listByVmId(vm.getId());
+        for (final NicVO nic : nics) {
+            final NetworkVO network = _networkDao.findById(nic.getNetworkId());
+            if (network != null && network.getTrafficType() == 
TrafficType.Guest) {
+                final String nicIp = 
Strings.isNullOrEmpty(nic.getIPv4Address()) ? nic.getIPv6Address() : 
nic.getIPv4Address();
+                if (!Strings.isNullOrEmpty(nicIp)) {
+                    NicProfile nicProfile = new 
NicProfile(nic.getIPv4Address(), nic.getIPv6Address(), nic.getMacAddress());
+                    nicProfile.setId(nic.getId());
+                    _networkMgr.cleanupNicDhcpDnsEntry(network, profile, 
nicProfile);
+                }
                 if (nic.getBroadcastUri() != null && 
nic.getBroadcastUri().getScheme().equals("pvlan")) {
                     NicProfile nicProfile = new NicProfile(nic, network, 
nic.getBroadcastUri(), nic.getIsolationUri(), 0, false, "pvlan-nic");
                     setupVmForPvlan(false, vm.getHostId(), nicProfile);
diff --git 
a/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java
 
b/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java
index f456fce..f35f142 100644
--- 
a/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java
+++ 
b/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java
@@ -173,6 +173,21 @@ public class AdvancedNetworkTopology extends 
BasicNetworkTopology {
     }
 
     @Override
+    public boolean removeDhcpEntry(Network network, NicProfile nic, 
VirtualMachineProfile profile, VirtualRouter virtualRouter) throws 
ResourceUnavailableException {
+        s_logger.debug("REMOVE VPC DHCP ENTRY RULES");
+
+        final String typeString = "dhcp entry";
+        final Long podId = null;
+        final boolean isPodLevelException = false;
+        final boolean failWhenDisconnect = false;
+
+        final DhcpEntryRules dhcpRules = new DhcpEntryRules(network, nic, 
profile, null);
+        dhcpRules.setRemove(true);
+
+        return applyRules(network, virtualRouter, typeString, 
isPodLevelException, podId, failWhenDisconnect, new 
RuleApplierWrapper<RuleApplier>(dhcpRules));
+    }
+
+    @Override
     public boolean associatePublicIP(final Network network, final List<? 
extends PublicIpAddress> ipAddresses, final VirtualRouter router)
             throws ResourceUnavailableException {
 
diff --git 
a/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkVisitor.java 
b/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkVisitor.java
index b5283da..c21b6d7 100644
--- 
a/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkVisitor.java
+++ 
b/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkVisitor.java
@@ -80,8 +80,9 @@ public class AdvancedNetworkVisitor extends 
BasicNetworkVisitor {
         final Commands commands = new Commands(Command.OnError.Stop);
         final NicVO nicVo = dhcp.getNicVo();
         final UserVmVO userVM = dhcp.getUserVM();
+        final boolean remove = dhcp.isRemove();
 
-        _commandSetupHelper.createDhcpEntryCommand(router, userVM, nicVo, 
commands);
+        _commandSetupHelper.createDhcpEntryCommand(router, userVM, nicVo, 
remove, commands);
 
         return _networkGeneralHelper.sendCommandsToRouter(router, commands);
     }
diff --git 
a/server/src/org/apache/cloudstack/network/topology/BasicNetworkTopology.java 
b/server/src/org/apache/cloudstack/network/topology/BasicNetworkTopology.java
index 0869b22..a7fbe31 100644
--- 
a/server/src/org/apache/cloudstack/network/topology/BasicNetworkTopology.java
+++ 
b/server/src/org/apache/cloudstack/network/topology/BasicNetworkTopology.java
@@ -442,4 +442,25 @@ public class BasicNetworkTopology implements 
NetworkTopology {
         }
         return result;
     }
+
+    @Override
+    public boolean removeDhcpEntry(Network network, NicProfile nic, 
VirtualMachineProfile profile, VirtualRouter virtualRouter) throws 
ResourceUnavailableException {
+        s_logger.debug("REMOVING DHCP ENTRY RULE");
+
+        final String typeString = "dhcp entry";
+        final Long podId = profile.getVirtualMachine().getPodIdToDeployIn();
+        boolean isPodLevelException = false;
+
+        if (podId != null && profile.getVirtualMachine().getType() == 
VirtualMachine.Type.User && network.getTrafficType() == TrafficType.Guest
+                && network.getGuestType() == Network.GuestType.Shared) {
+            isPodLevelException = true;
+        }
+
+        final boolean failWhenDisconnect = false;
+
+        final DhcpEntryRules dhcpRules = new DhcpEntryRules(network, nic, 
profile, null);
+        dhcpRules.setRemove(true);
+
+        return applyRules(network, virtualRouter, typeString, 
isPodLevelException, podId, failWhenDisconnect, new 
RuleApplierWrapper<RuleApplier>(dhcpRules));
+    }
 }
\ No newline at end of file
diff --git 
a/server/src/org/apache/cloudstack/network/topology/BasicNetworkVisitor.java 
b/server/src/org/apache/cloudstack/network/topology/BasicNetworkVisitor.java
index 53ddebb..35d846b 100644
--- a/server/src/org/apache/cloudstack/network/topology/BasicNetworkVisitor.java
+++ b/server/src/org/apache/cloudstack/network/topology/BasicNetworkVisitor.java
@@ -196,9 +196,10 @@ public class BasicNetworkVisitor extends 
NetworkTopologyVisitor {
         final NicVO nicVo = dhcp.getNicVo();
         final UserVmVO userVM = dhcp.getUserVM();
         final DeployDestination destination = dhcp.getDestination();
+        final boolean remove = dhcp.isRemove();
 
         if (router.getPodIdToDeployIn().longValue() == 
destination.getPod().getId()) {
-            _commandSetupHelper.createDhcpEntryCommand(router, userVM, nicVo, 
commands);
+            _commandSetupHelper.createDhcpEntryCommand(router, userVM, nicVo, 
remove, commands);
 
             return _networkGeneralHelper.sendCommandsToRouter(router, 
commands);
         }
diff --git 
a/server/src/org/apache/cloudstack/network/topology/NetworkTopology.java 
b/server/src/org/apache/cloudstack/network/topology/NetworkTopology.java
index 5190d5e..fa76f83 100644
--- a/server/src/org/apache/cloudstack/network/topology/NetworkTopology.java
+++ b/server/src/org/apache/cloudstack/network/topology/NetworkTopology.java
@@ -87,4 +87,6 @@ public interface NetworkTopology {
 
     boolean applyRules(final Network network, final VirtualRouter router, 
final String typeString, final boolean isPodLevelException, final Long podId,
             final boolean failWhenDisconnect, RuleApplierWrapper<RuleApplier> 
ruleApplier) throws ResourceUnavailableException;
+
+    boolean removeDhcpEntry(final Network network, final NicProfile nic, final 
VirtualMachineProfile profile, final VirtualRouter virtualRouter) throws 
ResourceUnavailableException;
 }
\ No newline at end of file
diff --git a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java 
b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
index 8cbf30c..980b440 100644
--- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
+++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java
@@ -925,6 +925,10 @@ public class MockNetworkManagerImpl extends ManagerBase 
implements NetworkOrches
     }
 
     @Override
+    public void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile 
vmProfile, NicProfile nicProfile) {
+    }
+
+    @Override
     public void finalizeUpdateInSequence(Network network, boolean success) {
         return;
     }
diff --git a/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py 
b/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py
index ecbc12f..6ea9174 100755
--- a/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py
+++ b/systemvm/debian/opt/cloud/bin/cs/CsDhcp.py
@@ -16,6 +16,7 @@
 # under the License.
 import CsHelper
 import logging
+import os
 from netaddr import *
 from random import randint
 from CsGuestNetwork import CsGuestNetwork
@@ -46,8 +47,8 @@ class CsDhcp(CsDataBag):
         for item in self.dbag:
             if item == "id":
                 continue
-            self.add(self.dbag[item])
-        self.write_hosts()
+            if not self.dbag[item]['remove']:
+                self.add(self.dbag[item])
 
         self.configure_server()
 
@@ -64,6 +65,8 @@ class CsDhcp(CsDataBag):
         if restart_dnsmasq:
             self.delete_leases()
 
+        self.write_hosts()
+
         if not self.cl.is_redundant() or self.cl.is_master():
             if restart_dnsmasq:
                 CsHelper.service("dnsmasq", "restart")
@@ -114,10 +117,28 @@ class CsDhcp(CsDataBag):
             idx += 1
 
     def delete_leases(self):
+        macs_dhcphosts = []
+        interfaces = filter(lambda x: x.startswith('eth'), 
os.listdir('/sys/class/net'))
         try:
-            open(LEASES, 'w').close()
-        except IOError:
-            return
+            logging.info("Attempting to delete entries from dnsmasq.leases 
file for VMs which are not on dhcphosts file")
+            for host in open(DHCP_HOSTS):
+                macs_dhcphosts.append(host.split(',')[0])
+
+            removed = 0
+            for leaseline in open(LEASES):
+                lease = leaseline.split(' ')
+                mac = lease[1]
+                ip = lease[2]
+                if mac not in macs_dhcphosts:
+                    for interface in interfaces:
+                        cmd = "dhcp_release %s %s %s" % (interface, ip, mac)
+                        logging.info(cmd)
+                        CsHelper.execute(cmd)
+                    removed = removed + 1
+                    self.del_host(ip)
+            logging.info("Deleted %s entries from dnsmasq.leases file" % 
str(removed))
+        except Exception as e:
+            logging.error("Caught error while trying to delete entries from 
dnsmasq.leases file: %s" % e)
 
     def preseed(self):
         self.add_host("127.0.0.1", "localhost %s" % CsHelper.get_hostname())
@@ -170,3 +191,7 @@ class CsDhcp(CsDataBag):
 
     def add_host(self, ip, hosts):
         self.hosts[ip] = hosts
+
+    def del_host(self, ip):
+        if ip in self.hosts:
+            self.hosts.pop(ip)
diff --git a/systemvm/debian/opt/cloud/bin/cs_dhcp.py 
b/systemvm/debian/opt/cloud/bin/cs_dhcp.py
index 88b4b75..735f847 100755
--- a/systemvm/debian/opt/cloud/bin/cs_dhcp.py
+++ b/systemvm/debian/opt/cloud/bin/cs_dhcp.py
@@ -15,6 +15,7 @@
 # specific language governing permissions and limitations
 # under the License.
 
+import logging
 from netaddr import *
 
 
@@ -36,6 +37,9 @@ def merge(dbag, data):
                 remove_keys.add(key)
                 break
 
+        if data['remove'] and key not in remove_keys:
+            remove_keys.add(key)
+
         for remove_key in remove_keys:
             del(dbag[remove_key])
 

Reply via email to