Skip to content

Commit f5edd2c

Browse files
weizhouapacheLocharla, Sandeep
authored andcommitted
api/server: support deploy-as-is template as VNF template (apache#12499)
1 parent 127efb1 commit f5edd2c

File tree

10 files changed

+76
-13
lines changed

10 files changed

+76
-13
lines changed

api/src/main/java/org/apache/cloudstack/api/command/user/vm/DeployVnfApplianceCmd.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
public class DeployVnfApplianceCmd extends DeployVMCmd implements UserCmd {
4444

4545
@Parameter(name = ApiConstants.VNF_CONFIGURE_MANAGEMENT, type = CommandType.BOOLEAN, required = false,
46-
description = "True by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. False otherwise. " +
46+
description = "False by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. True otherwise. " +
4747
"Network rules are configured if management network is an isolated network or shared network with security groups.")
4848
private Boolean vnfConfigureManagement;
4949

api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManager.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import org.apache.cloudstack.api.command.user.vm.DeployVnfApplianceCmd;
3030
import org.apache.cloudstack.framework.config.ConfigKey;
3131
import java.util.List;
32+
import java.util.Map;
3233

3334
public interface VnfTemplateManager {
3435

@@ -42,11 +43,12 @@ public interface VnfTemplateManager {
4243

4344
void updateVnfTemplate(long templateId, UpdateVnfTemplateCmd cmd);
4445

45-
void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long> networkIds);
46+
void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long> networkIds, Map<Integer, Long> vmNetworkMap);
4647

4748
SecurityGroup createSecurityGroupForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner, DeployVnfApplianceCmd cmd);
4849

4950
void createIsolatedNetworkRulesForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner,
5051
UserVm vm, DeployVnfApplianceCmd cmd)
5152
throws InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException;
53+
5254
}

api/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateUtils.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
// under the License.
1717
package org.apache.cloudstack.storage.template;
1818

19+
import com.cloud.agent.api.to.deployasis.OVFNetworkTO;
1920
import com.cloud.exception.InvalidParameterValueException;
2021
import com.cloud.network.VNF;
2122
import com.cloud.storage.Storage;
@@ -124,6 +125,9 @@ public static void validateVnfNics(List<VNF.VnfNic> nicsList) {
124125
public static void validateApiCommandParams(BaseCmd cmd, VirtualMachineTemplate template) {
125126
if (cmd instanceof RegisterVnfTemplateCmd) {
126127
RegisterVnfTemplateCmd registerCmd = (RegisterVnfTemplateCmd) cmd;
128+
if (registerCmd.isDeployAsIs() && CollectionUtils.isNotEmpty(registerCmd.getVnfNics())) {
129+
throw new InvalidParameterValueException("VNF nics cannot be specified when register a deploy-as-is Template. Please wait until Template settings are read from OVA.");
130+
}
127131
validateApiCommandParams(registerCmd.getVnfDetails(), registerCmd.getVnfNics(), registerCmd.getTemplateType());
128132
} else if (cmd instanceof UpdateVnfTemplateCmd) {
129133
UpdateVnfTemplateCmd updateCmd = (UpdateVnfTemplateCmd) cmd;
@@ -149,4 +153,18 @@ public static void validateVnfCidrList(List<String> cidrList) {
149153
}
150154
}
151155
}
156+
157+
public static void validateDeployAsIsTemplateVnfNics(List<OVFNetworkTO> ovfNetworks, List<VNF.VnfNic> vnfNics) {
158+
if (CollectionUtils.isEmpty(vnfNics)) {
159+
return;
160+
}
161+
if (CollectionUtils.isEmpty(ovfNetworks)) {
162+
throw new InvalidParameterValueException("The list of networks read from OVA is empty. Please wait until the template is fully downloaded and processed.");
163+
}
164+
for (VNF.VnfNic vnfNic : vnfNics) {
165+
if (vnfNic.getDeviceId() < ovfNetworks.size() && !vnfNic.isRequired()) {
166+
throw new InvalidParameterValueException(String.format("The VNF nic [device ID: %s ] is required as it is defined in the OVA template.", vnfNic.getDeviceId()));
167+
}
168+
}
169+
}
152170
}

