config-drive: support user data on L2 networks (#2615)

Supporting ConfigDrive user data on L2 networks.
Add UI checkbox to create L2 network offering with config drive.

Signed-off-by: Rohit Yadav <rohit.yadav@shapeblue.com>
This commit is contained in:
Nicolas Vazquez 2018-05-09 13:03:11 -03:00 committed by Rohit Yadav
parent 253f7d7728
commit bd89760108
6 changed files with 85 additions and 5 deletions

View File

@ -2291,13 +2291,14 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced
&& ntwkOff.getTrafficType() == TrafficType.Guest
&& (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated
&& !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))
|| ntwkOff.getGuestType() == GuestType.L2 && !_networkModel.listNetworkOfferingServices(ntwkOff.getId()).isEmpty());
&& !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)));
if (cidr == null && ip6Cidr == null && cidrRequired) {
throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask are required when create network of" + " type " + Network.GuestType.Shared
+ " and network of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled");
}
checkL2OfferingServices(ntwkOff);
// No cidr can be specified in Basic zone
if (zone.getNetworkType() == NetworkType.Basic && cidr != null) {
throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic);
@ -2396,6 +2397,21 @@ public class NetworkOrchestrator extends ManagerBase implements NetworkOrchestra
return network;
}
/**
* Checks for L2 network offering services. Only 2 cases allowed:
* - No services
* - User Data service only, provided by ConfigDrive
* @param ntwkOff network offering
*/
protected void checkL2OfferingServices(NetworkOfferingVO ntwkOff) {
if (ntwkOff.getGuestType() == GuestType.L2 && !_networkModel.listNetworkOfferingServices(ntwkOff.getId()).isEmpty() &&
(!_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.UserData) ||
(_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.UserData) &&
_networkModel.listNetworkOfferingServices(ntwkOff.getId()).size() > 1))) {
throw new InvalidParameterValueException("For L2 networks, only UserData service is allowed");
}
}
@Override
@DB
public boolean shutdownNetwork(final long networkId, final ReservationContext context, final boolean cleanupElements) {

View File

@ -26,10 +26,15 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Arrays;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.offerings.NetworkOfferingVO;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.Matchers;
import com.cloud.network.Network;
@ -56,6 +61,7 @@ import junit.framework.TestCase;
/**
* NetworkManagerImpl implements NetworkManager.
*/
@RunWith(JUnit4.class)
public class NetworkOrchestratorTest extends TestCase {
static final Logger s_logger = Logger.getLogger(NetworkOrchestratorTest.class);
@ -65,6 +71,10 @@ public class NetworkOrchestratorTest extends TestCase {
String dhcpProvider = "VirtualRouter";
NetworkGuru guru = mock(NetworkGuru.class);
NetworkOfferingVO networkOffering = mock(NetworkOfferingVO.class);
private static final long networkOfferingId = 1l;
@Override
@Before
public void setUp() {
@ -90,6 +100,9 @@ public class NetworkOrchestratorTest extends TestCase {
List<NetworkGuru> networkGurus = new ArrayList<NetworkGuru>();
networkGurus.add(guru);
testOrchastrator.networkGurus = networkGurus;
when(networkOffering.getGuestType()).thenReturn(GuestType.L2);
when(networkOffering.getId()).thenReturn(networkOfferingId);
}
@Test
@ -159,4 +172,32 @@ public class NetworkOrchestratorTest extends TestCase {
verify(testOrchastrator._ntwkSrvcDao, never()).getProviderForServiceInNetwork(network.getId(), Service.Dhcp);
verify(testOrchastrator._networksDao, times(1)).findById(nic.getNetworkId());
}
@Test
public void testCheckL2OfferingServicesEmptyServices() {
when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(new ArrayList<>());
when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(false);
testOrchastrator.checkL2OfferingServices(networkOffering);
}
@Test
public void testCheckL2OfferingServicesUserDataOnly() {
when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.UserData));
when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(true);
testOrchastrator.checkL2OfferingServices(networkOffering);
}
@Test(expected = InvalidParameterValueException.class)
public void testCheckL2OfferingServicesMultipleServicesIncludingUserData() {
when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.UserData, Service.Dhcp));
when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(true);
testOrchastrator.checkL2OfferingServices(networkOffering);
}
@Test(expected = InvalidParameterValueException.class)
public void testCheckL2OfferingServicesMultipleServicesNotIncludingUserData() {
when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.Dns, Service.Dhcp));
when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(false);
testOrchastrator.checkL2OfferingServices(networkOffering);
}
}

