This commit is contained in:
Shawn Edwards 2026-01-22 09:17:16 +01:00 committed by GitHub
commit 47d46a640b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 294 additions and 17 deletions

View File

@ -36,6 +36,7 @@ public class DhcpEntryCommand extends NetworkElementCommand {
private boolean isDefault; private boolean isDefault;
boolean executeInSequence = false; boolean executeInSequence = false;
boolean remove; boolean remove;
Long leaseTime;
public boolean isRemove() { public boolean isRemove() {
return remove; return remove;
@ -152,4 +153,12 @@ public class DhcpEntryCommand extends NetworkElementCommand {
public void setDefault(boolean isDefault) { public void setDefault(boolean isDefault) {
this.isDefault = isDefault; this.isDefault = isDefault;
} }
public Long getLeaseTime() {
return leaseTime;
}
public void setLeaseTime(Long leaseTime) {
this.leaseTime = leaseTime;
}
} }

View File

@ -35,7 +35,7 @@ public class DhcpEntryConfigItem extends AbstractConfigItemFacade {
final DhcpEntryCommand command = (DhcpEntryCommand) cmd; final DhcpEntryCommand command = (DhcpEntryCommand) cmd;
final VmDhcpConfig vmDhcpConfig = new VmDhcpConfig(command.getVmName(), command.getVmMac(), command.getVmIpAddress(), command.getVmIp6Address(), command.getDuid(), command.getDefaultDns(), final VmDhcpConfig vmDhcpConfig = new VmDhcpConfig(command.getVmName(), command.getVmMac(), command.getVmIpAddress(), command.getVmIp6Address(), command.getDuid(), command.getDefaultDns(),
command.getDefaultRouter(), command.getStaticRoutes(), command.isDefault(), command.isRemove()); command.getDefaultRouter(), command.getStaticRoutes(), command.isDefault(), command.isRemove(), command.getLeaseTime());
return generateConfigItems(vmDhcpConfig); return generateConfigItems(vmDhcpConfig);
} }

View File

@ -29,6 +29,7 @@ public class VmDhcpConfig extends ConfigBase {
private String defaultGateway; private String defaultGateway;
private String staticRoutes; private String staticRoutes;
private boolean defaultEntry; private boolean defaultEntry;
private Long leaseTime;
// Indicate if the entry should be removed when set to true // Indicate if the entry should be removed when set to true
private boolean remove; private boolean remove;
@ -39,6 +40,11 @@ public class VmDhcpConfig extends ConfigBase {
public VmDhcpConfig(String hostName, String macAddress, String ipv4Address, String ipv6Address, String ipv6Duid, String dnsAddresses, String defaultGateway, public VmDhcpConfig(String hostName, String macAddress, String ipv4Address, String ipv6Address, String ipv6Duid, String dnsAddresses, String defaultGateway,
String staticRoutes, boolean defaultEntry, boolean remove) { String staticRoutes, boolean defaultEntry, boolean remove) {
this(hostName, macAddress, ipv4Address, ipv6Address, ipv6Duid, dnsAddresses, defaultGateway, staticRoutes, defaultEntry, remove, null);
}
public VmDhcpConfig(String hostName, String macAddress, String ipv4Address, String ipv6Address, String ipv6Duid, String dnsAddresses, String defaultGateway,
String staticRoutes, boolean defaultEntry, boolean remove, Long leaseTime) {
super(VM_DHCP); super(VM_DHCP);
this.hostName = hostName; this.hostName = hostName;
this.macAddress = macAddress; this.macAddress = macAddress;
@ -50,6 +56,7 @@ public class VmDhcpConfig extends ConfigBase {
this.staticRoutes = staticRoutes; this.staticRoutes = staticRoutes;
this.defaultEntry = defaultEntry; this.defaultEntry = defaultEntry;
this.remove = remove; this.remove = remove;
this.leaseTime = leaseTime;
} }
public String getHostName() { public String getHostName() {
@ -132,4 +139,12 @@ public class VmDhcpConfig extends ConfigBase {
this.defaultEntry = defaultEntry; this.defaultEntry = defaultEntry;
} }
public Long getLeaseTime() {
return leaseTime;
}
public void setLeaseTime(Long leaseTime) {
this.leaseTime = leaseTime;
}
} }

View File

@ -91,6 +91,10 @@ public interface NetworkOrchestrationService {
ConfigKey<Integer> NetworkThrottlingRate = new ConfigKey<>("Network", Integer.class, NetworkThrottlingRateCK, "200", ConfigKey<Integer> NetworkThrottlingRate = new ConfigKey<>("Network", Integer.class, NetworkThrottlingRateCK, "200",
"Default data transfer rate in megabits per second allowed in network.", true, ConfigKey.Scope.Zone); "Default data transfer rate in megabits per second allowed in network.", true, ConfigKey.Scope.Zone);
ConfigKey<Integer> DhcpLeaseTimeout = new ConfigKey<>("Network", Integer.class, "dhcp.lease.timeout", "0",
"DHCP lease time in seconds for VMs. Use 0 for infinite lease time (default). A non-zero value sets the lease duration in seconds.",
true, ConfigKey.Scope.Zone, "0-");
ConfigKey<Boolean> PromiscuousMode = new ConfigKey<>("Advanced", Boolean.class, "network.promiscuous.mode", "false", ConfigKey<Boolean> PromiscuousMode = new ConfigKey<>("Advanced", Boolean.class, "network.promiscuous.mode", "false",
"Whether to allow or deny promiscuous mode on NICs for applicable network elements such as for vswitch/dvswitch portgroups.", true); "Whether to allow or deny promiscuous mode on NICs for applicable network elements such as for vswitch/dvswitch portgroups.", true);

View File

@ -17,18 +17,18 @@
# under the License. # under the License.
# Usage: dhcpd_edithosts.py mac ip hostname dns gateway nextserver # Usage: dhcpd_edithosts.py mac ip hostname dns gateway nextserver [leasetime]
import sys, os import sys, os
from os.path import exists from os.path import exists
from time import sleep from time import sleep
from os import remove from os import remove
usage = '''dhcpd_edithosts.py mac ip hostname dns gateway nextserver''' usage = '''dhcpd_edithosts.py mac ip hostname dns gateway nextserver [leasetime]'''
conf_path = "/etc/dhcpd.conf" conf_path = "/etc/dhcpd.conf"
file_lock = "/etc/dhcpd.conf_locked" file_lock = "/etc/dhcpd.conf_locked"
sleep_max = 20 sleep_max = 20
host_entry = 'host %s { hardware ethernet %s; fixed-address %s; option domain-name-servers %s; option domain-name "%s"; option routers %s; default-lease-time infinite; max-lease-time infinite; min-lease-time infinite; filename "pxelinux.0";}' host_entry = 'host %s { hardware ethernet %s; fixed-address %s; option domain-name-servers %s; option domain-name "%s"; option routers %s; default-lease-time %s; max-lease-time %s; min-lease-time %s; filename "pxelinux.0";}'
host_entry1 = 'host %s { hardware ethernet %s; fixed-address %s; option domain-name-servers %s; option domain-name "%s"; option routers %s; default-lease-time infinite; max-lease-time infinite; min-lease-time infinite; next-server %s; filename "pxelinux.0";}' host_entry1 = 'host %s { hardware ethernet %s; fixed-address %s; option domain-name-servers %s; option domain-name "%s"; option routers %s; default-lease-time %s; max-lease-time %s; min-lease-time %s; next-server %s; filename "pxelinux.0";}'
def lock(): def lock():
if exists(file_lock): if exists(file_lock):
count = 0 count = 0
@ -59,10 +59,14 @@ def unlock():
print "Cannot remove file lock at %s" % file_lock print "Cannot remove file lock at %s" % file_lock
return False return False
def insert_host_entry(mac, ip, hostname, dns, gateway, next_server): def insert_host_entry(mac, ip, hostname, dns, gateway, next_server, lease_time="infinite"):
if lock() == False: if lock() == False:
return 1 return 1
# Convert 0 to 'infinite' for lease time
if lease_time == "0":
lease_time = "infinite"
cmd = 'sed -i /"fixed-address %s"/d %s' % (ip, conf_path) cmd = 'sed -i /"fixed-address %s"/d %s' % (ip, conf_path)
ret = os.system(cmd) ret = os.system(cmd)
if ret != 0: if ret != 0:
@ -78,9 +82,9 @@ def insert_host_entry(mac, ip, hostname, dns, gateway, next_server):
return 1 return 1
if next_server != "null": if next_server != "null":
entry = host_entry1 % (hostname, mac, ip, dns, "cloudnine.internal", gateway, next_server) entry = host_entry1 % (hostname, mac, ip, dns, "cloudnine.internal", gateway, lease_time, lease_time, lease_time, next_server)
else: else:
entry = host_entry % (hostname, mac, ip, dns, "cloudnine.internal", gateway) entry = host_entry % (hostname, mac, ip, dns, "cloudnine.internal", gateway, lease_time, lease_time, lease_time)
cmd = '''echo '%s' >> %s''' % (entry, conf_path) cmd = '''echo '%s' >> %s''' % (entry, conf_path)
ret = os.system(cmd) ret = os.system(cmd)
if ret != 0: if ret != 0:
@ -111,6 +115,7 @@ if __name__ == "__main__":
dns = sys.argv[4] dns = sys.argv[4]
gateway = sys.argv[5] gateway = sys.argv[5]
next_server = sys.argv[6] next_server = sys.argv[6]
lease_time = sys.argv[7] if len(sys.argv) > 7 else "infinite"
if exists(conf_path) == False: if exists(conf_path) == False:
conf_path = "/etc/dhcp/dhcpd.conf" conf_path = "/etc/dhcp/dhcpd.conf"
@ -118,5 +123,5 @@ if __name__ == "__main__":
print "Cannot find dhcpd.conf" print "Cannot find dhcpd.conf"
sys.exit(1) sys.exit(1)
ret = insert_host_entry(mac, ip, hostname, dns, gateway, next_server) ret = insert_host_entry(mac, ip, hostname, dns, gateway, next_server, lease_time)
sys.exit(ret) sys.exit(ret)

View File

@ -21,6 +21,7 @@
# $1 : the mac address # $1 : the mac address
# $2 : the associated ip address # $2 : the associated ip address
# $3 : the hostname # $3 : the hostname
# $4 : the lease time (optional, defaults to 'infinite')
wait_for_dnsmasq () { wait_for_dnsmasq () {
local _pid=$(pidof dnsmasq) local _pid=$(pidof dnsmasq)
@ -41,11 +42,17 @@ no_dhcp_release=$?
[ ! -f /etc/dhcphosts.txt ] && touch /etc/dhcphosts.txt [ ! -f /etc/dhcphosts.txt ] && touch /etc/dhcphosts.txt
[ ! -f /var/lib/misc/dnsmasq.leases ] && touch /var/lib/misc/dnsmasq.leases [ ! -f /var/lib/misc/dnsmasq.leases ] && touch /var/lib/misc/dnsmasq.leases
# Set lease time, default to 'infinite', convert 0 to 'infinite'
lease_time=${4:-infinite}
if [ "$lease_time" = "0" ]; then
lease_time=infinite
fi
sed -i /$1/d /etc/dhcphosts.txt sed -i /$1/d /etc/dhcphosts.txt
sed -i /$2,/d /etc/dhcphosts.txt sed -i /$2,/d /etc/dhcphosts.txt
sed -i /$3,/d /etc/dhcphosts.txt sed -i /$3,/d /etc/dhcphosts.txt
echo "$1,$2,$3,infinite" >>/etc/dhcphosts.txt echo "$1,$2,$3,$lease_time" >>/etc/dhcphosts.txt
#release previous dhcp lease if present #release previous dhcp lease if present
if [ $no_dhcp_release -eq 0 ] if [ $no_dhcp_release -eq 0 ]

View File

@ -296,6 +296,10 @@ public class CommandSetupHelper {
dhcpCommand.setDefault(nic.isDefaultNic()); dhcpCommand.setDefault(nic.isDefaultNic());
dhcpCommand.setRemove(remove); dhcpCommand.setRemove(remove);
// Set DHCP lease timeout from zone-scoped config (0 = infinite)
Long leaseTime = (long) NetworkOrchestrationService.DhcpLeaseTimeout.valueIn(router.getDataCenterId());
dhcpCommand.setLeaseTime(leaseTime);
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId())); dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_IP, _routerControlHelper.getRouterControlIp(router.getId()));
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName()); dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_NAME, router.getInstanceName());
dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(nic.getNetworkId(), router.getId())); dhcpCommand.setAccessDetail(NetworkElementCommand.ROUTER_GUEST_IP, _routerControlHelper.getRouterIpInNetwork(nic.getNetworkId(), router.getId()));

View File

@ -46,6 +46,8 @@ import com.cloud.utils.net.Ip;
import com.cloud.vm.NicVO; import com.cloud.vm.NicVO;
import com.cloud.vm.VirtualMachine; import com.cloud.vm.VirtualMachine;
import com.cloud.vm.dao.NicDao; import com.cloud.vm.dao.NicDao;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.apache.cloudstack.network.BgpPeerVO; import org.apache.cloudstack.network.BgpPeerVO;
import org.apache.cloudstack.network.dao.BgpPeerDetailsDao; import org.apache.cloudstack.network.dao.BgpPeerDetailsDao;
import org.junit.Assert; import org.junit.Assert;
@ -271,4 +273,42 @@ public class CommandSetupHelperTest {
Assert.assertTrue(cmd instanceof SetBgpPeersCommand); Assert.assertTrue(cmd instanceof SetBgpPeersCommand);
Assert.assertEquals(4, ((SetBgpPeersCommand) cmd).getBpgPeers().length); Assert.assertEquals(4, ((SetBgpPeersCommand) cmd).getBpgPeers().length);
} }
@Test
public void testDhcpLeaseTimeoutDefaultValue() {
// Test that the default value is 0 (infinite)
Integer defaultValue = NetworkOrchestrationService.DhcpLeaseTimeout.value();
Assert.assertEquals("Default DHCP lease timeout should be 0 (infinite)", 0, defaultValue.intValue());
}
@Test
public void testDhcpLeaseTimeoutAcceptsZero() {
// Test that 0 value is accepted (infinite lease)
ConfigKey<Integer> configKey = NetworkOrchestrationService.DhcpLeaseTimeout;
Assert.assertNotNull("ConfigKey should not be null", configKey);
Assert.assertEquals("ConfigKey default should be 0", "0", configKey.defaultValue());
}
@Test
public void testDhcpLeaseTimeoutAcceptsPositiveValues() {
// Test that positive values are accepted
ConfigKey<Integer> configKey = NetworkOrchestrationService.DhcpLeaseTimeout;
Assert.assertNotNull("ConfigKey should not be null", configKey);
// Verify the config key exists and has expected default
Assert.assertEquals("ConfigKey default should be 0", "0", configKey.defaultValue());
}
@Test
public void testDhcpLeaseTimeoutHasZoneScope() {
// Test that the ConfigKey has Zone scope
ConfigKey<Integer> configKey = NetworkOrchestrationService.DhcpLeaseTimeout;
Assert.assertTrue("ConfigKey should have Zone scope", configKey.getScopes().contains(ConfigKey.Scope.Zone));
}
@Test
public void testDhcpLeaseTimeoutIsDynamic() {
// Test that the ConfigKey is dynamic (can be updated at runtime)
ConfigKey<Integer> configKey = NetworkOrchestrationService.DhcpLeaseTimeout;
Assert.assertTrue("ConfigKey should be dynamic", configKey.isDynamic());
}
} }