server/src/main/java/com/cloud/template/TemplateManagerImpl.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@
121121
import com.cloud.agent.api.to.DiskTO;
122122
import com.cloud.agent.api.to.NfsTO;
123123
import com.cloud.agent.api.to.VirtualMachineTO;
124+
import com.cloud.agent.api.to.deployasis.OVFNetworkTO;
124125
import com.cloud.api.ApiDBUtils;
125126
import com.cloud.api.query.dao.UserVmJoinDao;
126127
import com.cloud.api.query.vo.UserVmJoinVO;
@@ -131,6 +132,7 @@
131132
import com.cloud.dc.DataCenterVO;
132133
import com.cloud.dc.dao.DataCenterDao;
133134
import com.cloud.deploy.DeployDestination;
135+
import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao;
134136
import com.cloud.domain.Domain;
135137
import com.cloud.domain.dao.DomainDao;
136138
import com.cloud.event.ActionEvent;
@@ -315,6 +317,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
315317
protected SnapshotHelper snapshotHelper;
316318
@Inject
317319
VnfTemplateManager vnfTemplateManager;
320+
@Inject
321+
TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao;
318322

319323
@Inject
320324
private SecondaryStorageHeuristicDao secondaryStorageHeuristicDao;
@@ -2217,6 +2221,11 @@ private VMTemplateVO updateTemplateOrIso(BaseUpdateTemplateOrIsoCmd cmd) {
22172221
templateType = validateTemplateType(cmd, isAdmin, template.isCrossZones(), template.getHypervisorType());
22182222
if (cmd instanceof UpdateVnfTemplateCmd) {
22192223
VnfTemplateUtils.validateApiCommandParams(cmd, template);
2224+
UpdateVnfTemplateCmd updateCmd = (UpdateVnfTemplateCmd) cmd;
2225+
if (template.isDeployAsIs() && CollectionUtils.isNotEmpty(updateCmd.getVnfNics())) {
2226+
List<OVFNetworkTO> ovfNetworks = templateDeployAsIsDetailsDao.listNetworkRequirementsByTemplateId(template.getId());
2227+
VnfTemplateUtils.validateDeployAsIsTemplateVnfNics(ovfNetworks, updateCmd.getVnfNics());
2228+
}
22202229
vnfTemplateManager.updateVnfTemplate(template.getId(), (UpdateVnfTemplateCmd) cmd);
22212230
}
22222231
templateTag = ((UpdateTemplateCmd)cmd).getTemplateTag();

server/src/main/java/org/apache/cloudstack/storage/template/VnfTemplateManagerImpl.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,14 @@ public ConfigKey<?>[] getConfigKeys() {
201201
}
202202

203203
@Override
204-
public void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long> networkIds) {
204+
public void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long> networkIds, Map<Integer, Long> vmNetworkMap) {
205+
if (template.isDeployAsIs()) {
206+
if (CollectionUtils.isNotEmpty(networkIds)) {
207+
throw new InvalidParameterValueException("VNF nics mappings should be empty for deploy-as-is templates");
208+
}
209+
validateVnfApplianceNetworksMap(template, vmNetworkMap);
210+
return;
211+
}
205212
if (CollectionUtils.isEmpty(networkIds)) {
206213
throw new InvalidParameterValueException("VNF nics list is empty");
207214
}
@@ -213,6 +220,18 @@ public void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long>
213220
}
214221
}
215222

