Add option to select DNS or VR IP as resolver on VPC creation

* Add API param and UI to select option

* Add column on vpc and pass the value on the databags for CsDhcp.py to fix accordingly
This commit is contained in:
Nicolas Vazquez 2025-01-28 17:21:17 -03:00 committed by Pearl Dsilva
parent e4e9e470ae
commit ac53e4b322
15 changed files with 86 additions and 17 deletions

View File

@ -105,4 +105,6 @@ public interface Vpc extends ControlledEntity, Identity, InternalIdentity {
String getIp6Dns1();
String getIp6Dns2();
boolean useRouterIpAsResolver();
}

View File

@ -48,17 +48,17 @@ public interface VpcService {
* @param vpcName
* @param displayText
* @param cidr
* @param networkDomain TODO
* @param networkDomain TODO
* @param ip4Dns1
* @param ip4Dns2
* @param displayVpc TODO
* @param displayVpc TODO
* @param useVrIpResolver
* @return
* @throws ResourceAllocationException TODO
*/
Vpc createVpc(long zoneId, long vpcOffId, long vpcOwnerId, String vpcName, String displayText, String cidr, String networkDomain,
String ip4Dns1, String ip4Dns2, String ip6Dns1, String ip6Dns2, Boolean displayVpc, Integer publicMtu, Integer cidrSize,
Long asNumber, List<Long> bgpPeerIds)
throws ResourceAllocationException;
Long asNumber, List<Long> bgpPeerIds, Boolean useVrIpResolver) throws ResourceAllocationException;
/**
* Persists VPC record in the database

View File

@ -548,6 +548,7 @@ public class ApiConstants {
public static final String USER_SECURITY_GROUP_LIST = "usersecuritygrouplist";
public static final String USER_SECRET_KEY = "usersecretkey";
public static final String USE_VIRTUAL_NETWORK = "usevirtualnetwork";
public static final String USE_VIRTUAL_ROUTER_IP_RESOLVER = "userouteripresolver";
public static final String UPDATE_IN_SEQUENCE = "updateinsequence";
public static final String VALUE = "value";
public static final String VIRTUAL_MACHINE_ID = "virtualmachineid";

View File

@ -16,6 +16,7 @@
// under the License.
package org.apache.cloudstack.api.command.user.vpc;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.cloudstack.acl.RoleType;
@ -125,6 +126,10 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd {
@Parameter(name=ApiConstants.AS_NUMBER, type=CommandType.LONG, since = "4.20.0", description="the AS Number of the VPC tiers")
private Long asNumber;
@Parameter(name=ApiConstants.USE_VIRTUAL_ROUTER_IP_RESOLVER, type=CommandType.BOOLEAN,
description="(optional) for NSX based VPCs: when set to true, use the VR IP as nameserver, otherwise use DNS1 and DNS2")
private Boolean useVrIpResolver;
// ///////////////////////////////////////////////////
// ///////////////// Accessors ///////////////////////
// ///////////////////////////////////////////////////
@ -205,6 +210,10 @@ public class CreateVPCCmd extends BaseAsyncCreateCmd implements UserCmd {
return asNumber;
}
public boolean getUseVrIpResolver() {
return BooleanUtils.toBoolean(useVrIpResolver);
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -105,6 +105,9 @@ public class VpcVO implements Vpc {
@Column(name = "ip6Dns2")
String ip6Dns2;
@Column(name = "use_router_ip_resolver")
boolean useRouterIpResolver = false;
@Transient
boolean rollingRestart = false;
@ -309,4 +312,13 @@ public class VpcVO implements Vpc {
public String getIp6Dns2() {
return ip6Dns2;
}
@Override
public boolean useRouterIpAsResolver() {
return useRouterIpResolver;
}
public void setUseRouterIpResolver(boolean useRouterIpResolver) {
this.useRouterIpResolver = useRouterIpResolver;
}
}

View File

@ -68,6 +68,9 @@ ALTER TABLE `cloud`.`kubernetes_cluster` ADD CONSTRAINT `fk_cluster__etcd_templa
-- Add for_cks column to the user_data table to represent CNI Configuration stored as userdata
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.user_data','for_cks', 'int(1) unsigned DEFAULT "0" COMMENT "if true, the userdata represent CNI configuration meant for CKS use only"');
-- Add use VR IP as resolver option on VPC
CALL `cloud`.`IDEMPOTENT_ADD_COLUMN`('cloud.vpc','use_router_ip_resolver', 'tinyint(1) DEFAULT 0 COMMENT "use router ip as resolver instead of dns options"');
-----------------------------------------------------------
-- END - CKS Enhancements
-----------------------------------------------------------

View File

@ -301,7 +301,7 @@ public class NetworkMigrationManagerImpl implements NetworkMigrationManager {
copyOfVpc = _vpcService.createVpc(vpc.getZoneId(), vpcOfferingId, vpc.getAccountId(), vpc.getName(),
vpc.getDisplayText(), vpc.getCidr(), vpc.getNetworkDomain(), vpc.getIp4Dns1(), vpc.getIp4Dns2(),
vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(), vpc.getPublicMtu(), null, null, null);
vpc.getIp6Dns1(), vpc.getIp6Dns2(), vpc.isDisplay(), vpc.getPublicMtu(), null, null, null, vpc.useRouterIpAsResolver());
copyOfVpcId = copyOfVpc.getId();
//on resume of migration the uuid will be swapped already. So the copy will have the value of the original vpcid.

View File

@ -2072,6 +2072,7 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
* service, we need to override the DHCP response to return DNS server
* rather than virtual router itself.
*/
boolean useRouterIpResolver = getUseRouterIpAsResolver(router);
if (dnsProvided || dhcpProvided) {
if (defaultDns1 != null) {
buf.append(" dns1=").append(defaultDns1);
@ -2093,6 +2094,9 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
if (useExtDns) {
buf.append(" useextdns=true");
}
if (useRouterIpResolver) {
buf.append(" userouteripresolver=true");
}
}
if (Boolean.TRUE.equals(ExposeDnsAndBootpServer.valueIn(dc.getId()))) {
@ -2132,6 +2136,18 @@ Configurable, StateListener<VirtualMachine.State, VirtualMachine.Event, VirtualM
return true;
}
private boolean getUseRouterIpAsResolver(DomainRouterVO router) {
if (router == null || router.getVpcId() == null) {
return false;
}
Vpc vpc = _vpcDao.findById(router.getVpcId());
if (vpc == null) {
s_logger.warn(String.format("Cannot find VPC with ID %s from router %s", router.getVpcId(), router.getName()));
return false;
}
return vpc.useRouterIpAsResolver();
}
/**
* @param routerLogrotateFrequency The string to be checked if matches with any acceptable values.
* Checks if the value in the global configuration is an acceptable value to be informed to the Virtual Router.

View File

@ -1145,7 +1145,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
@ActionEvent(eventType = EventTypes.EVENT_VPC_CREATE, eventDescription = "creating vpc", create = true)
public Vpc createVpc(final long zoneId, final long vpcOffId, final long vpcOwnerId, final String vpcName, final String displayText, final String cidr, String networkDomain,
final String ip4Dns1, final String ip4Dns2, final String ip6Dns1, final String ip6Dns2, final Boolean displayVpc, Integer publicMtu,
final Integer cidrSize, final Long asNumber, final List<Long> bgpPeerIds) throws ResourceAllocationException {
final Integer cidrSize, final Long asNumber, final List<Long> bgpPeerIds, Boolean useVrIpResolver) throws ResourceAllocationException {
final Account caller = CallContext.current().getCallingAccount();
final Account owner = _accountMgr.getAccount(vpcOwnerId);
@ -1247,6 +1247,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
vpcOff.isRedundantRouter(), ip4Dns1, ip4Dns2, ip6Dns1, ip6Dns2);
vpc.setPublicMtu(publicMtu);
vpc.setDisplay(Boolean.TRUE.equals(displayVpc));
vpc.setUseRouterIpResolver(Boolean.TRUE.equals(useVrIpResolver));
if (vpc.getCidr() == null && cidrSize != null) {
// Allocate a CIDR for VPC
@ -1305,7 +1306,7 @@ public class VpcManagerImpl extends ManagerBase implements VpcManager, VpcProvis
List<Long> bgpPeerIds = (cmd instanceof CreateVPCCmdByAdmin) ? ((CreateVPCCmdByAdmin)cmd).getBgpPeerIds() : null;
Vpc vpc = createVpc(cmd.getZoneId(), cmd.getVpcOffering(), cmd.getEntityOwnerId(), cmd.getVpcName(), cmd.getDisplayText(),
cmd.getCidr(), cmd.getNetworkDomain(), cmd.getIp4Dns1(), cmd.getIp4Dns2(), cmd.getIp6Dns1(),
cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu(), cmd.getCidrSize(), cmd.getAsNumber(), bgpPeerIds);
cmd.getIp6Dns2(), cmd.isDisplay(), cmd.getPublicMtu(), cmd.getCidrSize(), cmd.getAsNumber(), bgpPeerIds, cmd.getUseVrIpResolver());
String sourceNatIP = cmd.getSourceNatIP();
boolean forNsx = isVpcForNsx(vpc);

View File

@ -492,7 +492,7 @@ public class VpcManagerImplTest {
try {
doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc);
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain,
ip4Dns[0], null, null, null, true, 1500, null, null, null);
ip4Dns[0], null, null, null, true, 1500, null, null, null, false);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s", e.getMessage()));
}
@ -504,7 +504,7 @@ public class VpcManagerImplTest {
try {
doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc);
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain,
ip4Dns[0], ip4Dns[1], ip6Dns[0], null, true, 1500, null, null, null);
ip4Dns[0], ip4Dns[1], ip6Dns[0], null, true, 1500, null, null, null, false);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s", e.getMessage()));
}
@ -519,7 +519,7 @@ public class VpcManagerImplTest {
try {
doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc);
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain,
ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null, null);
ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null, null, false);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s", e.getMessage()));
}
@ -536,7 +536,7 @@ public class VpcManagerImplTest {
try {
doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc);
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, ip4Cidr, vpcDomain,
ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null, null);
ip4Dns[0], ip4Dns[1], null, null, true, 1500, null, null, null, false);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s", e.getMessage()));
}
@ -559,7 +559,7 @@ public class VpcManagerImplTest {
try {
doNothing().when(resourceLimitService).checkResourceLimit(account, Resource.ResourceType.vpc);
manager.createVpc(zoneId, vpcOfferingId, vpcOwnerId, vpcName, vpcName, null, vpcDomain,
ip4Dns[0], ip4Dns[1], null, null, true, 1500, 24, null, bgpPeerIds);
ip4Dns[0], ip4Dns[1], null, null, true, 1500, 24, null, bgpPeerIds, false);
} catch (ResourceAllocationException e) {
Assert.fail(String.format("failure with exception: %s", e.getMessage()));
}

View File

@ -114,6 +114,9 @@ class CsConfig(object):
def expose_dns(self):
return self.cmdline().idata().get('exposedns', 'false') == 'true'
def use_router_ip_as_resolver(self):
return self.cl.get_use_router_ip_as_resolver()
def get_dns(self):
conf = self.cmdline().idata()
dns = []
@ -123,9 +126,10 @@ class CsConfig(object):
else:
dns.append(self.address().get_guest_ip())
for name in ('dns1', 'dns2'):
if name in conf:
dns.append(conf[name])
if not 'userouteripresolver' in conf:
for name in ('dns1', 'dns2'):
if name in conf:
dns.append(conf[name])
return dns
def get_format(self):

View File

@ -176,6 +176,11 @@ class CsCmdLine(CsDataBag):
return self.idata()['useextdns']
return False
def get_use_router_ip_as_resolver(self):
if "userouteripresolver" in self.idata():
return self.idata()['userouteripresolver']
return False
def get_advert_int(self):
if 'advert_int' in self.idata():
return self.idata()['advert_int']

View File

@ -38,6 +38,9 @@ class CsGuestNetwork:
if not self.guest:
return self.config.get_dns()
if self.config.use_router_ip_as_resolver():
return [self.data['router_guest_ip']]
dns = []
if 'router_guest_gateway' in self.data and not self.config.use_extdns() and ('is_vr_guest_gateway' not in self.data or not self.data['is_vr_guest_gateway']):
dns.append(self.data['router_guest_gateway'])

View File

@ -2488,6 +2488,7 @@
"label.usagetypedescription": "Usage description",
"label.use.kubectl.access.cluster": "<code><b>kubectl</b></code> and <code><b>kubeconfig</b></code> file to access cluster",
"label.use.local.timezone": "Use local timezone",
"label.use.router.ip.resolver": "Use Virtual Router IP as resolver",
"label.used": "Used",
"label.usehttps": "Use HTTPS",
"label.usenewdiskoffering": "Replace disk offering?",

View File

@ -142,7 +142,15 @@
<div style="color: red" v-if="errorPublicMtu" v-html="errorPublicMtu"></div>
</a-form-item>
</div>
<a-row :gutter="12" v-if="selectedVpcOfferingSupportsDns">
<div v-if="isNsxNetwork">
<a-form-item name="userouteripresolver" ref="userouteripresolver">
<template #label>
<tooltip-label :title="$t('label.use.router.ip.resolver')" :tooltip="apiParams.userouteripresolver.description"/>
</template>
<a-switch v-model:checked="useRouterIpResolver" />
</a-form-item>
</div>
<a-row :gutter="12" v-if="selectedVpcOfferingSupportsDns && !useRouterIpResolver">
<a-col :md="12" :lg="12">
<a-form-item v-if="'dns1' in apiParams" name="dns1" ref="dns1">
<template #label>
@ -240,7 +248,8 @@ export default {
isNsxNetwork: false,
asNumberLoading: false,
asNumbersZone: [],
selectedAsNumber: 0
selectedAsNumber: 0,
useRouterIpResolver: false
}
},
beforeCreate () {
@ -459,6 +468,9 @@ export default {
if ('asnumber' in values && this.isASNumberRequired()) {
params.asnumber = values.asnumber
}
if (this.useRouterIpResolver) {
params.userouteripresolver = true
}
this.loading = true
const title = this.$t('label.add.vpc')
const description = this.$t('message.success.add.vpc')