Support IPv6 address in addIpToNic (#2773)

The admin will manually need to add the address to the Instance, but the
Security Grouping should allow it.
This commit is contained in:
Gabriel Beims Bräscher 2018-09-11 12:03:19 -03:00 committed by Rafael Weingärtner
parent d12c106a47
commit fbf488497f
27 changed files with 671 additions and 128 deletions

View File

@ -282,7 +282,7 @@ public interface NetworkModel {
boolean isNetworkInlineMode(Network network);
boolean isIP6AddressAvailableInNetwork(long networkId);
boolean areThereIPv6AddressAvailableInNetwork(long networkId);
boolean isIP6AddressAvailableInVlan(long vlanId);

View File

@ -34,6 +34,7 @@ import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network.IpAddresses;
import com.cloud.network.Network.Service;
import com.cloud.network.Networks.TrafficType;
import com.cloud.network.vpc.Vpc;
@ -155,15 +156,6 @@ public interface NetworkService {
List<? extends Network> getIsolatedNetworksWithSourceNATOwnedByAccountInZone(long zoneId, Account owner);
/**
* @param networkId
* @param entityId
* @return
* @throws ConcurrentOperationException
* @throws ResourceUnavailableException
* @throws ResourceAllocationException
* @throws InsufficientAddressCapacityException
*/
IpAddress associateIPToNetwork(long ipId, long networkId) throws InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException,
ConcurrentOperationException;
@ -189,12 +181,16 @@ public interface NetworkService {
String netmask, long networkOwnerId, Long vpcId, Boolean sourceNat, Long networkOfferingId) throws ResourceAllocationException, ConcurrentOperationException,
InsufficientCapacityException;
/* Requests an IP address for the guest nic */
NicSecondaryIp allocateSecondaryGuestIP(long nicId, String ipaddress) throws InsufficientAddressCapacityException;
/**
* Requests an IP address for the guest NIC
*/
NicSecondaryIp allocateSecondaryGuestIP(long nicId, IpAddresses requestedIpPair) throws InsufficientAddressCapacityException;
boolean releaseSecondaryIpFromNic(long ipAddressId);
/* lists the nic informaton */
/**
* lists the NIC information
*/
List<? extends Nic> listNics(ListNicsCmd listNicsCmd);
Map<Network.Capability, String> getNetworkOfferingServiceCapabilities(NetworkOffering offering, Service service);

View File

@ -29,7 +29,6 @@ import org.apache.cloudstack.context.CallContext;
import org.apache.log4j.Logger;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientAddressCapacityException;
@ -38,6 +37,7 @@ import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.Network;
import com.cloud.network.Network.IpAddresses;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.Nic;
import com.cloud.vm.NicSecondaryIp;
@ -78,20 +78,6 @@ public class AddIpToVmNicCmd extends BaseAsyncCreateCmd {
return nicId;
}
private String getIpaddress() {
if (ipAddr != null) {
return ipAddr;
} else {
return null;
}
}
private NetworkType getNetworkType() {
Network ntwk = _entityMgr.findById(Network.class, getNetworkId());
DataCenter dc = _entityMgr.findById(DataCenter.class, ntwk.getDataCenterId());
return dc.getNetworkType();
}
private boolean isZoneSGEnabled() {
Network ntwk = _entityMgr.findById(Network.class, getNetworkId());
DataCenter dc = _entityMgr.findById(DataCenter.class, ntwk.getDataCenterId());
@ -144,7 +130,6 @@ public class AddIpToVmNicCmd extends BaseAsyncCreateCmd {
}
}
@Override
public Long getSyncObjId() {
return getNetworkId();
@ -169,17 +154,15 @@ public class AddIpToVmNicCmd extends BaseAsyncCreateCmd {
@Override
public void create() throws ResourceAllocationException {
String ip;
NicSecondaryIp result;
String secondaryIp = null;
if ((ip = getIpaddress()) != null) {
if (!NetUtils.isValidIp4(ip)) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Invalid ip address " + ip);
}
IpAddresses requestedIpPair = new IpAddresses(ipAddr, null);
if (!NetUtils.isIpv4(ipAddr)) {
requestedIpPair = new IpAddresses(null, ipAddr);
}
try {
result = _networkService.allocateSecondaryGuestIP(getNicId(), getIpaddress());
result = _networkService.allocateSecondaryGuestIP(getNicId(), requestedIpPair);
if (result != null) {
setEntityId(result.getId());
setEntityUuid(result.getUuid());

View File

@ -147,10 +147,15 @@ public class RemoveIpFromVmNicCmd extends BaseAsyncCmd {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Invalid IP id is passed");
}
String secIp = nicSecIp.getIp4Address();
if (secIp == null) {
secIp = nicSecIp.getIp6Address();
}
if (isZoneSGEnabled()) {
//remove the security group rules for this secondary ip
boolean success = false;
success = _securityGroupService.securityGroupRulesForVmSecIp(nicSecIp.getNicId(), nicSecIp.getIp4Address(), false);
success = _securityGroupService.securityGroupRulesForVmSecIp(nicSecIp.getNicId(), secIp, false);
if (success == false) {
throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to set security group rules for the secondary ip");
}

View File

@ -61,7 +61,7 @@ public class AddIpToVmNicTest extends TestCase {
NicSecondaryIp secIp = Mockito.mock(NicSecondaryIp.class);
Mockito.when(
networkService.allocateSecondaryGuestIP(Matchers.anyLong(), Matchers.anyString()))
networkService.allocateSecondaryGuestIP(Matchers.anyLong(), Matchers.any()))
.thenReturn(secIp);
ipTonicCmd._networkService = networkService;
@ -81,7 +81,7 @@ public class AddIpToVmNicTest extends TestCase {
AddIpToVmNicCmd ipTonicCmd = Mockito.mock(AddIpToVmNicCmd.class);
Mockito.when(
networkService.allocateSecondaryGuestIP(Matchers.anyLong(), Matchers.anyString()))
networkService.allocateSecondaryGuestIP(Matchers.anyLong(), Matchers.any()))
.thenReturn(null);
ipTonicCmd._networkService = networkService;

View File

@ -166,18 +166,18 @@ public interface IpAddressManager {
* @throws ConcurrentOperationException
* @throws InsufficientAddressCapacityException
*/
PublicIp assignDedicateIpAddress(Account owner, Long guestNtwkId, Long vpcId, long dcId, boolean isSourceNat) throws ConcurrentOperationException,
InsufficientAddressCapacityException;
PublicIp assignDedicateIpAddress(Account owner, Long guestNtwkId, Long vpcId, long dcId, boolean isSourceNat)
throws ConcurrentOperationException, InsufficientAddressCapacityException;
IpAddress allocateIp(Account ipOwner, boolean isSystem, Account caller, long callerId, DataCenter zone, Boolean displayIp) throws ConcurrentOperationException,
ResourceAllocationException, InsufficientAddressCapacityException;
IpAddress allocateIp(Account ipOwner, boolean isSystem, Account caller, long callerId, DataCenter zone, Boolean displayIp)
throws ConcurrentOperationException, ResourceAllocationException, InsufficientAddressCapacityException;
PublicIp assignPublicIpAddressFromVlans(long dcId, Long podId, Account owner, VlanType type, List<Long> vlanDbIds, Long networkId, String requestedIp,
boolean isSystem) throws InsufficientAddressCapacityException;
PublicIp assignPublicIpAddressFromVlans(long dcId, Long podId, Account owner, VlanType type, List<Long> vlanDbIds, Long networkId, String requestedIp, boolean isSystem)
throws InsufficientAddressCapacityException;
@DB
void allocateNicValues(NicProfile nic, DataCenter dc, VirtualMachineProfile vm, Network network, String requestedIpv4,
String requestedIpv6) throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException;
void allocateNicValues(NicProfile nic, DataCenter dc, VirtualMachineProfile vm, Network network, String requestedIpv4, String requestedIpv6)
throws InsufficientVirtualNetworkCapacityException, InsufficientAddressCapacityException;
int getRuleCountForIp(Long addressId, FirewallRule.Purpose purpose, FirewallRule.State state);
@ -187,6 +187,8 @@ public interface IpAddressManager {
AcquirePodIpCmdResponse allocatePodIp(String zoneId, String podId) throws ConcurrentOperationException, ResourceAllocationException;
public boolean isIpEqualsGatewayOrNetworkOfferingsEmpty(Network network, String requestedIp);
void releasePodIp(Long id) throws CloudRuntimeException;
}

View File

@ -113,6 +113,7 @@ public class NicIpAliasDaoImpl extends GenericDaoBase<NicIpAliasVO, Long> implem
List<String> ips = new ArrayList<String>(results.size());
for (NicIpAliasVO result : results) {
ips.add(result.getIp4Address());
ips.add(result.getIp6Address());
}
return ips;
}

View File

@ -34,11 +34,7 @@ public interface NicSecondaryIpDao extends GenericDao<NicSecondaryIpVO, Long> {
NicSecondaryIpVO findByIp4AddressAndNetworkId(String ip4Address, long networkId);
/**
* @param networkId
* @param instanceId
* @return
*/
NicSecondaryIpVO findByIp6AddressAndNetworkId(String ip6Address, long networkId);
List<NicSecondaryIpVO> getSecondaryIpAddressesForVm(long vmId);

View File

@ -19,10 +19,9 @@ package com.cloud.vm.dao;
import java.util.ArrayList;
import java.util.List;
import com.cloud.utils.StringUtils;
import org.springframework.stereotype.Component;
import com.cloud.utils.StringUtils;
import com.cloud.utils.db.GenericDaoBase;
import com.cloud.utils.db.GenericSearchBuilder;
import com.cloud.utils.db.SearchBuilder;
@ -32,16 +31,18 @@ import com.cloud.utils.db.SearchCriteria.Op;
@Component
public class NicSecondaryIpDaoImpl extends GenericDaoBase<NicSecondaryIpVO, Long> implements NicSecondaryIpDao {
private final SearchBuilder<NicSecondaryIpVO> AllFieldsSearch;
private final GenericSearchBuilder<NicSecondaryIpVO, String> IpSearch;
protected GenericSearchBuilder<NicSecondaryIpVO, Long> CountByNicId;
protected NicSecondaryIpDaoImpl() {
public NicSecondaryIpDaoImpl() {
super();
AllFieldsSearch = createSearchBuilder();
AllFieldsSearch.and("instanceId", AllFieldsSearch.entity().getVmId(), Op.EQ);
AllFieldsSearch.and("network", AllFieldsSearch.entity().getNetworkId(), Op.EQ);
AllFieldsSearch.and("address", AllFieldsSearch.entity().getIp4Address(), Op.LIKE);
AllFieldsSearch.and("ip6address", AllFieldsSearch.entity().getIp6Address(), Op.LIKE);
AllFieldsSearch.and("nicId", AllFieldsSearch.entity().getNicId(), Op.EQ);
AllFieldsSearch.done();
@ -132,6 +133,14 @@ public class NicSecondaryIpDaoImpl extends GenericDaoBase<NicSecondaryIpVO, Long
return findOneBy(sc);
}
@Override
public NicSecondaryIpVO findByIp6AddressAndNetworkId(String ip6Address, long networkId) {
SearchCriteria<NicSecondaryIpVO> sc = AllFieldsSearch.create();
sc.setParameters("network", networkId);
sc.setParameters("ip6address", ip6Address);
return findOneBy(sc);
}
@Override
public NicSecondaryIpVO findByIp4AddressAndNicId(String ip4Address, long nicId) {
SearchCriteria<NicSecondaryIpVO> sc = AllFieldsSearch.create();

View File

@ -471,7 +471,7 @@ public class ApiResponseHelper implements ResponseGenerator {
cfgResponse.setCategory(cfg.getCategory());
cfgResponse.setDescription(cfg.getDescription());
cfgResponse.setName(cfg.getName());
if(cfg.isEncrypted()) {
if (cfg.isEncrypted()) {
cfgResponse.setValue(DBEncryptionUtil.encrypt(cfg.getValue()));
} else {
cfgResponse.setValue(cfg.getValue());
@ -3623,13 +3623,24 @@ public class ApiResponseHelper implements ResponseGenerator {
NicVO nic = _entityMgr.findById(NicVO.class, result.getNicId());
NetworkVO network = _entityMgr.findById(NetworkVO.class, result.getNetworkId());
response.setId(result.getUuid());
response.setIpAddr(result.getIp4Address());
setResponseIpAddress(result, response);
response.setNicId(nic.getUuid());
response.setNwId(network.getUuid());
response.setObjectName("nicsecondaryip");
return response;
}
/**
* Set the NicSecondaryIpResponse object with the IP address that is not null (IPv4 or IPv6)
*/
public static void setResponseIpAddress(NicSecondaryIp result, NicSecondaryIpResponse response) {
if (result.getIp4Address() != null) {
response.setIpAddr(result.getIp4Address());
} else if (result.getIp6Address() != null) {
response.setIpAddr(result.getIp6Address());
}
}
/**
* The resulting Response attempts to be in line with what is returned from
* @see com.cloud.api.query.dao.UserVmJoinDaoImpl#setUserVmResponse(ResponseView, UserVmResponse, UserVmJoinVO)
@ -3706,7 +3717,7 @@ public class ApiResponseHelper implements ResponseGenerator {
for (NicSecondaryIpVO ip : secondaryIps) {
NicSecondaryIpResponse ipRes = new NicSecondaryIpResponse();
ipRes.setId(ip.getUuid());
ipRes.setIpAddr(ip.getIp4Address());
setResponseIpAddress(ip, ipRes);
ipList.add(ipRes);
}
response.setSecondaryIps(ipList);

View File

@ -41,6 +41,7 @@ import org.apache.log4j.Logger;
import org.springframework.stereotype.Component;
import com.cloud.api.ApiDBUtils;
import com.cloud.api.ApiResponseHelper;
import com.cloud.api.query.vo.UserVmJoinVO;
import com.cloud.gpu.GPU;
import com.cloud.service.ServiceOfferingDetailsVO;
@ -267,15 +268,14 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
for (NicSecondaryIpVO ip : secondaryIps) {
NicSecondaryIpResponse ipRes = new NicSecondaryIpResponse();
ipRes.setId(ip.getUuid());
ipRes.setIpAddr(ip.getIp4Address());
ApiResponseHelper.setResponseIpAddress(ip, ipRes);
ipList.add(ipRes);
}
nicResponse.setSecondaryIps(ipList);
}
nicResponse.setObjectName("nic");
List<NicExtraDhcpOptionResponse> nicExtraDhcpOptionResponses = _nicExtraDhcpOptionDao.listByNicId(nic_id)
.stream()
List<NicExtraDhcpOptionResponse> nicExtraDhcpOptionResponses = _nicExtraDhcpOptionDao.listByNicId(nic_id).stream()
.map(vo -> new NicExtraDhcpOptionResponse(Dhcp.DhcpOptionCode.valueOfInt(vo.getCode()).getName(), vo.getCode(), vo.getValue()))
.collect(Collectors.toList());
nicResponse.setExtraDhcpOptions(nicExtraDhcpOptionResponses);
@ -400,7 +400,7 @@ public class UserVmJoinDaoImpl extends GenericDaoBaseWithTagInformation<UserVmJo
for (NicSecondaryIpVO ip : secondaryIps) {
NicSecondaryIpResponse ipRes = new NicSecondaryIpResponse();
ipRes.setId(ip.getUuid());
ipRes.setIpAddr(ip.getIp4Address());
ApiResponseHelper.setResponseIpAddress(ip, ipRes);
ipList.add(ipRes);
}
nicResponse.setSecondaryIps(ipList);

View File

@ -179,25 +179,25 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
private static final Logger s_logger = Logger.getLogger(IpAddressManagerImpl.class);
@Inject
NetworkOrchestrationService _networkMgr = null;
NetworkOrchestrationService _networkMgr;
@Inject
EntityManager _entityMgr = null;
EntityManager _entityMgr;
@Inject
DataCenterDao _dcDao = null;
DataCenterDao _dcDao;
@Inject
VlanDao _vlanDao = null;
VlanDao _vlanDao;
@Inject
IPAddressDao _ipAddressDao = null;
IPAddressDao _ipAddressDao;
@Inject
AccountDao _accountDao = null;
AccountDao _accountDao;
@Inject
DomainDao _domainDao = null;
DomainDao _domainDao;
@Inject
UserDao _userDao = null;
UserDao _userDao;
@Inject
ConfigurationDao _configDao;
@Inject
UserVmDao _userVmDao = null;
UserVmDao _userVmDao;
@Inject
AlertManager _alertMgr;
@Inject
@ -209,11 +209,11 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
@Inject
DomainVlanMapDao _domainVlanMapDao;
@Inject
NetworkOfferingDao _networkOfferingDao = null;
NetworkOfferingDao _networkOfferingDao;
@Inject
NetworkDao _networksDao = null;
NetworkDao _networksDao;
@Inject
NicDao _nicDao = null;
NicDao _nicDao;
@Inject
RulesManager _rulesMgr;
@Inject
@ -300,6 +300,8 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
Boolean.class, "system.vm.public.ip.reservation.mode.strictness", "false",
"If enabled, the use of System VMs public IP reservation is strict, preferred if not.", false, ConfigKey.Scope.Global);
private Random rand = new Random(System.currentTimeMillis());
@Override
public boolean configure(String name, Map<String, Object> params) {
// populate providers
@ -1840,11 +1842,9 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
return requestedIp;
}
return NetUtils.long2Ip(array[_rand.nextInt(array.length)]);
return NetUtils.long2Ip(array[rand.nextInt(array.length)]);
}
Random _rand = new Random(System.currentTimeMillis());
/**
* Get the list of public IPs that need to be applied for a static NAT enable/disable operation.
* Manipulating only these ips prevents concurrency issues when disabling static nat at the same time.
@ -2184,4 +2184,18 @@ public class IpAddressManagerImpl extends ManagerBase implements IpAddressManage
public ConfigKey<?>[] getConfigKeys() {
return new ConfigKey<?>[] {UseSystemPublicIps, RulesContinueOnError, SystemVmPublicIpReservationModeStrictness};
}
/**
* Returns true if the given IP address is equals the gateway or there is no network offerrings for the given network
*/
@Override
public boolean isIpEqualsGatewayOrNetworkOfferingsEmpty(Network network, String requestedIp) {
if (requestedIp.equals(network.getGateway()) || requestedIp.equals(network.getIp6Gateway())) {
return true;
}
if (_networkModel.listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty() && network.getCidr() == null) {
return true;
}
return false;
}
}

View File

@ -26,4 +26,11 @@ public interface Ipv6AddressManager extends Manager {
public UserIpv6Address assignDirectIp6Address(long dcId, Account owner, Long networkId, String requestedIp6) throws InsufficientAddressCapacityException;
public void revokeDirectIpv6Address(long networkId, String ip6Address);
public String allocateGuestIpv6(Network network, String requestedIpv6) throws InsufficientAddressCapacityException;
public String allocatePublicIp6ForGuestNic(Network network, Long podId, Account ipOwner, String requestedIp) throws InsufficientAddressCapacityException;
public String acquireGuestIpv6Address(Network network, String requestedIpv6) throws InsufficientAddressCapacityException;
}

View File

@ -23,9 +23,8 @@ import java.util.Map;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.log4j.Logger;
import com.cloud.configuration.Config;
import com.cloud.dc.DataCenter;
@ -35,13 +34,21 @@ import com.cloud.dc.VlanVO;
import com.cloud.dc.dao.DataCenterDao;
import com.cloud.dc.dao.VlanDao;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.network.IpAddress.State;
import com.cloud.network.Network.IpAddresses;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.NetworkDao;
import com.cloud.network.dao.UserIpv6AddressDao;
import com.cloud.user.Account;
import com.cloud.utils.NumbersUtil;
import com.cloud.utils.component.ManagerBase;
import com.cloud.utils.db.DB;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.dao.NicSecondaryIpDao;
import com.cloud.vm.dao.NicSecondaryIpVO;
public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressManager {
public static final Logger s_logger = Logger.getLogger(Ipv6AddressManagerImpl.class.getName());
@ -61,6 +68,12 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
NetworkDao _networkDao;
@Inject
ConfigurationDao _configDao;
@Inject
IpAddressManager ipAddressManager;
@Inject
NicSecondaryIpDao nicSecondaryIpDao;
@Inject
IPAddressDao ipAddressDao;
@Override
public boolean configure(String name, Map<String, Object> params) throws ConfigurationException {
@ -84,7 +97,7 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
String ip = null;
Vlan ipVlan = null;
if (requestedIp6 == null) {
if (!_networkModel.isIP6AddressAvailableInNetwork(networkId)) {
if (!_networkModel.areThereIPv6AddressAvailableInNetwork(networkId)) {
throw new InsufficientAddressCapacityException("There is no more address available in the network " + network.getName(), DataCenter.class,
network.getDataCenterId());
}
@ -132,7 +145,7 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
dc.setMacAddress(nextMac);
_dcDao.update(dc.getId(), dc);
String macAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(mac,NetworkModel.MACIdentifier.value()));
String macAddress = NetUtils.long2Mac(NetUtils.createSequenceBasedMacAddress(mac, NetworkModel.MACIdentifier.value()));
UserIpv6AddressVO ipVO = new UserIpv6AddressVO(ip, dcId, macAddress, ipVlan.getId());
ipVO.setPhysicalNetworkId(network.getPhysicalNetworkId());
ipVO.setSourceNetworkId(networkId);
@ -150,4 +163,101 @@ public class Ipv6AddressManagerImpl extends ManagerBase implements Ipv6AddressMa
_ipv6Dao.remove(ip.getId());
}
}
/**
* Executes method {@link #acquireGuestIpv6Address(Network, String)} and returns the requested IPv6 (String) in case of successfully allocating the guest IPv6 address.
*/
@Override
public String allocateGuestIpv6(Network network, String requestedIpv6) throws InsufficientAddressCapacityException {
return acquireGuestIpv6Address(network, requestedIpv6);
}
/**
* Allocates a guest IPv6 address for the guest NIC. It will throw exceptions in the following cases:
* <ul>
* <li>there is no IPv6 address available in the network;</li>
* <li>IPv6 address is equals to the Gateway;</li>
* <li>the network offering is empty;</li>
* <li>the IPv6 address is not in the network;</li>
* <li>the requested IPv6 address is already in use in the network.</li>
* </ul>
*/
@Override
@DB
public String acquireGuestIpv6Address(Network network, String requestedIpv6) throws InsufficientAddressCapacityException {
if (!_networkModel.areThereIPv6AddressAvailableInNetwork(network.getId())) {
throw new InsufficientAddressCapacityException(
String.format("There is no IPv6 address available in the network [name=%s, network id=%s]", network.getName(), network.getId()), DataCenter.class,
network.getDataCenterId());
}
checkIfCanAllocateIpv6Address(network, requestedIpv6);
IpAddresses requestedIpPair = new IpAddresses(null, requestedIpv6);
_networkModel.checkRequestedIpAddresses(network.getId(), requestedIpPair);
IPAddressVO ip = ipAddressDao.findByIpAndSourceNetworkId(network.getId(), requestedIpv6);
if (ip != null) {
State ipState = ip.getState();
if (ipState != State.Free) {
throw new InsufficientAddressCapacityException(String.format("Requested ip address [%s] is not free [ip state=%]", requestedIpv6, ipState), DataCenter.class,
network.getDataCenterId());
}
}
return requestedIpv6;
}
/**
* Allocates a public IPv6 address for the guest NIC. It will throw exceptions in the following cases:
* <ul>
* <li>the the requested IPv6 address is already in use in the network;</li>
* <li>IPv6 address is equals to the Gateway;</li>
* <li>the network offering is empty;</li>
* <li>the IPv6 address is not in the network.</li>
* </ul>
*/
@Override
public String allocatePublicIp6ForGuestNic(Network network, Long podId, Account owner, String requestedIpv6) throws InsufficientAddressCapacityException {
checkIfCanAllocateIpv6Address(network, requestedIpv6);
return requestedIpv6;
}
/**
* Performs some checks on the given IPv6 address. It will throw exceptions in the following cases:
* <ul>
* <li>the the requested IPv6 address is already in use in the network;</li>
* <li>IPv6 address is equals to the Gateway;</li>
* <li>the network offering is empty;</li>
* <li>the IPv6 address is not in the network.</li>
* </ul>
*/
protected void checkIfCanAllocateIpv6Address(Network network, String ipv6) throws InsufficientAddressCapacityException {
if (isIp6Taken(network, ipv6)) {
throw new InsufficientAddressCapacityException(
String.format("The IPv6 address [%s] is already in use in the network [id=%s, name=%s]", ipv6, network.getId(), network.getName()), Network.class,
network.getId());
}
if (ipAddressManager.isIpEqualsGatewayOrNetworkOfferingsEmpty(network, ipv6)) {
throw new InvalidParameterValueException(
String.format("The network [id=%s] offering is empty or the requested IP address [%s] is equals to the Gateway", network.getId(), ipv6));
}
String networkIp6Cidr = network.getIp6Cidr();
if (!NetUtils.isIp6InNetwork(ipv6, networkIp6Cidr)) {
throw new InvalidParameterValueException(
String.format("The IPv6 address [%s] is not in the network [id=%s, name=%s, ipv6cidr=%s]", ipv6, network.getId(), network.getName(), network.getIp6Cidr()));
}
}
/**
* Returns false if the requested ipv6 address is taken by some VM, checking on the 'user_ipv6_address' table or 'nic_secondary_ips' table.
*/
protected boolean isIp6Taken(Network network, String requestedIpv6) {
UserIpv6AddressVO ip6Vo = _ipv6Dao.findByNetworkIdAndIp(network.getId(), requestedIpv6);
NicSecondaryIpVO nicSecondaryIpVO = nicSecondaryIpDao.findByIp6AddressAndNetworkId(requestedIpv6, network.getId());
return ip6Vo != null || nicSecondaryIpVO != null;
}
}

View File

@ -592,7 +592,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
return false;
}
if (network.getIp6Gateway() != null) {
hasFreeIps = isIP6AddressAvailableInNetwork(network.getId());
hasFreeIps = areThereIPv6AddressAvailableInNetwork(network.getId());
}
} else {
if (network.getCidr() == null) {
@ -606,7 +606,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
}
@Override
public boolean isIP6AddressAvailableInNetwork(long networkId) {
public boolean areThereIPv6AddressAvailableInNetwork(long networkId) {
Network network = _networksDao.findById(networkId);
if (network == null) {
return false;

View File

@ -34,11 +34,10 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
import org.apache.log4j.Logger;
import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.api.ApiConstants;
@ -58,6 +57,7 @@ import org.apache.cloudstack.framework.config.dao.ConfigurationDao;
import org.apache.cloudstack.framework.messagebus.MessageBus;
import org.apache.cloudstack.framework.messagebus.PublishScope;
import org.apache.cloudstack.network.element.InternalLoadBalancerElementService;
import org.apache.log4j.Logger;
import com.cloud.api.ApiDBUtils;
import com.cloud.configuration.Config;
@ -91,6 +91,7 @@ import com.cloud.host.dao.HostDao;
import com.cloud.network.IpAddress.State;
import com.cloud.network.Network.Capability;
import com.cloud.network.Network.GuestType;
import com.cloud.network.Network.IpAddresses;
import com.cloud.network.Network.Provider;
import com.cloud.network.Network.Service;
import com.cloud.network.Networks.BroadcastDomainType;
@ -293,6 +294,8 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
@Inject
IpAddressManager _ipAddrMgr;
@Inject
Ipv6AddressManager ipv6AddrMgr;
@Inject
EntityManager _entityMgr;
@Inject
public SecurityGroupService _securityGroupService;
@ -642,13 +645,16 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
}
@Override
@ActionEvent(eventType = EventTypes.EVENT_NIC_SECONDARY_IP_CONFIGURE, eventDescription = "Configuring secondary ip " +
"rules", async = true)
@ActionEvent(eventType = EventTypes.EVENT_NIC_SECONDARY_IP_CONFIGURE, eventDescription = "Configuring secondary ip " + "rules", async = true)
public boolean configureNicSecondaryIp(NicSecondaryIp secIp, boolean isZoneSgEnabled) {
boolean success = false;
String secondaryIp = secIp.getIp4Address();
if (secIp.getIp4Address() == null) {
secondaryIp = secIp.getIp6Address();
}
if (isZoneSgEnabled) {
success = _securityGroupService.securityGroupRulesForVmSecIp(secIp.getNicId(), secIp.getIp4Address(), true);
success = _securityGroupService.securityGroupRulesForVmSecIp(secIp.getNicId(), secondaryIp, true);
s_logger.info("Associated ip address to NIC : " + secIp.getIp4Address());
} else {
success = true;
@ -656,11 +662,16 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
return success;
}
/**
* It allocates a secondary IP alias on the NIC. It can be either an Ipv4 or an Ipv6 or even both, according to the the given IpAddresses object.
*/
@Override
@ActionEvent(eventType = EventTypes.EVENT_NIC_SECONDARY_IP_ASSIGN, eventDescription = "assigning secondary ip to nic", create = true)
public NicSecondaryIp allocateSecondaryGuestIP(final long nicId, String requestedIp) throws InsufficientAddressCapacityException {
public NicSecondaryIp allocateSecondaryGuestIP(final long nicId, IpAddresses requestedIpPair) throws InsufficientAddressCapacityException {
Account caller = CallContext.current().getCallingAccount();
String ipv4Address = requestedIpPair.getIp4Address();
String ipv6Address = requestedIpPair.getIp6Address();
//check whether the nic belongs to user vm.
final NicVO nicVO = _nicDao.findById(nicId);
@ -690,18 +701,23 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
int maxAllowedIpsPerNic = NumbersUtil.parseInt(_configDao.getValue(Config.MaxNumberOfSecondaryIPsPerNIC.key()), 10);
Long nicWiseIpCount = _nicSecondaryIpDao.countByNicId(nicId);
if(nicWiseIpCount.intValue() >= maxAllowedIpsPerNic) {
s_logger.error("Maximum Number of Ips \"vm.network.nic.max.secondary.ipaddresses = \"" + maxAllowedIpsPerNic + " per Nic has been crossed for the nic " + nicId + ".");
if (nicWiseIpCount.intValue() >= maxAllowedIpsPerNic) {
s_logger.error("Maximum Number of Ips \"vm.network.nic.max.secondary.ipaddresses = \"" + maxAllowedIpsPerNic + " per Nic has been crossed for the nic " + nicId + ".");
throw new InsufficientAddressCapacityException("Maximum Number of Ips per Nic has been crossed.", Nic.class, nicId);
}
s_logger.debug("Calling the ip allocation ...");
String ipaddr = null;
String ip6addr = null;
//Isolated network can exist in Basic zone only, so no need to verify the zone type
if (network.getGuestType() == Network.GuestType.Isolated) {
try {
ipaddr = _ipAddrMgr.allocateGuestIP(network, requestedIp);
if (ipv4Address != null) {
ipaddr = _ipAddrMgr.allocateGuestIP(network, ipv4Address);
}
if (ipv6Address != null) {
ip6addr = ipv6AddrMgr.allocateGuestIpv6(network, ipv6Address);
}
} catch (InsufficientAddressCapacityException e) {
throw new InvalidParameterValueException("Allocating guest ip for nic failed");
}
@ -711,16 +727,21 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
DataCenter dc = _dcDao.findById(network.getDataCenterId());
if (dc.getNetworkType() == NetworkType.Basic) {
VMInstanceVO vmi = (VMInstanceVO)vm;
VMInstanceVO vmi = (VMInstanceVO)vm;
podId = vmi.getPodIdToDeployIn();
if (podId == null) {
if (podId == null) {
throw new InvalidParameterValueException("vm pod id is null in Basic zone; can't decide the range for ip allocation");
}
}
}
try {
ipaddr = _ipAddrMgr.allocatePublicIpForGuestNic(network, podId, ipOwner, requestedIp);
if (ipaddr == null) {
if (ipv4Address != null) {
ipaddr = _ipAddrMgr.allocatePublicIpForGuestNic(network, podId, ipOwner, ipv4Address);
}
if (ipv6Address != null) {
ip6addr = ipv6AddrMgr.allocatePublicIp6ForGuestNic(network, podId, ipOwner, ipv6Address);
}
if (ipaddr == null && ipv6Address == null) {
throw new InvalidParameterValueException("Allocating ip to guest nic " + nicId + " failed");
}
} catch (InsufficientAddressCapacityException e) {
@ -732,29 +753,30 @@ public class NetworkServiceImpl extends ManagerBase implements NetworkService {
return null;
}
if (ipaddr != null) {
if (ipaddr != null || ip6addr != null) {
// we got the ip addr so up the nics table and secodary ip
final String addrFinal = ipaddr;
final String ip4AddrFinal = ipaddr;
final String ip6AddrFinal = ip6addr;
long id = Transaction.execute(new TransactionCallback<Long>() {
@Override
public Long doInTransaction(TransactionStatus status) {
boolean nicSecondaryIpSet = nicVO.getSecondaryIp();
if (!nicSecondaryIpSet) {
nicVO.setSecondaryIp(true);
// commit when previously set ??
s_logger.debug("Setting nics table ...");
_nicDao.update(nicId, nicVO);
}
boolean nicSecondaryIpSet = nicVO.getSecondaryIp();
if (!nicSecondaryIpSet) {
nicVO.setSecondaryIp(true);
// commit when previously set ??
s_logger.debug("Setting nics table ...");
_nicDao.update(nicId, nicVO);
}
s_logger.debug("Setting nic_secondary_ip table ...");
s_logger.debug("Setting nic_secondary_ip table ...");
Long vmId = nicVO.getInstanceId();
NicSecondaryIpVO secondaryIpVO = new NicSecondaryIpVO(nicId, addrFinal, vmId, ipOwner.getId(), ipOwner.getDomainId(), networkId);
_nicSecondaryIpDao.persist(secondaryIpVO);
NicSecondaryIpVO secondaryIpVO = new NicSecondaryIpVO(nicId, ip4AddrFinal, ip6AddrFinal, vmId, ipOwner.getId(), ipOwner.getDomainId(), networkId);
_nicSecondaryIpDao.persist(secondaryIpVO);
return secondaryIpVO.getId();
}
});
return getNicSecondaryIp(id);
return getNicSecondaryIp(id);
} else {
return null;
}

View File

@ -45,6 +45,7 @@ import com.cloud.network.Network.Service;
import com.cloud.network.Network.State;
import com.cloud.network.NetworkModel;
import com.cloud.network.NetworkProfile;
import com.cloud.network.NetworkService;
import com.cloud.network.Networks.BroadcastDomainType;
import com.cloud.network.Networks.Mode;
import com.cloud.network.Networks.TrafficType;
@ -65,6 +66,7 @@ import com.cloud.utils.db.TransactionCallbackWithExceptionNoReturn;
import com.cloud.utils.db.TransactionStatus;
import com.cloud.utils.exception.CloudRuntimeException;
import com.cloud.utils.exception.ExceptionUtil;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.Nic;
import com.cloud.vm.Nic.ReservationStrategy;
import com.cloud.vm.NicProfile;
@ -74,6 +76,7 @@ import com.cloud.vm.VirtualMachine;
import com.cloud.vm.VirtualMachineProfile;
import com.cloud.vm.dao.NicDao;
import com.cloud.vm.dao.NicSecondaryIpDao;
import com.cloud.vm.dao.NicSecondaryIpVO;
public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
@ -105,6 +108,10 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
NetworkOfferingServiceMapDao _ntwkOfferingSrvcDao;
@Inject
PhysicalNetworkDao _physicalNetworkDao;
@Inject
private NetworkService networkService;
@Inject
private NicSecondaryIpDao nicSecondaryIpDao;
private static final TrafficType[] TrafficTypes = {TrafficType.Guest};
protected IsolationMethod[] _isolationMethods;
@ -338,9 +345,16 @@ public class DirectNetworkGuru extends AdapterBase implements NetworkGuru {
List<String> nicSecIps = null;
nicSecIps = _nicSecondaryIpDao.getSecondaryIpAddressesForNic(nic.getId());
for (String secIp : nicSecIps) {
IPAddressVO pubIp = _ipAddressDao.findByIpAndSourceNetworkId(nic.getNetworkId(), secIp);
_ipAddrMgr.markIpAsUnavailable(pubIp.getId());
_ipAddressDao.unassignIpAddress(pubIp.getId());
if (NetUtils.isValidIp4(secIp)) {
IPAddressVO pubIp = _ipAddressDao.findByIpAndSourceNetworkId(nic.getNetworkId(), secIp);
_ipAddrMgr.markIpAsUnavailable(pubIp.getId());
_ipAddressDao.unassignIpAddress(pubIp.getId());
} else {
NicSecondaryIpVO nicSecIp = nicSecondaryIpDao.findByIp6AddressAndNetworkId(secIp, nic.getNetworkId());
if (nicSecIp != null) {
networkService.releaseSecondaryIpFromNic(nicSecIp.getId());
}
}
}
}
});

View File

@ -19,6 +19,9 @@ package com.cloud.api;
import com.cloud.domain.DomainVO;
import com.cloud.usage.UsageVO;
import com.cloud.user.AccountVO;
import com.cloud.vm.NicSecondaryIp;
import org.apache.cloudstack.api.response.NicSecondaryIpResponse;
import org.apache.cloudstack.api.response.UsageRecordResponse;
import org.apache.cloudstack.usage.UsageService;
import org.junit.Before;
@ -37,6 +40,7 @@ import java.util.Date;
import java.util.TimeZone;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.when;
@ -109,4 +113,32 @@ public class ApiResponseHelperTest {
UsageRecordResponse MockResponse = helper.createUsageResponse(usage);
assertEquals("DomainName",MockResponse.getDomainName());
}
@Test
public void setResponseIpAddressTestIpv4() {
NicSecondaryIp result = Mockito.mock(NicSecondaryIp.class);
NicSecondaryIpResponse response = new NicSecondaryIpResponse();
setResult(result, "ipv4", "ipv6");
ApiResponseHelper.setResponseIpAddress(result, response);
assertTrue(response.getIpAddr().equals("ipv4"));
}
private void setResult(NicSecondaryIp result, String ipv4, String ipv6) {
when(result.getIp4Address()).thenReturn(ipv4);
when(result.getIp6Address()).thenReturn(ipv6);
}
@Test
public void setResponseIpAddressTestIpv6() {
NicSecondaryIp result = Mockito.mock(NicSecondaryIp.class);
NicSecondaryIpResponse response = new NicSecondaryIpResponse();
setResult(result, null, "ipv6");
ApiResponseHelper.setResponseIpAddress(result, response);
assertTrue(response.getIpAddr().equals("ipv6"));
}
}

View File

@ -17,27 +17,29 @@
package com.cloud.network;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import com.cloud.network.Network.Service;
import com.cloud.network.dao.IPAddressDao;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.rules.StaticNat;
import com.cloud.network.rules.StaticNatImpl;
import com.cloud.utils.net.Ip;
import static org.mockito.Mockito.when;
import java.util.Collections;
import java.util.List;
import static org.mockito.Mockito.anyLong;
import static org.mockito.Mockito.mock;
public class IpAddressManagerTest {
@Mock
@ -46,6 +48,9 @@ public class IpAddressManagerTest {
@InjectMocks
IpAddressManagerImpl _ipManager;
@InjectMocks
NetworkModelImpl networkModel = Mockito.spy(new NetworkModelImpl());
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
@ -68,4 +73,68 @@ public class IpAddressManagerTest {
IPAddressVO returnedVO = ips.get(0);
Assert.assertEquals(vo, returnedVO);
}
}
@Test
public void isIpEqualsGatewayOrNetworkOfferingsEmptyTestRequestedIpEqualsIp6Gateway() {
Network network = setTestIsIpEqualsGatewayOrNetworkOfferingsEmpty(0l, "gateway", "ip6Gateway", null, new ArrayList<Service>());
boolean result = _ipManager.isIpEqualsGatewayOrNetworkOfferingsEmpty(network, "ip6Gateway");
Mockito.verify(networkModel, Mockito.times(0)).listNetworkOfferingServices(Mockito.anyLong());
Assert.assertTrue(result);
}
@Test
public void isIpEqualsGatewayOrNetworkOfferingsEmptyTestRequestedIpEqualsGateway() {
Network network = setTestIsIpEqualsGatewayOrNetworkOfferingsEmpty(0l, "gateway", "ip6Gateway", null, new ArrayList<Service>());
boolean result = _ipManager.isIpEqualsGatewayOrNetworkOfferingsEmpty(network, "gateway");
Mockito.verify(networkModel, Mockito.times(0)).listNetworkOfferingServices(Mockito.anyLong());
Assert.assertTrue(result);
}
@Test
public void isIpEqualsGatewayOrNetworkOfferingsEmptyTestExpectFalseServicesNotEmpty() {
List<Service> services = new ArrayList<Service>();
Service serviceGateway = new Service("Gateway");
services.add(serviceGateway);
Network network = setTestIsIpEqualsGatewayOrNetworkOfferingsEmpty(0l, "gateway", "ip6Gateway", null, services);
boolean result = _ipManager.isIpEqualsGatewayOrNetworkOfferingsEmpty(network, "requestedIp");
Mockito.verify(networkModel).listNetworkOfferingServices(Mockito.anyLong());
Assert.assertFalse(result);
}
@Test
public void isIpEqualsGatewayOrNetworkOfferingsEmptyTestExpectFalseServicesCidrNotNull() {
Network network = setTestIsIpEqualsGatewayOrNetworkOfferingsEmpty(0l, "gateway", "ip6Gateway", "cidr", new ArrayList<Service>());
boolean result = _ipManager.isIpEqualsGatewayOrNetworkOfferingsEmpty(network, "requestedIp");
Mockito.verify(networkModel).listNetworkOfferingServices(Mockito.anyLong());
Assert.assertFalse(result);
}
@Test
public void isIpEqualsGatewayOrNetworkOfferingsEmptyTestNetworkOfferingsEmptyAndCidrNull() {
Network network = setTestIsIpEqualsGatewayOrNetworkOfferingsEmpty(0l, "gateway", "ip6Gateway", null, new ArrayList<Service>());
boolean result = _ipManager.isIpEqualsGatewayOrNetworkOfferingsEmpty(network, "requestedIp");
Mockito.verify(networkModel).listNetworkOfferingServices(Mockito.anyLong());
Assert.assertTrue(result);
}
private Network setTestIsIpEqualsGatewayOrNetworkOfferingsEmpty(long networkOfferingId, String gateway, String ip6Gateway, String cidr, List<Service> services) {
Network network = mock(Network.class);
Mockito.when(network.getNetworkOfferingId()).thenReturn(networkOfferingId);
Mockito.when(network.getGateway()).thenReturn(gateway);
Mockito.when(network.getIp6Gateway()).thenReturn(ip6Gateway);
Mockito.when(network.getCidr()).thenReturn(cidr);
Mockito.doReturn(services).when(networkModel).listNetworkOfferingServices(Mockito.anyLong());
return network;
}
}

View File

@ -0,0 +1,229 @@
// 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;
import static org.mockito.Mockito.mock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import com.cloud.exception.InsufficientAddressCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.network.IpAddress.State;
import com.cloud.network.Network.IpAddresses;
import com.cloud.network.dao.IPAddressDaoImpl;
import com.cloud.network.dao.IPAddressVO;
import com.cloud.network.dao.UserIpv6AddressDaoImpl;
import com.cloud.user.Account;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.dao.NicSecondaryIpDaoImpl;
import com.cloud.vm.dao.NicSecondaryIpVO;
public class Ipv6AddressManagerTest {
@InjectMocks
Ipv6AddressManagerImpl ip6Manager = Mockito.spy(new Ipv6AddressManagerImpl());
@InjectMocks
NicSecondaryIpDaoImpl nicSecondaryIpDao = Mockito.spy(new NicSecondaryIpDaoImpl());
@InjectMocks
UserIpv6AddressDaoImpl ipv6Dao = Mockito.spy(new UserIpv6AddressDaoImpl());
@InjectMocks
IpAddressManagerImpl ipAddressManager = Mockito.spy(new IpAddressManagerImpl());
@InjectMocks
NetworkModelImpl networkModel = Mockito.mock(NetworkModelImpl.class);// = Mockito.spy(new NetworkModelImpl());
@InjectMocks
IPAddressDaoImpl ipAddressDao = Mockito.spy(new IPAddressDaoImpl());
private Network network = mockNetwork();
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
}
@Test
public void isIp6TakenTestNoNull() {
setIsIp6TakenTest(new UserIpv6AddressVO(), new NicSecondaryIpVO(0l, "ipaddr", 0l, 0l, 0l, 0l));
boolean result = ip6Manager.isIp6Taken(network, "requestedIpv6");
assertAndVerifyIsIp6Taken(true, result);
}
@Test
public void isIp6TakenTestSecIpNull() {
setIsIp6TakenTest(new UserIpv6AddressVO(), null);
boolean result = ip6Manager.isIp6Taken(network, "requestedIpv6");
assertAndVerifyIsIp6Taken(true, result);
}
@Test
public void isIp6TakenTestUserIpv6AddressNull() {
setIsIp6TakenTest(null, new NicSecondaryIpVO(0l, "ipaddr", 0l, 0l, 0l, 0l));
boolean result = ip6Manager.isIp6Taken(network, "requestedIpv6");
assertAndVerifyIsIp6Taken(true, result);
}
@Test
public void isIp6TakenTestAllNull() {
setIsIp6TakenTest(null, null);
boolean result = ip6Manager.isIp6Taken(network, "requestedIpv6");
assertAndVerifyIsIp6Taken(false, result);
}
private void assertAndVerifyIsIp6Taken(boolean expected, boolean result) {
Assert.assertEquals(expected, result);
Mockito.verify(ipv6Dao).findByNetworkIdAndIp(Mockito.anyLong(), Mockito.anyString());
Mockito.verify(nicSecondaryIpDao).findByIp6AddressAndNetworkId(Mockito.anyString(), Mockito.anyLong());
}
private void setIsIp6TakenTest(UserIpv6AddressVO userIpv6, NicSecondaryIpVO nicSecondaryIp) {
Mockito.doReturn(userIpv6).when(ipv6Dao).findByNetworkIdAndIp(Mockito.anyLong(), Mockito.anyString());
Mockito.doReturn(nicSecondaryIp).when(nicSecondaryIpDao).findByIp6AddressAndNetworkId(Mockito.anyString(), Mockito.anyLong());
}
private Network mockNetwork() {
Network network = mock(Network.class);
Mockito.when(network.getId()).thenReturn(0l);
Mockito.when(network.getIp6Cidr()).thenReturn("2001:db8::/32");
return network;
}
@Test
public void allocatePublicIp6ForGuestNicTestNoException() throws InsufficientAddressCapacityException {
Account owner = Mockito.mock(Account.class);
String requestedIpv6 = setCheckIfCanAllocateIpv6AddresscTest("2001:db8::10", false, false);
String returnedIp = ip6Manager.allocatePublicIp6ForGuestNic(network, 0l, owner, requestedIpv6);
Mockito.verify(ip6Manager).checkIfCanAllocateIpv6Address(network, requestedIpv6);
Assert.assertEquals(requestedIpv6, returnedIp);
}
@Test(expected = InsufficientAddressCapacityException.class)
public void checkIfCanAllocateIpv6AddressTestIp6IsTaken() throws InsufficientAddressCapacityException {
String requestedIpv6 = setCheckIfCanAllocateIpv6AddresscTest("2001:db8::10", true, false);
ip6Manager.checkIfCanAllocateIpv6Address(network, requestedIpv6);
verifyCheckIfCanAllocateIpv6AddressTest(network, requestedIpv6, 1, 0);
}
@Test(expected = InvalidParameterValueException.class)
public void checkIfCanAllocateIpv6AddressTestIpIsIpEqualsGatewayOrNetworkOfferingsEmpty() throws InsufficientAddressCapacityException {
String requestedIpv6 = setCheckIfCanAllocateIpv6AddresscTest("2001:db8::10", false, true);
ip6Manager.checkIfCanAllocateIpv6Address(network, requestedIpv6);
verifyCheckIfCanAllocateIpv6AddressTest(network, requestedIpv6, 1, 1);
}
@Test(expected = InvalidParameterValueException.class)
public void checkIfCanAllocateIpv6AddressTestIpINotInTheNetwork() throws InsufficientAddressCapacityException {
String requestedIpv6 = "2002:db8::10";
setCheckIfCanAllocateIpv6AddresscTest(requestedIpv6, false, false);
ip6Manager.checkIfCanAllocateIpv6Address(network, requestedIpv6);
verifyCheckIfCanAllocateIpv6AddressTest(network, requestedIpv6, 1, 1);
}
private void verifyCheckIfCanAllocateIpv6AddressTest(Network network, String requestedIpv6, int isIp6TakenTimes, int isIpEqualsGatewayTimes) {
Mockito.verify(ip6Manager, Mockito.times(isIp6TakenTimes)).isIp6Taken(network, requestedIpv6);
Mockito.verify(ipAddressManager, Mockito.times(isIpEqualsGatewayTimes)).isIpEqualsGatewayOrNetworkOfferingsEmpty(network, requestedIpv6);
}
private String setCheckIfCanAllocateIpv6AddresscTest(String requestedIpv6, boolean isIp6Taken, boolean isIpEqualsGatewayOrNetworkOfferingsEmpty) {
Mockito.doReturn(isIp6Taken).when(ip6Manager).isIp6Taken(Mockito.eq(network), Mockito.anyString());
Mockito.doReturn(isIpEqualsGatewayOrNetworkOfferingsEmpty).when(ipAddressManager).isIpEqualsGatewayOrNetworkOfferingsEmpty(network, requestedIpv6);
NetUtils.isIp6InNetwork(requestedIpv6, network.getIp6Cidr());
return requestedIpv6;
}
@Test
public void acquireGuestIpv6AddressTest() throws InsufficientAddressCapacityException {
setAcquireGuestIpv6AddressTest(true, State.Free);
String requestedIpv6 = setCheckIfCanAllocateIpv6AddresscTest("2001:db8::10", false, false);
ip6Manager.acquireGuestIpv6Address(network, requestedIpv6);
verifyAcquireGuestIpv6AddressTest();
}
private void verifyAcquireGuestIpv6AddressTest() {
Mockito.verify(networkModel).areThereIPv6AddressAvailableInNetwork(Mockito.anyLong());
Mockito.verify(networkModel).checkRequestedIpAddresses(Mockito.anyLong(), Mockito.any(IpAddresses.class));
Mockito.verify(ipAddressDao).findByIpAndSourceNetworkId(Mockito.anyLong(), Mockito.anyString());
}
@Test(expected = InsufficientAddressCapacityException.class)
public void acquireGuestIpv6AddressTestUnavailableIp() throws InsufficientAddressCapacityException {
setAcquireGuestIpv6AddressTest(false, State.Free);
String requestedIpv6 = setCheckIfCanAllocateIpv6AddresscTest("2001:db8::10", false, false);
ip6Manager.acquireGuestIpv6Address(network, requestedIpv6);
verifyAcquireGuestIpv6AddressTest();
}
@Test(expected = InsufficientAddressCapacityException.class)
public void acquireGuestIpv6AddressTestStateAllocating() throws InsufficientAddressCapacityException {
setAcquireGuestIpv6AddressTest(false, State.Allocating);
String requestedIpv6 = setCheckIfCanAllocateIpv6AddresscTest("2001:db8::10", false, false);
ip6Manager.acquireGuestIpv6Address(network, requestedIpv6);
verifyAcquireGuestIpv6AddressTest();
}
@Test(expected = InsufficientAddressCapacityException.class)
public void acquireGuestIpv6AddressTestStateAllocated() throws InsufficientAddressCapacityException {
setAcquireGuestIpv6AddressTest(false, State.Allocated);
String requestedIpv6 = setCheckIfCanAllocateIpv6AddresscTest("2001:db8::10", false, false);
ip6Manager.acquireGuestIpv6Address(network, requestedIpv6);
verifyAcquireGuestIpv6AddressTest();
}
@Test(expected = InsufficientAddressCapacityException.class)
public void acquireGuestIpv6AddressTestStateReleasing() throws InsufficientAddressCapacityException {
setAcquireGuestIpv6AddressTest(false, State.Releasing);
String requestedIpv6 = setCheckIfCanAllocateIpv6AddresscTest("2001:db8::10", false, false);
ip6Manager.acquireGuestIpv6Address(network, requestedIpv6);
verifyAcquireGuestIpv6AddressTest();
}
private void setAcquireGuestIpv6AddressTest(boolean isIPAvailable, State state) {
mockNetwork();
IPAddressVO ipVo = Mockito.mock(IPAddressVO.class);
Mockito.doReturn(isIPAvailable).when(networkModel).areThereIPv6AddressAvailableInNetwork(Mockito.anyLong());
Mockito.doReturn(ipVo).when(ipAddressDao).findByIpAndSourceNetworkId(Mockito.anyLong(), Mockito.anyString());
Mockito.when(ipVo.getState()).thenReturn(state);
}
}

View File

@ -813,7 +813,7 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel {
}
@Override
public boolean isIP6AddressAvailableInNetwork(long networkId) {
public boolean areThereIPv6AddressAvailableInNetwork(long networkId) {
// TODO Auto-generated method stub
return false;
}

View File

@ -51,6 +51,7 @@ import com.cloud.exception.ResourceUnavailableException;
import com.cloud.network.GuestVlan;
import com.cloud.network.IpAddress;
import com.cloud.network.Network;
import com.cloud.network.Network.IpAddresses;
import com.cloud.network.Network.Provider;
import com.cloud.network.Network.Service;
import com.cloud.network.NetworkProfile;
@ -827,7 +828,7 @@ public class MockNetworkManagerImpl extends ManagerBase implements NetworkOrches
}
@Override
public NicSecondaryIp allocateSecondaryGuestIP(long nicId, String ipaddress) {
public NicSecondaryIp allocateSecondaryGuestIP(long nicId, IpAddresses requestedIpPair) {
// TODO Auto-generated method stub
return null;
}

View File

@ -829,7 +829,7 @@ public class MockNetworkModelImpl extends ManagerBase implements NetworkModel {
}
@Override
public boolean isIP6AddressAvailableInNetwork(long networkId) {
public boolean areThereIPv6AddressAvailableInNetwork(long networkId) {
// TODO Auto-generated method stub
return false;
}

View File

@ -503,6 +503,16 @@ $.validator.addMethod("ipv6", function(value, element) {
return this.optional(element) || /^((([0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}:[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){5}:([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){4}:([0-9A-Fa-f]{1,4}:){0,2}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){3}:([0-9A-Fa-f]{1,4}:){0,3}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){2}:([0-9A-Fa-f]{1,4}:){0,4}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){6}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(([0-9A-Fa-f]{1,4}:){0,5}:((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|(::([0-9A-Fa-f]{1,4}:){0,5}((\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b)\.){3}(\b((25[0-5])|(1\d{2})|(2[0-4]\d)|(\d{1,2}))\b))|([0-9A-Fa-f]{1,4}::([0-9A-Fa-f]{1,4}:){0,5}[0-9A-Fa-f]{1,4})|(::([0-9A-Fa-f]{1,4}:){0,6}[0-9A-Fa-f]{1,4})|(([0-9A-Fa-f]{1,4}:){1,7}:))$/i.test(value);
}, "Please enter a valid IP v6 address.");
$.validator.addMethod("ipv46address", function(value, element) {
if (this.optional(element) && value.length == 0)
return true;
if ($.validator.methods.ipv4.call(this, value, element) || $.validator.methods.ipv6.call(this, value, element))
return true;
return false;
}, "Please enter a valid IPv4/IPv6 address.");
$.validator.addMethod("lettersonly", function(value, element) {
return this.optional(element) || /^[a-z]+$/i.test(value);
}, "Letters only please");

View File

@ -1782,7 +1782,7 @@
label: 'label.ip.address',
validation: {
required: false,
ipv4: true
ipv46address: true
}
}
}

View File

@ -23,9 +23,9 @@ import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.InetAddress;
import java.net.Inet4Address;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.net.InterfaceAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
@ -1547,4 +1547,20 @@ public class NetUtils {
return EUI64Address(IPv6Network.LINK_LOCAL_NETWORK, macAddress);
}
/**
* Returns true if the given IP address is IPv4 or false if it is an IPv6. If it is an invalid IP address it throws an exception.
*/
public static boolean isIpv4(String ipAddr) {
boolean isIpv4 = true;
if (ipAddr != null) {
if (!NetUtils.isValidIp4(ipAddr)) {
isIpv4 = false;
}
if (!NetUtils.isValidIp6(ipAddr) && !isIpv4) {
throw new IllegalArgumentException("Invalid ip address " + ipAddr);
}
}
return isIpv4;
}
}

View File

@ -678,4 +678,20 @@ public class NetUtilsTest {
assertFalse(NetUtils.isValidPort(-1));
assertFalse(NetUtils.isValidPort(65536));
}
@Test
public void testIsIpv4() {
assertTrue(NetUtils.isIpv4("192.168.1.1"));
assertFalse(NetUtils.isIpv4("2a01:4f8:130:2192::2"));
}
@Test(expected = IllegalArgumentException.class)
public void testIsIpv4ExpectException() {
NetUtils.isIpv4("test");
}
@Test(expected = IllegalArgumentException.class)
public void testIsIpv4ExpectException2() {
NetUtils.isIpv4("2001:db8:300::/64");
}
}