CLOUDSTACK-10199: Support requesting a specific IPv4 address - #2595
Conversation
bdf1163 to
03acbd2
Compare
| List<VlanVO> vlanVoList = listVlansByNetworkId(networkId); | ||
| VlanVO vlanVo = null; | ||
| for (VlanVO vlan : vlanVoList) { | ||
| if (vlan.getRemoved() == null) { |
There was a problem hiding this comment.
listVlansByNetworkId won't return removed entry since it's using the normal DAO listBy().
Another question: why picking the first one when the result could return many?
There was a problem hiding this comment.
I have the same doubt. It looks like a logic that should be somewhere else. If the network can use multiple Vlan, then should not you look for the correct one (the one that is in the same CIDR) as the requested IPv4?
There was a problem hiding this comment.
Well noticed, thanks :)
| verifyAndAssert(null, null, null, nicProfile, 1, 0); | ||
| } | ||
|
|
||
| @Test//(expected = CloudRuntimeException.class) |
There was a problem hiding this comment.
typo: the comment //... can be removed
| if (ipVO == null) { | ||
| throw new CloudRuntimeException(String.format("Cannot find IPAddressVO for guest [IPv4 address='%s'] and [network id=%s]", requestedIpv4Address, network.getId())); | ||
| } | ||
| IPAddressVO lockedIpVO = _ipAddressDao.acquireInLockTable(ipVO.getId()); |
There was a problem hiding this comment.
lockedIpVO can be null (https://github.com/apache/cloudstack/blob/master/framework/db/src/main/java/com/cloud/utils/db/GenericDaoBase.java#L1049). Moreover, it would be better IMO to put the lock/release operation with a try/finally block to ensure to release the lock all the time (when it could get it).
There was a problem hiding this comment.
@marcaurele are you LGTM on this one?
Thanks for your reviews and hard work 👍
| } | ||
| IPAddressVO lockedIpVO = _ipAddressDao.acquireInLockTable(ipVO.getId()); | ||
| if (lockedIpVO.getState() != IPAddressVO.State.Free) { | ||
| String ipState = lockedIpVO.getState().toString(); |
There was a problem hiding this comment.
You don't need to have a variable holding the state, you can use lockedIpVo.getState() directly in the String.format() function, its value won't change after releasing the lock on the VO.
| String macAddress = _networkModel.getNextAvailableMacAddressInNetwork(network.getId()); | ||
| nicProfile.setMacAddress(macAddress); | ||
| } catch (InsufficientAddressCapacityException e) { | ||
| e.printStackTrace(); |
There was a problem hiding this comment.
Require a logger here and to throw an exception
| protected void configureNicProfileBasedOnRequestedIp(NicProfile requestedNicProfile, NicProfile nicProfile, Network network) { | ||
| String requestedIpv4Address = requestedNicProfile.getRequestedIPv4(); | ||
| if (requestedIpv4Address == null) { | ||
| s_logger.debug(String.format("The requested [IPv4 address=%s] is null", requestedIpv4Address)); |
There was a problem hiding this comment.
Could be simplified since requestedIpv4Address is always null to avoid parameter.
| protected void configureNicProfileBasedOnRequestedIp(NicProfile requestedNicProfile, NicProfile nicProfile, Network network) { | ||
| String requestedIpv4Address = requestedNicProfile.getRequestedIPv4(); | ||
| if (requestedIpv4Address == null) { | ||
| s_logger.debug(String.format("The requested [IPv4 address=%s] is null", requestedIpv4Address)); |
There was a problem hiding this comment.
If requestedIpv4Address is null, then it is null. I do not think that we need to log it then. I mean, the value of requestedIpv4Address
| */ | ||
| protected void configureNicProfileBasedOnRequestedIp(NicProfile requestedNicProfile, NicProfile nicProfile, Network network) { | ||
| String requestedIpv4Address = requestedNicProfile.getRequestedIPv4(); | ||
| if (requestedIpv4Address == null) { |
There was a problem hiding this comment.
You can extract lines 893-902 to a specific method that validates requestedIpv4Address
There was a problem hiding this comment.
@GabrielBrascher, @rafaelweingartner, I think the last if condition covers both null and blank check. (isValidIp4 method)
There was a problem hiding this comment.
Thanks, you are right @nitin-maharana; however, the null check should exist to return without an exception and consider that the user did not request a specific IPv4.
| requestedIpv4Address, network.getId())); | ||
| } | ||
|
|
||
| acquireLockAndCheckIfIpv4IsFree(network, requestedIpv4Address); |
There was a problem hiding this comment.
Should not you validate the vlanVo before trying to acquire the IP?
I mean, you are executing the validation on vlanVo afterwards.
There was a problem hiding this comment.
Thanks for pointing that @rafaelweingartner 👍
| List<VlanVO> vlanVoList = listVlansByNetworkId(networkId); | ||
| VlanVO vlanVo = null; | ||
| for (VlanVO vlan : vlanVoList) { | ||
| if (vlan.getRemoved() == null) { |
There was a problem hiding this comment.
I have the same doubt. It looks like a logic that should be somewhere else. If the network can use multiple Vlan, then should not you look for the correct one (the one that is in the same CIDR) as the requested IPv4?
| */ | ||
| protected void configureNicProfileBasedOnRequestedIp(NicProfile requestedNicProfile, NicProfile nicProfile, Network network) { | ||
| String requestedIpv4Address = requestedNicProfile.getRequestedIPv4(); | ||
| if (requestedIpv4Address == null) { |
There was a problem hiding this comment.
@GabrielBrascher, @rafaelweingartner, I think the last if condition covers both null and blank check. (isValidIp4 method)
|
In advanced zone, I can see the API allows acceptance of |
|
@rhtyd I am using basic network and KVM. It might be a problem only on the basic network then. |
2ebdb34 to
32bd12c
Compare
| throw new InvalidParameterValueException(String.format("The requested [IPv4 address='%s'] is not a valid IP address", requestedIpv4Address)); | ||
| } | ||
|
|
||
| VlanVO vlanVo = _vlanDao.findByNetworkIdAndIpv4Range(network.getId(), requestedIpv4Address); |
There was a problem hiding this comment.
This name findByNetworkIdAndIpv4Range does not seem to be adequate. It seems that you are going to look for a VlanVo by ipv4 range, which is not the case. You are looking for a VlanVo of a network that "supports" the specified IPv4. Therefore, something like findVlanByNetworkIdAndIpv4 seems better.
There was a problem hiding this comment.
Makes sense, I will change it. Thanks!
There was a problem hiding this comment.
convention says to leave out the vlan (we are already in the vlandao. so findByNetworkIdAndIpv4Range actually makes sense.
There was a problem hiding this comment.
I can update it to findByNetworkIdAndIpv4 then. What do you guys think of it?
| /** | ||
| * Acquires lock of in table "user_ip_address" and checks if the requested IPv4 address is Free. | ||
| */ | ||
| protected void acquireLockAndCheckIfIpv4IsFree(Network network, String requestedIpv4Address) { |
There was a problem hiding this comment.
These exceptions that you throw here, I think it might be better to throw an InvalidParameterValueException instead. These exception will happen when the user enters an "invalid/already used" IP, right?
There was a problem hiding this comment.
Yes, that makes sense. I will update it.
| try { | ||
| testOrchastrator.configureNicProfileBasedOnRequestedIp(requestedNicProfile, nicProfile, network); | ||
| } catch (InvalidParameterValueException e) { | ||
| assertEquals(exceptionAssert, e.getMessage()); |
There was a problem hiding this comment.
why not use the @Test with the expected parameter?
There was a problem hiding this comment.
I always use @test(expected=Exception); however, it is not working here now, I will check again what might be causing it.
| String[] ipRangeParts = ipRange.split("-"); | ||
| String startIP = ipRangeParts[0]; | ||
| String endIP = ipRangeParts[1]; | ||
| if (NetUtils.isIpInRange(ipv4Address, startIP, endIP)) { |
There was a problem hiding this comment.
What about using something similar to isIp6InNetwork?
I also get a feeling that this method you implemented is doing the same as isIpWithInCidrRange
There was a problem hiding this comment.
The problem is that I do not have a CIDR. The network CIDR (at least in the basic network) is null. I like the idea of isIp6InNetwork, but I will need to ignore CIDR at this stage and use only ipv4 range.
32bd12c to
4ac2b74
Compare
82e6a68 to
f8ebd6b
Compare
a63d506 to
5344e18
Compare
| /** | ||
| * Acquires lock of in table "user_ip_address" and checks if the requested IPv4 address is Free. | ||
| */ | ||
| protected void acquireLockAndCheckIfIpv4IsFree(Network network, String requestedIpv4Address) { |
There was a problem hiding this comment.
Will this check work for requested IP addresses in case of Advanced networking?
I believe if the network is of type Guest, then you won't be able to find an IPAddressVO.
So the method should only be called if the network is public or shared.
There was a problem hiding this comment.
@fmaximus cannot users define a guest IP? I mean, as long as the guest IP defined by the user is within the bounds of the guest network, it should be possible, right?
There was a problem hiding this comment.
@rafaelweingartner The issue is that when a user selects a guest IP, it won't be found by _ipAddressDao, because IPAddressVO is for public IP's, not for guest IP's.
There was a problem hiding this comment.
@fmaximus thanks for pointing it out and sorry for the delay. I did some testing in advanced network and confirmed that a few modifications in this PR will be necessary. When I finish them I will ping you.
981ed87 to
e138034
Compare
|
|
||
| DataCenterVO dcVo = _dcDao.findById(network.getDataCenterId()); | ||
| if (dcVo.getNetworkType() == NetworkType.Basic) { | ||
| configureNicProfileBasedOnRequestedIp(requested, profile, network); |
There was a problem hiding this comment.
@fmaximus I changed the execution flow; now this workflow will be executed just in case of a basic network.
After this change I could add IPv4 either in an advanced network, with a 'guest IP', and in a basic network with a 'public IP' (from the IPAddressVO in this case). Thanks again for pointing that issue.
|
@blueorangutan test |
|
@rhtyd a Trillian-Jenkins test job (centos7 mgmt + kvm-centos7) has been kicked to run smoke tests |
|
@rafaelweingartner I have checked and could not find any point indicating that the errors presented here are caused by my changes. |
e138034 to
723c054
Compare
|
@blueorangutan package |
|
@DaanHoogland a Jenkins job has been kicked to build packages. I'll keep you posted as I make progress. |
|
Packaging result: ✔centos6 ✔centos7 ✔debian. JID-2430 |
723c054 to
a38d7db
Compare
a38d7db to
6825df7
Compare
|
@blueorangutan package |
|
@GabrielBrascher a Jenkins job has been kicked to build packages. I'll keep you posted as I make progress. |
There was a problem hiding this comment.
@DaanHoogland @marcaurele @nitin-maharana @rafaelweingartner @fmaximus please note that I introduced a minor change. The updated code follows detailed in the comments.
There is no logic changes, just extracted and enhanced the code, allowing JUnit tests and proper documentation.
| lockedIpVO.setState(IPAddressVO.State.Allocated); | ||
| _ipAddressDao.update(lockedIpVO.getId(), lockedIpVO); | ||
| } finally { | ||
| _ipAddressDao.releaseFromLockTable(ipVO.getId()); |
There was a problem hiding this comment.
The finally block will release the lock in case of success or an exception.
| /** | ||
| * Validates the locked IP, throwing an exeption if the locked IP is null or the locked IP is not in 'Free' state. | ||
| */ | ||
| protected void validateLockedRequestedIp(IPAddressVO ipVO, IPAddressVO lockedIpVO) { |
There was a problem hiding this comment.
Created validateLockedRequestedIp to extract some verifications, documenting it and creating test cases. The logic stills the same. as the caller (method acquireLockAndCheckIfIpv4IsFree) does have a finally block that releases the lock, it is ok to throw the exceptions in this validation.
|
Packaging result: ✔centos6 ✔centos7 ✖debian. JID-2433 |
6825df7 to
b52eab6
Compare
|
@blueorangutan package |
|
@GabrielBrascher a Jenkins job has been kicked to build packages. I'll keep you posted as I make progress. |
|
Packaging result: ✔centos6 ✔centos7 ✖debian. JID-2434 |
|
Packaging result: ✔centos6 ✔centos7 ✔debian. JID-2435 |
This commit allows deploying VMs with a specific IPv4 address.
DirectPodBasedNetworkGuru does not support requesting a custom
IP-Address while creating a new NIC/Instance, throwing the following
error:
Error 530: Does not support custom ip allocation at this time:
NicProfile[0-0-null-null-null
Unknown macro: { "cserrorcode"}
Some use-cases prefer the ability to request the IPv4 address which the
Instance will get.
This implementation adds unit test cases to cover and it was manually
tested in Basic Networking. I can perform more tests if requested.
b52eab6 to
04f73b9
Compare
|
@DaanHoogland @nvazquez @rhtyd @borisstoyanov All ok after packaging 🥇 |
|
Sure @GabrielBrascher |
|
@nvazquez a Trillian-Jenkins matrix job (centos6 mgmt + xs71, centos7 mgmt + vmware65, centos7 mgmt + kvmcentos7) has been kicked to run smoke tests |
|
Trillian test result (tid-3197)
|
|
Trillian test result (tid-3196)
|
|
Trillian test result (tid-3198)
|
borisstoyanov
left a comment
There was a problem hiding this comment.
LGTM, based on marvin tests results
This pull request allows deploying VMs with a specific IPv4 address.
DirectPodBasedNetworkGuru does not support requesting a custom IP-Address while creating a new NIC/Instance, throwing the following error:
Some use-cases prefer the ability to request the IPv4 address which the Instance will get.
This implementation adds unit test cases to cover and it was manually tested in Basic Networking. I can perform more tests if requested.
Types of changes
GitHub Issue/PRs
Screenshots (if appropriate):
How Has This Been Tested?
Checklist:
Testing