View File

@ -580,7 +580,7 @@ public class NetworkModelImpl extends ManagerBase implements NetworkModel, Confi
if (network.getTrafficType() != TrafficType.Guest) {
return false;
}
if (listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
if (network.getGuestType() == GuestType.L2 || listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
return true; // do not check free IPs if there is no service in the network
}
boolean hasFreeIps = true;

View File

@ -200,7 +200,7 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur
if (userSpecified.getCidr() != null) {
network.setCidr(userSpecified.getCidr());
network.setGateway(userSpecified.getGateway());
} else if (offering.getGuestType() == GuestType.Shared || !_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty()) {
} else if (offering.getGuestType() != GuestType.L2 && (offering.getGuestType() == GuestType.Shared || !_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty())) {
final String guestNetworkCidr = dc.getGuestNetworkCidr();
if (guestNetworkCidr != null) {
final String[] cidrTuple = guestNetworkCidr.split("\\/");
@ -370,7 +370,7 @@ public abstract class GuestNetworkGuru extends AdapterBase implements NetworkGur
guestIp = network.getGateway();
} else {
guestIp = _ipAddrMgr.acquireGuestIpAddress(network, nic.getRequestedIPv4());
if (guestIp == null && !_networkModel.listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
if (guestIp == null && network.getGuestType() != GuestType.L2 && !_networkModel.listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
throw new InsufficientVirtualNetworkCapacityException("Unable to acquire Guest IP" + " address for network " + network, DataCenter.class,
dc.getId());
}

View File

@ -2404,8 +2404,10 @@
var $useVpc = args.$form.find('.form-item[rel=\"useVpc\"]');
var $useVpcCb = $useVpc.find("input[type=checkbox]");
var $supportedServices = args.$form.find('.form-item[rel=\"supportedServices\"]');
var $userDataL2 = args.$form.find('.form-item[rel=\"userDataL2\"]');
if ($guestTypeField.val() == 'Shared') { //Shared network offering
$useVpc.hide();
$userDataL2.hide();
$supportedServices.css('display', 'inline-block');
if ($useVpcCb.is(':checked')) { //if useVpc is checked,
$useVpcCb.removeAttr("checked"); //remove "checked" attribute in useVpc
@ -2413,9 +2415,11 @@
} else if ($guestTypeField.val() == 'Isolated') { //Isolated network offering
$useVpc.css('display', 'inline-block');
$supportedServices.css('display', 'inline-block');
$userDataL2.hide();
} else if ($guestTypeField.val() == 'L2') {
$useVpc.hide();
$supportedServices.hide();
$userDataL2.css('display', 'inline-block');
}
var $providers = $useVpcCb.closest('form').find('.dynamic-input select[name!="service.Connectivity.provider"]');
var $optionsOfProviders = $providers.find('option');
@ -2803,6 +2807,13 @@
isBoolean: true
},
userDataL2: {
label: 'label.user.data',
docID: 'helpL2UserData',
isBoolean: true,
isHidden: true
},
lbType: { //only shown when VPC is checked and LB service is checked
label: 'label.load.balancer.type',
isHidden: true,
@ -3384,6 +3395,14 @@
} else { //specifyVlan checkbox is unchecked
delete inputData.specifyVlan; //if specifyVlan checkbox is unchecked, do not pass specifyVlan parameter to API call since we need to keep API call's size as small as possible (p.s. specifyVlan is defaulted as false at server-side)
}
if (inputData['userDataL2'] == 'on') {
inputData['serviceProviderList[0].service'] = 'UserData';
inputData['serviceProviderList[0].provider'] = 'ConfigDrive';
inputData['supportedServices'] = 'UserData';
} else {
delete inputData.serviceProviderList;
}
}
if (inputData['forvpc'] == 'on') {

View File

@ -1351,5 +1351,9 @@ cloudStack.docs = {
},
helpSetReservationSystemVms: {
desc: 'If enabled, IP range reservation is set for SSVM & CPVM. Global setting "system.vm.public.ip.reservation.mode.strictness" is used to control whether reservation is strict or not (preferred)'
},
helpL2UserData: {
desc: 'Pass user and meta data to VMs (via ConfigDrive)',
externalLink: ''
}
};