From 2484527cae9e810203eea8850d493737fa2336a4 Mon Sep 17 00:00:00 2001 From: Richard Lawley Date: Mon, 3 Jun 2019 12:21:23 +0100 Subject: [PATCH 1/2] srx: Fix removing static NAT rules with Juniper SRX (#3310) Fixed the logic for deleting static NAT rules on a Juniper SRX device. Previously the private (trust) rule was not being removed. Fixes #3309 --- .../com/cloud/network/resource/JuniperSrxResource.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java b/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java index 20031e3227f..8ada819c7a3 100644 --- a/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java +++ b/plugins/network-elements/juniper-srx/src/com/cloud/network/resource/JuniperSrxResource.java @@ -2078,11 +2078,11 @@ public class JuniperSrxResource implements ServerResource { xml = replaceXmlValue(xml, "rule-set", _privateZone); xml = replaceXmlValue(xml, "from-zone", _privateZone); xml = replaceXmlValue(xml, "rule-name", ruleName_private); - } - if (!sendRequestAndCheckResponse(command, xml, "name", ruleName_private)) - { - throw new ExecutionException("Failed to delete trust static NAT rule from public IP " + publicIp + " to private IP " + privateIp); + if (!sendRequestAndCheckResponse(command, xml, "name", ruleName_private)) + { + throw new ExecutionException("Failed to delete trust static NAT rule from public IP " + publicIp + " to private IP " + privateIp); + } } return true; } @@ -3568,6 +3568,7 @@ public class JuniperSrxResource implements ServerResource { case CHECK_IF_EXISTS: case CHECK_IF_IN_USE: + case CHECK_PRIVATE_IF_EXISTS: assert (keyAndValue != null && keyAndValue.length == 2) : "If the SrxCommand is " + command + ", both a key and value must be specified."; key = keyAndValue[0]; From c9ce3e2344ab863dae09e63bf783331097a660cb Mon Sep 17 00:00:00 2001 From: Nicolas Vazquez Date: Mon, 3 Jun 2019 08:34:16 -0300 Subject: [PATCH 2/2] 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 --- .../network/element/DhcpServiceProvider.java | 2 ++ .../agent/api/routing/DhcpEntryCommand.java | 9 +++++ .../resource/virtualnetwork/VRScripts.java | 1 - .../VirtualRoutingResource.java | 2 +- .../facade/DhcpEntryConfigItem.java | 2 +- .../virtualnetwork/model/VmDhcpConfig.java | 14 +++++++- .../service/NetworkOrchestrationService.java | 4 +++ .../orchestration/NetworkOrchestrator.java | 28 +++++++++++++++ .../networkservice/BaremetalDhcpElement.java | 5 +++ .../management/ContrailElementImpl.java | 5 +++ .../network/element/NuageVspElement.java | 5 +++ .../network/element/VirtualRouterElement.java | 28 +++++++++++++++ .../network/router/CommandSetupHelper.java | 5 +-- .../cloud/network/rules/DhcpEntryRules.java | 10 ++++++ .../src/com/cloud/vm/UserVmManagerImpl.java | 15 +++++--- .../topology/AdvancedNetworkTopology.java | 15 ++++++++ .../topology/AdvancedNetworkVisitor.java | 3 +- .../topology/BasicNetworkTopology.java | 21 +++++++++++ .../network/topology/BasicNetworkVisitor.java | 3 +- .../network/topology/NetworkTopology.java | 2 ++ .../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 12830f8cec0..3f530c23d34 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 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 b94280641c0..7fb65fe15cf 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 838f0877918..bd108a9bc3b 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 13672186653..8dd0620134a 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 77d436d2f93..0710ecc1dcd 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 150b8cf5665..d9cb8b0b264 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 fa7601db27d..94a4259a689 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 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 9f4778e6d22..9cceee881e6 100644 --- a/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java +++ b/engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java @@ -2912,6 +2912,34 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra return true; } + /** + * Cleanup entry on VR file specified by type + */ + @Override + public void cleanupNicDhcpDnsEntry(Network network, VirtualMachineProfile vmProfile, NicProfile nicProfile) { + + final List 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 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 5696048b847..807babcb09f 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 62ca29c7e44..4771441e9e7 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 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 48c3d88d0d0..0f6bff9d522 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 @@ -541,6 +541,11 @@ public class NuageVspElement extends AdapterBase implements ConnectivityProvider return true; } + @Override + public boolean removeDhcpEntry(Network network, NicProfile nic, VirtualMachineProfile vmProfile) { + return false; + } + @Override public boolean applyStaticNats(Network config, List rules) throws ResourceUnavailableException { List vspStaticNatDetails = new ArrayList(); diff --git a/server/src/com/cloud/network/element/VirtualRouterElement.java b/server/src/com/cloud/network/element/VirtualRouterElement.java index 58d9343f633..11a553cb758 100644 --- a/server/src/com/cloud/network/element/VirtualRouterElement.java +++ b/server/src/com/cloud/network/element/VirtualRouterElement.java @@ -946,6 +946,34 @@ NetworkMigrationResponder, AggregatedCommandExecutor, RedundantResource, DnsServ return false; } + @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 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 diff --git a/server/src/com/cloud/network/router/CommandSetupHelper.java b/server/src/com/cloud/network/router/CommandSetupHelper.java index c8d85fe2644..fe231c3f8c6 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 c4a91f42686..530cf0aea87 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 8857fe91ee1..5e0bb1be2a7 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 nics = _nicDao.listByVmId(vm.getId()); - for (NicVO nic : nics) { - NetworkVO network = _networkDao.findById(nic.getNetworkId()); - if (network.getTrafficType() == TrafficType.Guest) { + final List 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 f456fcee177..f35f1425f81 100644 --- a/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java +++ b/server/src/org/apache/cloudstack/network/topology/AdvancedNetworkTopology.java @@ -172,6 +172,21 @@ public class AdvancedNetworkTopology extends BasicNetworkTopology { return applyRules(network, router, typeString, isPodLevelException, podId, failWhenDisconnect, new RuleApplierWrapper(dhcpRules)); } + @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(dhcpRules)); + } + @Override public boolean associatePublicIP(final Network network, final List 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 b5283dacfeb..c21b6d7e9ad 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 0869b2289ca..a7fbe317828 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(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 53ddebbc3de..35d846b9876 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 5190d5e2f5d..fa76f8375f9 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) 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 8cbf30cdc88..980b4402b19 100644 --- a/server/test/com/cloud/vpc/MockNetworkManagerImpl.java +++ b/server/test/com/cloud/vpc/MockNetworkManagerImpl.java @@ -924,6 +924,10 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches return false; } + @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 ecbc12f5a6f..6ea91742d78 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 88b4b7568c5..735f847dbb8 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])