Skip to content

Commit bd89760

Browse files
nvazquezyadvr
authored andcommitted
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>
1 parent 253f7d7 commit bd89760

6 files changed

Lines changed: 85 additions & 5 deletions

File tree

engine/orchestration/src/org/apache/cloudstack/engine/orchestration/NetworkOrchestrator.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2291,13 +2291,14 @@ public Network createGuestNetwork(final long networkOfferingId, final String nam
22912291
final boolean cidrRequired = zone.getNetworkType() == NetworkType.Advanced
22922292
&& ntwkOff.getTrafficType() == TrafficType.Guest
22932293
&& (ntwkOff.getGuestType() == GuestType.Shared || (ntwkOff.getGuestType() == GuestType.Isolated
2294-
&& !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat))
2295-
|| ntwkOff.getGuestType() == GuestType.L2 && !_networkModel.listNetworkOfferingServices(ntwkOff.getId()).isEmpty());
2294+
&& !_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.SourceNat)));
22962295
if (cidr == null && ip6Cidr == null && cidrRequired) {
22972296
throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask are required when create network of" + " type " + Network.GuestType.Shared
22982297
+ " and network of type " + GuestType.Isolated + " with service " + Service.SourceNat.getName() + " disabled");
22992298
}
23002299

2300+
checkL2OfferingServices(ntwkOff);
2301+
23012302
// No cidr can be specified in Basic zone
23022303
if (zone.getNetworkType() == NetworkType.Basic && cidr != null) {
23032304
throw new InvalidParameterValueException("StartIp/endIp/gateway/netmask can't be specified for zone of type " + NetworkType.Basic);
@@ -2396,6 +2397,21 @@ public Network doInTransaction(final TransactionStatus status) {
23962397
return network;
23972398
}
23982399

2400+
/**
2401+
* Checks for L2 network offering services. Only 2 cases allowed:
2402+
* - No services
2403+
* - User Data service only, provided by ConfigDrive
2404+
* @param ntwkOff network offering
2405+
*/
2406+
protected void checkL2OfferingServices(NetworkOfferingVO ntwkOff) {
2407+
if (ntwkOff.getGuestType() == GuestType.L2 && !_networkModel.listNetworkOfferingServices(ntwkOff.getId()).isEmpty() &&
2408+
(!_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.UserData) ||
2409+
(_networkModel.areServicesSupportedByNetworkOffering(ntwkOff.getId(), Service.UserData) &&
2410+
_networkModel.listNetworkOfferingServices(ntwkOff.getId()).size() > 1))) {
2411+
throw new InvalidParameterValueException("For L2 networks, only UserData service is allowed");
2412+
}
2413+
}
2414+
23992415
@Override
24002416
@DB
24012417
public boolean shutdownNetwork(final long networkId, final ReservationContext context, final boolean cleanupElements) {

engine/orchestration/test/org/apache/cloudstack/engine/orchestration/NetworkOrchestratorTest.java

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,15 @@
2626
import java.util.HashMap;
2727
import java.util.List;
2828
import java.util.Map;
29+
import java.util.Arrays;
2930

31+
import com.cloud.exception.InvalidParameterValueException;
32+
import com.cloud.offerings.NetworkOfferingVO;
3033
import org.apache.log4j.Logger;
3134
import org.junit.Before;
3235
import org.junit.Test;
36+
import org.junit.runner.RunWith;
37+
import org.junit.runners.JUnit4;
3338
import org.mockito.Matchers;
3439

3540
import com.cloud.network.Network;
@@ -56,6 +61,7 @@
5661
/**
5762
* NetworkManagerImpl implements NetworkManager.
5863
*/
64+
@RunWith(JUnit4.class)
5965
public class NetworkOrchestratorTest extends TestCase {
6066
static final Logger s_logger = Logger.getLogger(NetworkOrchestratorTest.class);
6167

@@ -65,6 +71,10 @@ public class NetworkOrchestratorTest extends TestCase {
6571
String dhcpProvider = "VirtualRouter";
6672
NetworkGuru guru = mock(NetworkGuru.class);
6773

74+
NetworkOfferingVO networkOffering = mock(NetworkOfferingVO.class);
75+
76+
private static final long networkOfferingId = 1l;
77+
6878
@Override
6979
@Before
7080
public void setUp() {
@@ -90,6 +100,9 @@ public void setUp() {
90100
List<NetworkGuru> networkGurus = new ArrayList<NetworkGuru>();
91101
networkGurus.add(guru);
92102
testOrchastrator.networkGurus = networkGurus;
103+
104+
when(networkOffering.getGuestType()).thenReturn(GuestType.L2);
105+
when(networkOffering.getId()).thenReturn(networkOfferingId);
93106
}
94107

95108
@Test
@@ -159,4 +172,32 @@ public void testDontRemoveDhcpServiceWhenNotProvided() {
159172
verify(testOrchastrator._ntwkSrvcDao, never()).getProviderForServiceInNetwork(network.getId(), Service.Dhcp);
160173
verify(testOrchastrator._networksDao, times(1)).findById(nic.getNetworkId());
161174
}
175+
176+
@Test
177+
public void testCheckL2OfferingServicesEmptyServices() {
178+
when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(new ArrayList<>());
179+
when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(false);
180+
testOrchastrator.checkL2OfferingServices(networkOffering);
181+
}
182+
183+
@Test
184+
public void testCheckL2OfferingServicesUserDataOnly() {
185+
when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.UserData));
186+
when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(true);
187+
testOrchastrator.checkL2OfferingServices(networkOffering);
188+
}
189+
190+
@Test(expected = InvalidParameterValueException.class)
191+
public void testCheckL2OfferingServicesMultipleServicesIncludingUserData() {
192+
when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.UserData, Service.Dhcp));
193+
when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(true);
194+
testOrchastrator.checkL2OfferingServices(networkOffering);
195+
}
196+
197+
@Test(expected = InvalidParameterValueException.class)
198+
public void testCheckL2OfferingServicesMultipleServicesNotIncludingUserData() {
199+
when(testOrchastrator._networkModel.listNetworkOfferingServices(networkOfferingId)).thenReturn(Arrays.asList(Service.Dns, Service.Dhcp));
200+
when(testOrchastrator._networkModel.areServicesSupportedByNetworkOffering(networkOfferingId, Service.UserData)).thenReturn(false);
201+
testOrchastrator.checkL2OfferingServices(networkOffering);
202+
}
162203
}