View File

@ -0,0 +1,184 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package com.cloud.network.router;
import com.cloud.agent.api.Command;
import com.cloud.agent.api.routing.DhcpEntryCommand;
import com.cloud.agent.manager.Commands;
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.network.NetworkModel;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.NetworkDetailsDao;
import com.cloud.offerings.dao.NetworkOfferingDao;
import com.cloud.offerings.dao.NetworkOfferingDetailsDao;
import com.cloud.vm.UserVmVO;
import com.cloud.vm.NicVO;
import com.cloud.vm.dao.NicDao;
import org.apache.cloudstack.engine.orchestration.service.NetworkOrchestrationService;
import org.apache.cloudstack.framework.config.ConfigKey;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.test.util.ReflectionTestUtils;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.Mockito.when;
/**
* Integration tests for DHCP lease timeout functionality.
* Tests the end-to-end flow from ConfigKey through DhcpEntryCommand creation.
*/
@RunWith(MockitoJUnitRunner.class)
public class DhcpLeaseTimeoutIntegrationTest {
@InjectMocks
protected CommandSetupHelper commandSetupHelper = new CommandSetupHelper();
@Mock
NicDao nicDao;
@Mock
NetworkDao networkDao;
@Mock
NetworkModel networkModel;
@Mock
NetworkOfferingDao networkOfferingDao;
@Mock
NetworkOfferingDetailsDao networkOfferingDetailsDao;
@Mock
NetworkDetailsDao networkDetailsDao;
@Mock
RouterControlHelper routerControlHelper;
@Mock
DataCenterDao dcDao;
private VirtualRouter mockRouter;
private UserVmVO mockVm;
private NicVO mockNic;
private DataCenterVO mockDc;
@Before
public void setUp() {
ReflectionTestUtils.setField(commandSetupHelper, "_nicDao", nicDao);
ReflectionTestUtils.setField(commandSetupHelper, "_networkDao", networkDao);
ReflectionTestUtils.setField(commandSetupHelper, "_networkModel", networkModel);
ReflectionTestUtils.setField(commandSetupHelper, "_networkOfferingDao", networkOfferingDao);
ReflectionTestUtils.setField(commandSetupHelper, "networkOfferingDetailsDao", networkOfferingDetailsDao);
ReflectionTestUtils.setField(commandSetupHelper, "networkDetailsDao", networkDetailsDao);
ReflectionTestUtils.setField(commandSetupHelper, "_routerControlHelper", routerControlHelper);
ReflectionTestUtils.setField(commandSetupHelper, "_dcDao", dcDao);
// Create common mocks
mockRouter = Mockito.mock(VirtualRouter.class);
mockVm = Mockito.mock(UserVmVO.class);
mockNic = Mockito.mock(NicVO.class);
mockDc = Mockito.mock(DataCenterVO.class);
// Setup default mock behaviors
when(mockRouter.getId()).thenReturn(100L);
when(mockRouter.getInstanceName()).thenReturn("r-100-VM");
when(mockRouter.getDataCenterId()).thenReturn(1L);
when(mockVm.getHostName()).thenReturn("test-vm");
when(mockNic.getMacAddress()).thenReturn("02:00:0a:0b:0c:0d");
when(mockNic.getIPv4Address()).thenReturn("10.1.1.10");
when(mockNic.getIPv6Address()).thenReturn(null);
when(mockNic.getNetworkId()).thenReturn(400L);
when(mockNic.isDefaultNic()).thenReturn(true);
when(dcDao.findById(anyLong())).thenReturn(mockDc);
when(mockDc.getNetworkType()).thenReturn(com.cloud.dc.DataCenter.NetworkType.Advanced);
when(routerControlHelper.getRouterControlIp(anyLong())).thenReturn("10.1.1.1");
when(routerControlHelper.getRouterIpInNetwork(anyLong(), anyLong())).thenReturn("10.1.1.1");
when(networkModel.getExecuteInSeqNtwkElmtCmd()).thenReturn(false);
}
@Test
public void testDhcpEntryCommandContainsLeaseTime() {
// Test that DhcpEntryCommand includes the lease time from ConfigKey
Commands cmds = new Commands(Command.OnError.Continue);
commandSetupHelper.createDhcpEntryCommand(mockRouter, mockVm, mockNic, false, cmds);
Assert.assertEquals("Should have one DHCP command", 1, cmds.size());
DhcpEntryCommand dhcpCmd = (DhcpEntryCommand) cmds.toCommands()[0];
Assert.assertNotNull("DHCP command should not be null", dhcpCmd);
Assert.assertNotNull("Lease time should not be null", dhcpCmd.getLeaseTime());
// Default value should be 0 (infinite)
Assert.assertEquals("Default lease time should be 0", Long.valueOf(0L), dhcpCmd.getLeaseTime());
}
@Test
public void testDhcpEntryCommandUsesZoneScopedValue() {
// Test that the command uses zone-scoped configuration
Long zoneId = mockRouter.getDataCenterId();
Integer expectedLeaseTime = NetworkOrchestrationService.DhcpLeaseTimeout.valueIn(zoneId);
Commands cmds = new Commands(Command.OnError.Continue);
commandSetupHelper.createDhcpEntryCommand(mockRouter, mockVm, mockNic, false, cmds);
DhcpEntryCommand dhcpCmd = (DhcpEntryCommand) cmds.toCommands()[0];
Assert.assertEquals("Lease time should match zone-scoped config",
expectedLeaseTime.longValue(), dhcpCmd.getLeaseTime().longValue());
}
@Test
public void testInfiniteLeaseWithZeroValue() {
// Test that 0 value represents infinite lease
ConfigKey<Integer> configKey = NetworkOrchestrationService.DhcpLeaseTimeout;
Assert.assertEquals("Default value should be 0 for infinite lease", "0", configKey.defaultValue());
Commands cmds = new Commands(Command.OnError.Continue);
commandSetupHelper.createDhcpEntryCommand(mockRouter, mockVm, mockNic, false, cmds);
DhcpEntryCommand dhcpCmd = (DhcpEntryCommand) cmds.toCommands()[0];
Assert.assertEquals("Lease time 0 represents infinite lease", Long.valueOf(0L), dhcpCmd.getLeaseTime());
}
@Test
public void testDhcpCommandForNonDefaultNic() {
// Test DHCP command creation for non-default NIC
when(mockNic.isDefaultNic()).thenReturn(false);
Commands cmds = new Commands(Command.OnError.Continue);
commandSetupHelper.createDhcpEntryCommand(mockRouter, mockVm, mockNic, false, cmds);
DhcpEntryCommand dhcpCmd = (DhcpEntryCommand) cmds.toCommands()[0];
Assert.assertNotNull("DHCP command should be created for non-default NIC", dhcpCmd);
Assert.assertNotNull("Lease time should be set even for non-default NIC", dhcpCmd.getLeaseTime());
Assert.assertFalse("Command should reflect non-default NIC", dhcpCmd.isDefault());
}
@Test
public void testDhcpCommandWithRemoveFlag() {
// Test DHCP command with remove flag set
Commands cmds = new Commands(Command.OnError.Continue);
commandSetupHelper.createDhcpEntryCommand(mockRouter, mockVm, mockNic, true, cmds);
DhcpEntryCommand dhcpCmd = (DhcpEntryCommand) cmds.toCommands()[0];
Assert.assertNotNull("DHCP command should be created even with remove flag", dhcpCmd);
Assert.assertTrue("Remove flag should be set", dhcpCmd.isRemove());
// Lease time should still be included even for removal
Assert.assertNotNull("Lease time should be present even for removal", dhcpCmd.getLeaseTime());
}
}

