Skip to content

Commit d64089d

Browse files
committed
api/server: support deploy-as-is template as VNF template
1 parent 6e5d78a commit d64089d

File tree

8 files changed

+77
-3
lines changed

8 files changed

+77
-3
lines changed

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

Lines changed: 4 additions & 0 deletions
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

@@ -44,9 +45,12 @@ public interface VnfTemplateManager {
4445

4546
void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long> networkIds);
4647

48+
void validateVnfApplianceNetworksMap(VirtualMachineTemplate template, Map<Integer, Long> vmNetworkMap);
49+
4750
SecurityGroup createSecurityGroupForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner, DeployVnfApplianceCmd cmd);
4851

4952
void createIsolatedNetworkRulesForVnfAppliance(DataCenter zone, VirtualMachineTemplate template, Account owner,
5053
UserVm vm, DeployVnfApplianceCmd cmd)
5154
throws InsufficientAddressCapacityException, ResourceAllocationException, ResourceUnavailableException;
55+
5256
}

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 Template cannot be registered with VNF nics as 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
@@ -122,6 +122,7 @@
122122
import com.cloud.agent.api.to.DiskTO;
123123
import com.cloud.agent.api.to.NfsTO;
124124
import com.cloud.agent.api.to.VirtualMachineTO;
125+
import com.cloud.agent.api.to.deployasis.OVFNetworkTO;
125126
import com.cloud.api.ApiDBUtils;
126127
import com.cloud.api.query.dao.UserVmJoinDao;
127128
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;
@@ -313,6 +315,8 @@ public class TemplateManagerImpl extends ManagerBase implements TemplateManager,
313315
protected SnapshotHelper snapshotHelper;
314316
@Inject
315317
VnfTemplateManager vnfTemplateManager;
318+
@Inject
319+
TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao;
316320