223+
private void validateVnfApplianceNetworksMap(VirtualMachineTemplate template, Map<Integer, Long> vmNetworkMap) {
224+
if (MapUtils.isEmpty(vmNetworkMap)) {
225+
throw new InvalidParameterValueException("VNF networks map is empty");
226+
}
227+
List<VnfTemplateNicVO> vnfNics = vnfTemplateNicDao.listByTemplateId(template.getId());
228+
for (VnfTemplateNicVO vnfNic : vnfNics) {
229+
if (vnfNic.isRequired() && vmNetworkMap.size() <= vnfNic.getDeviceId()) {
230+
throw new InvalidParameterValueException("VNF nic is required but not found: " + vnfNic);
231+
}
232+
}
233+
}
234+
216235
protected Set<Integer> getOpenPortsForVnfAppliance(VirtualMachineTemplate template) {
217236
Set<Integer> ports = new HashSet<>();
218237
VnfTemplateDetailVO accessMethodsDetail = vnfTemplateDetailsDao.findDetail(template.getId(), VNF.AccessDetail.ACCESS_METHODS.name().toLowerCase());

server/src/test/java/com/cloud/vm/UserVmManagerImplTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,7 +1164,7 @@ public void createVirtualMachine() throws ResourceUnavailableException, Insuffic
11641164
when(templateMock.isDeployAsIs()).thenReturn(false);
11651165
when(templateMock.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
11661166
when(templateMock.getUserDataId()).thenReturn(null);
1167-
Mockito.doNothing().when(vnfTemplateManager).validateVnfApplianceNics(any(), nullable(List.class));
1167+
Mockito.doNothing().when(vnfTemplateManager).validateVnfApplianceNics(any(), nullable(List.class), nullable(Map.class));
11681168

11691169
ServiceOfferingJoinVO svcOfferingMock = Mockito.mock(ServiceOfferingJoinVO.class);
11701170
when(serviceOfferingJoinDao.findById(anyLong())).thenReturn(svcOfferingMock);
@@ -1176,7 +1176,7 @@ public void createVirtualMachine() throws ResourceUnavailableException, Insuffic
11761176

11771177
UserVm result = userVmManagerImpl.createVirtualMachine(deployVMCmd);
11781178
assertEquals(userVmVoMock, result);
1179-
Mockito.verify(vnfTemplateManager).validateVnfApplianceNics(templateMock, null);
1179+
Mockito.verify(vnfTemplateManager).validateVnfApplianceNics(templateMock, null, null);
11801180
Mockito.verify(userVmManagerImpl).createBasicSecurityGroupVirtualMachine(any(), any(), any(), any(), any(), any(), any(),
11811181
any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), any(), nullable(Boolean.class), any(), any(), any(),
11821182
any(), any(), any(), any(), eq(true), any(), any(), any());
@@ -1420,7 +1420,7 @@ public void createVirtualMachineWithCloudRuntimeException() throws ResourceUnava
14201420
when(templateMock.isDeployAsIs()).thenReturn(false);
14211421
when(templateMock.getFormat()).thenReturn(Storage.ImageFormat.QCOW2);
14221422
when(templateMock.getUserDataId()).thenReturn(null);
1423-
Mockito.doNothing().when(vnfTemplateManager).validateVnfApplianceNics(any(), nullable(List.class));
1423+
Mockito.doNothing().when(vnfTemplateManager).validateVnfApplianceNics(any(), nullable(List.class), nullable(Map.class));
14241424

14251425
ServiceOfferingJoinVO svcOfferingMock = Mockito.mock(ServiceOfferingJoinVO.class);
14261426
when(serviceOfferingJoinDao.findById(anyLong())).thenReturn(svcOfferingMock);

server/src/test/java/org/apache/cloudstack/storage/template/VnfTemplateManagerImplTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -228,25 +228,25 @@ public void testPersistVnfTemplateUpdateWithoutDetails() {
228228
@Test
229229
public void testValidateVnfApplianceNicsWithRequiredNics() {
230230
List<Long> networkIds = Arrays.asList(200L, 201L);
231-
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds);
231+
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds, null);
232232
}
233233

234234
@Test
235235
public void testValidateVnfApplianceNicsWithAllNics() {
236236
List<Long> networkIds = Arrays.asList(200L, 201L, 202L);
237-
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds);
237+
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds, null);
238238
}
239239

240240
@Test(expected = InvalidParameterValueException.class)
241241
public void testValidateVnfApplianceNicsWithEmptyList() {
242242
List<Long> networkIds = new ArrayList<>();
243-
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds);
243+
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds, null);
244244
}
245245

246246
@Test(expected = InvalidParameterValueException.class)
247247
public void testValidateVnfApplianceNicsWithMissingNetworkId() {
248248
List<Long> networkIds = Arrays.asList(200L);
249-
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds);
249+
vnfTemplateManagerImpl.validateVnfApplianceNics(template, networkIds, null);
250250
}
251251

252252
@Test

ui/public/locales/en.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2770,7 +2770,7 @@
27702770
"label.vnf.cidr.list": "CIDR from which access to the VNF appliance's Management interface should be allowed from",
27712771
"label.vnf.cidr.list.tooltip": "the CIDR list to forward traffic from to the VNF management interface. Multiple entries must be separated by a single comma character (,). The default value is 0.0.0.0/0.",
27722772
"label.vnf.configure.management": "Configure network rules for VNF's management interfaces",
2773-
"label.vnf.configure.management.tooltip": "True by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. False otherwise. Learn what rules are configured at http://docs.cloudstack.apache.org/en/latest/adminguide/networking/vnf_templates_appliances.html#deploying-vnf-appliances",
2773+
"label.vnf.configure.management.tooltip": "False by default, security group or network rules (source nat and firewall rules) will be configured for VNF management interfaces. True otherwise. Learn what rules are configured at http://docs.cloudstack.apache.org/en/latest/adminguide/networking/vnf_templates_appliances.html#deploying-vnf-appliances",
27742774
"label.vnf.detail.add": "Add VNF detail",
27752775
"label.vnf.detail.remove": "Remove VNF detail",
27762776
"label.vnf.details": "VNF Details",