server/src/com/cloud/network/NetworkModelImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -580,7 +580,7 @@ public boolean canUseForDeploy(Network network) {
580580
if (network.getTrafficType() != TrafficType.Guest) {
581581
return false;
582582
}
583-
if (listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
583+
if (network.getGuestType() == GuestType.L2 || listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
584584
return true; // do not check free IPs if there is no service in the network
585585
}
586586
boolean hasFreeIps = true;

server/src/com/cloud/network/guru/GuestNetworkGuru.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ public Network design(final NetworkOffering offering, final DeploymentPlan plan,
200200
if (userSpecified.getCidr() != null) {
201201
network.setCidr(userSpecified.getCidr());
202202
network.setGateway(userSpecified.getGateway());
203-
} else if (offering.getGuestType() == GuestType.Shared || !_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty()) {
203+
} else if (offering.getGuestType() != GuestType.L2 && (offering.getGuestType() == GuestType.Shared || !_networkModel.listNetworkOfferingServices(offering.getId()).isEmpty())) {
204204
final String guestNetworkCidr = dc.getGuestNetworkCidr();
205205
if (guestNetworkCidr != null) {
206206
final String[] cidrTuple = guestNetworkCidr.split("\\/");
@@ -370,7 +370,7 @@ public NicProfile allocate(final Network network, NicProfile nic, final VirtualM
370370
guestIp = network.getGateway();
371371
} else {
372372
guestIp = _ipAddrMgr.acquireGuestIpAddress(network, nic.getRequestedIPv4());
373-
if (guestIp == null && !_networkModel.listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
373+
if (guestIp == null && network.getGuestType() != GuestType.L2 && !_networkModel.listNetworkOfferingServices(network.getNetworkOfferingId()).isEmpty()) {
374374
throw new InsufficientVirtualNetworkCapacityException("Unable to acquire Guest IP" + " address for network " + network, DataCenter.class,
375375
dc.getId());
376376
}

ui/scripts/configuration.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2404,18 +2404,22 @@
24042404
var $useVpc = args.$form.find('.form-item[rel=\"useVpc\"]');
24052405
var $useVpcCb = $useVpc.find("input[type=checkbox]");
24062406
var $supportedServices = args.$form.find('.form-item[rel=\"supportedServices\"]');
2407+
var $userDataL2 = args.$form.find('.form-item[rel=\"userDataL2\"]');
24072408
if ($guestTypeField.val() == 'Shared') { //Shared network offering
24082409
$useVpc.hide();
2410+
$userDataL2.hide();
24092411
$supportedServices.css('display', 'inline-block');
24102412
if ($useVpcCb.is(':checked')) { //if useVpc is checked,
24112413
$useVpcCb.removeAttr("checked"); //remove "checked" attribute in useVpc
24122414
}
24132415
} else if ($guestTypeField.val() == 'Isolated') { //Isolated network offering
24142416
$useVpc.css('display', 'inline-block');
24152417
$supportedServices.css('display', 'inline-block');
2418+
$userDataL2.hide();
24162419
} else if ($guestTypeField.val() == 'L2') {
24172420
$useVpc.hide();
24182421
$supportedServices.hide();
2422+
$userDataL2.css('display', 'inline-block');
24192423
}
24202424
var $providers = $useVpcCb.closest('form').find('.dynamic-input select[name!="service.Connectivity.provider"]');
24212425
var $optionsOfProviders = $providers.find('option');
@@ -2803,6 +2807,13 @@
28032807
isBoolean: true
28042808
},
28052809

2810+
userDataL2: {
2811+
label: 'label.user.data',
2812+
docID: 'helpL2UserData',
2813+
isBoolean: true,
2814+
isHidden: true
2815+
},
2816+
28062817
lbType: { //only shown when VPC is checked and LB service is checked
28072818
label: 'label.load.balancer.type',
28082819
isHidden: true,
@@ -3384,6 +3395,14 @@
33843395
} else { //specifyVlan checkbox is unchecked
33853396
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)
33863397
}
3398+
3399+
if (inputData['userDataL2'] == 'on') {
3400+
inputData['serviceProviderList[0].service'] = 'UserData';
3401+
inputData['serviceProviderList[0].provider'] = 'ConfigDrive';
3402+
inputData['supportedServices'] = 'UserData';
3403+
} else {
3404+
delete inputData.serviceProviderList;
3405+
}
33873406
}
33883407

33893408
if (inputData['forvpc'] == 'on') {

ui/scripts/docs.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,5 +1351,9 @@ cloudStack.docs = {
13511351
},
13521352
helpSetReservationSystemVms: {
13531353
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)'
1354+
},
1355+
helpL2UserData: {
1356+
desc: 'Pass user and meta data to VMs (via ConfigDrive)',
1357+
externalLink: ''
13541358
}
13551359
};

0 commit comments

Comments
 (0)