317321
@Inject
318322
private SecondaryStorageHeuristicDao secondaryStorageHeuristicDao;
@@ -2172,6 +2176,11 @@ private VMTemplateVO updateTemplateOrIso(BaseUpdateTemplateOrIsoCmd cmd) {
21722176
templateType = validateTemplateType(cmd, isAdmin, template.isCrossZones());
21732177
if (cmd instanceof UpdateVnfTemplateCmd) {
21742178
VnfTemplateUtils.validateApiCommandParams(cmd, template);
2179+
UpdateVnfTemplateCmd updateCmd = (UpdateVnfTemplateCmd) cmd;
2180+
if (template.isDeployAsIs() && CollectionUtils.isNotEmpty(updateCmd.getVnfNics())) {
2181+
List<OVFNetworkTO> ovfNetworks = templateDeployAsIsDetailsDao.listNetworkRequirementsByTemplateId(template.getId());
2182+
VnfTemplateUtils.validateDeployAsIsTemplateVnfNics(ovfNetworks, updateCmd.getVnfNics());
2183+
}
21752184
vnfTemplateManager.updateVnfTemplate(template.getId(), (UpdateVnfTemplateCmd) cmd);
21762185
}
21772186
templateTag = ((UpdateTemplateCmd)cmd).getTemplateTag();

server/src/main/java/com/cloud/vm/UserVmManagerImpl.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6127,7 +6127,11 @@ public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityE
61276127
throw new InvalidParameterValueException("Unable to use template " + templateId);
61286128
}
61296129
if (TemplateType.VNF.equals(template.getTemplateType())) {
6130-
vnfTemplateManager.validateVnfApplianceNics(template, cmd.getNetworkIds());
6130+
if (template.isDeployAsIs()) {
6131+
vnfTemplateManager.validateVnfApplianceNetworksMap(template, cmd.getVmNetworkMap());
6132+
} else {
6133+
vnfTemplateManager.validateVnfApplianceNics(template, cmd.getNetworkIds());
6134+
}
61316135
} else if (cmd instanceof DeployVnfApplianceCmd) {
61326136
throw new InvalidParameterValueException("Can't deploy VNF appliance from a non-VNF template");
61336137
}

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,9 @@ public void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long>
205205
if (CollectionUtils.isEmpty(networkIds)) {
206206
throw new InvalidParameterValueException("VNF nics list is empty");
207207
}
208+
if (CollectionUtils.isNotEmpty(networkIds) && template.isDeployAsIs()) {
209+
throw new InvalidParameterValueException("VNF nics list should be empty for deploy-as-is templates");
210+
}
208211
List<VnfTemplateNicVO> vnfNics = vnfTemplateNicDao.listByTemplateId(template.getId());
209212
for (VnfTemplateNicVO vnfNic : vnfNics) {
210213
if (vnfNic.isRequired() && networkIds.size() <= vnfNic.getDeviceId()) {
@@ -213,6 +216,19 @@ public void validateVnfApplianceNics(VirtualMachineTemplate template, List<Long>
213216
}
214217
}
215218

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

server/src/test/java/com/cloud/template/TemplateManagerImplTest.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import com.cloud.api.query.dao.UserVmJoinDao;
2424
import com.cloud.configuration.Resource;
2525
import com.cloud.dc.dao.DataCenterDao;
26+
import com.cloud.deployasis.dao.TemplateDeployAsIsDetailsDao;
2627
import com.cloud.domain.dao.DomainDao;
2728
import com.cloud.event.dao.UsageEventDao;
2829
import com.cloud.exception.InvalidParameterValueException;
@@ -204,6 +205,8 @@ public class TemplateManagerImplTest {
204205
AccountManager _accountMgr;
205206
@Inject
206207
VnfTemplateManager vnfTemplateManager;
208+
@Inject
209+
TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao;
207210

208211
@Inject
209212
HeuristicRuleHelper heuristicRuleHelperMock;
@@ -956,6 +959,11 @@ public VnfTemplateManager vnfTemplateManager() {
956959
return Mockito.mock(VnfTemplateManager.class);
957960
}
958961

962+
@Bean
963+
public TemplateDeployAsIsDetailsDao templateDeployAsIsDetailsDao() {
964+
return Mockito.mock(TemplateDeployAsIsDetailsDao.class);
965+
}
966+
959967
@Bean
960968
public SnapshotHelper snapshotHelper() {
961969
return Mockito.mock(SnapshotHelper.class);

ui/src/views/compute/DeployVnfAppliance.vue

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,6 +372,7 @@
372372
<div>
373373
<vnf-nics-selection
374374
:items="templateVnfNics"
375+
:templateNics="templateNics"
375376
:networks="networks"
376377
@update-vnf-nic-networks="($event) => updateVnfNicNetworks($event)" />
377378
</div>
@@ -1293,7 +1294,8 @@ export default {
12931294
return tabList
12941295
},
12951296
showVnfNicsSection () {
1296-
return this.networks && this.networks.length > 0 && this.vm.templateid && this.templateVnfNics && this.templateVnfNics.length > 0
1297+
return ((this.networks && this.networks.length > 0) || (this.templateNics && this.templateNics.length > 0)) &&
1298+
this.vm.templateid && this.templateVnfNics && this.templateVnfNics.length > 0
12971299
},
12981300
showVnfConfigureManagement () {
12991301
const managementDeviceIds = []
@@ -1303,6 +1305,11 @@ export default {
13031305
}
13041306
}
13051307
for (const deviceId of managementDeviceIds) {
1308+
if (this.templateNics && this.templateNics[deviceId] &&
1309+
((this.templateNics[deviceId].selectednetworktype === 'Isolated' && this.templateNics[deviceId].selectednetworkvpcid === undefined) ||
1310+
(this.templateNics[deviceId].selectednetworktype === 'Shared' && this.templateNics[deviceId].selectednetworkwithsg))) {
1311+
return true
1312+
}
13061313
if (this.vnfNicNetworks && this.vnfNicNetworks[deviceId] &&
13071314
((this.vnfNicNetworks[deviceId].type === 'Isolated' && this.vnfNicNetworks[deviceId].vpcid === undefined) ||
13081315
(this.vnfNicNetworks[deviceId].type === 'Shared' && this.zone.securitygroupsenabled))) {
@@ -2005,7 +2012,7 @@ export default {
20052012
// All checked networks should be used and only once.
20062013
// Required NIC must be associated to a network
20072014
// DeviceID must be consequent
2008-
if (this.templateVnfNics && this.templateVnfNics.length > 0) {
2015+
if (this.templateVnfNics && this.templateVnfNics.length > 0 && (!this.templateNics || this.templateNics.length === 0)) {
20092016
let nextDeviceId = 0
20102017
const usedNetworkIds = []
20112018
const keys = Object.keys(this.vnfNicNetworks)
@@ -2629,6 +2636,9 @@ export default {
26292636
var network = this.options.networks[Math.min(i, this.options.networks.length - 1)]
26302637
nic.selectednetworkid = network.id
26312638
nic.selectednetworkname = network.name
2639+
nic.selectednetworktype = network.type
2640+
nic.selectednetworkvpcid = network.vpcid
2641+
nic.selectednetworkwithsg = network.service.filter(svc => svc.name === 'SecurityGroupProvider').length > 0
26322642
this.nicToNetworkSelection.push({ nic: nic.id, network: network.id })
26332643
}
26342644
}

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)