ui/src/views/compute/DeployVnfAppliance.vue

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,7 @@
360360
<div>
361361
<vnf-nics-selection
362362
:items="templateVnfNics"
363+
:templateNics="templateNics"
363364
:networks="networks"
364365
@update-vnf-nic-networks="($event) => updateVnfNicNetworks($event)" />
365366
</div>
@@ -1303,7 +1304,8 @@ export default {
13031304
return tabList
13041305
},
13051306
showVnfNicsSection () {
1306-
return this.networks && this.networks.length > 0 && this.vm.templateid && this.templateVnfNics && this.templateVnfNics.length > 0
1307+
return ((this.networks && this.networks.length > 0) || (this.templateNics && this.templateNics.length > 0)) &&
1308+
this.vm.templateid && this.templateVnfNics && this.templateVnfNics.length > 0
13071309
},
13081310
showVnfConfigureManagement () {
13091311
const managementDeviceIds = []
@@ -1313,6 +1315,11 @@ export default {
13131315
}
13141316
}
13151317
for (const deviceId of managementDeviceIds) {
1318+
if (this.templateNics && this.templateNics[deviceId] &&
1319+
((this.templateNics[deviceId].selectednetworktype === 'Isolated' && this.templateNics[deviceId].selectednetworkvpcid === undefined) ||
1320+
(this.templateNics[deviceId].selectednetworktype === 'Shared' && this.templateNics[deviceId].selectednetworkwithsg))) {
1321+
return true
1322+
}
13161323
if (this.vnfNicNetworks && this.vnfNicNetworks[deviceId] &&
13171324
((this.vnfNicNetworks[deviceId].type === 'Isolated' && this.vnfNicNetworks[deviceId].vpcid === undefined) ||
13181325
(this.vnfNicNetworks[deviceId].type === 'Shared' && this.vnfNicNetworks[deviceId].service.filter(svc => svc.name === 'SecurityGroupProvider')))) {
@@ -2090,7 +2097,7 @@ export default {
20902097
// All checked networks should be used and only once.
20912098
// Required NIC must be associated to a network
20922099
// DeviceID must be consequent
2093-
if (this.templateVnfNics && this.templateVnfNics.length > 0) {
2100+
if (this.templateVnfNics && this.templateVnfNics.length > 0 && (!this.templateNics || this.templateNics.length === 0)) {
20942101
let nextDeviceId = 0
20952102
const usedNetworkIds = []
20962103
const keys = Object.keys(this.vnfNicNetworks)
@@ -2720,6 +2727,9 @@ export default {
27202727
var network = this.options.networks[Math.min(i, this.options.networks.length - 1)]
27212728
nic.selectednetworkid = network.id
27222729
nic.selectednetworkname = network.name
2730+
nic.selectednetworktype = network.type
2731+
nic.selectednetworkvpcid = network.vpcid
2732+
nic.selectednetworkwithsg = network.service.filter(svc => svc.name === 'SecurityGroupProvider').length > 0
27232733
this.nicToNetworkSelection.push({ nic: nic.id, network: network.id })
27242734
}
27252735
}

ui/src/views/compute/wizard/VnfNicsSelection.vue

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@
5050
<template #network="{ record }">
5151
<a-form-item style="display: block" :name="'nic-' + record.deviceid">
5252
<a-select
53+
disabled="templateNics && templateNics.length > 0"
5354
@change="updateNicNetworkValue($event, record.deviceid)"
5455
optionFilterProp="label"
5556
:filterOption="(input, option) => {
@@ -75,6 +76,10 @@ export default {
7576
type: Array,
7677
default: () => []
7778
},
79+
templateNics: {
80+
type: Array,
81+
default: () => []
82+
},
7883
networks: {
7984
type: Array,
8085
default: () => []

0 commit comments

Comments
 (0)