View File

@ -199,12 +199,14 @@ class CsDhcp(CsDataBag):
def add(self, entry): def add(self, entry):
self.add_host(entry['ipv4_address'], entry['host_name']) self.add_host(entry['ipv4_address'], entry['host_name'])
# Lease time set to "infinite" since we properly control all DHCP/DNS config via CloudStack. # Lease time is configurable via CloudStack global config dhcp.lease.timeout
# 0 = infinite (default), otherwise the value represents seconds
# Infinite time helps avoid some edge cases which could cause DHCPNAK being sent to VMs since # Infinite time helps avoid some edge cases which could cause DHCPNAK being sent to VMs since
# (RHEL) system lose routes when they receive DHCPNAK. # (RHEL) system lose routes when they receive DHCPNAK.
# When VM is expunged, its active lease and DHCP/DNS config is properly removed from related files in VR, # When VM is expunged, its active lease and DHCP/DNS config is properly removed from related files in VR,
# so the infinite duration of lease does not cause any issues or garbage. # so the infinite duration of lease does not cause any issues or garbage.
lease = 'infinite' lease_time = entry.get('lease_time', 0)
lease = 'infinite' if lease_time == 0 else str(lease_time)
if entry['default_entry']: if entry['default_entry']:
self.dhcp_hosts.add("%s,%s,%s,%s" % (entry['mac_address'], self.dhcp_hosts.add("%s,%s,%s,%s" % (entry['mac_address'],

View File

@ -21,7 +21,7 @@
# edithosts.sh -- edit the dhcphosts file on the routing domain # edithosts.sh -- edit the dhcphosts file on the routing domain
usage() { usage() {
printf "Usage: %s: -m <MAC address> -4 <IPv4 address> -6 <IPv6 address> -h <hostname> -d <default router> -n <name server address> -s <Routes> -u <DUID> [-N]\n" $(basename $0) >&2 printf "Usage: %s: -m <MAC address> -4 <IPv4 address> -6 <IPv6 address> -h <hostname> -d <default router> -n <name server address> -s <Routes> -u <DUID> -l <lease time> [-N]\n" $(basename $0) >&2
} }
mac= mac=
@ -33,8 +33,9 @@ dns=
routes= routes=
duid= duid=
nondefault= nondefault=
lease_time=infinite
while getopts 'm:4:h:d:n:s:6:u:N' OPTION while getopts 'm:4:h:d:n:s:6:u:l:N' OPTION
do do
case $OPTION in case $OPTION in
m) mac="$OPTARG" m) mac="$OPTARG"
@ -53,6 +54,8 @@ do
;; ;;
s) routes="$OPTARG" s) routes="$OPTARG"
;; ;;
l) lease_time="$OPTARG"
;;
N) nondefault=1 N) nondefault=1
;; ;;
?) usage ?) usage
@ -124,17 +127,21 @@ fi
sed -i /$host,/d $DHCP_HOSTS sed -i /$host,/d $DHCP_HOSTS
#put in the new entry #put in the new entry
# If lease_time is 0, use 'infinite', otherwise use the value
if [ "$lease_time" = "0" ]; then
lease_time=infinite
fi
if [ $ipv4 ] if [ $ipv4 ]
then then
echo "$mac,$ipv4,$host,infinite" >>$DHCP_HOSTS echo "$mac,$ipv4,$host,$lease_time" >>$DHCP_HOSTS
fi fi
if [ $ipv6 ] if [ $ipv6 ]
then then
if [ $nondefault ] if [ $nondefault ]
then then
echo "id:$duid,set:nondefault6,[$ipv6],$host,infinite" >>$DHCP_HOSTS echo "id:$duid,set:nondefault6,[$ipv6],$host,$lease_time" >>$DHCP_HOSTS
else else
echo "id:$duid,[$ipv6],$host,infinite" >>$DHCP_HOSTS echo "id:$duid,[$ipv6],$host,$lease_time" >>$DHCP_HOSTS
fi fi
fi fi