Skip to content

Commit 54b44cc

Browse files
authored
KVM: Option to deploy a VM with existing volume/snapshot (#10503)
* Option to deploy a VM with existing volume/snapshot * smoke test changes check if the hypervisor is KVM check if the primary storage's scope is ZONE wide * skip all tests if the storage isn't Zone-Wide and the hypervisor isn't KVM * support StorPool tags add StorPool tags to a volume created from snapshot or to a volume which will be attached as a ROOT to a new VM * Add StorPool tags on the new ROOT volume * Add the StorPool's tags when volume is created from a snapshot or a volume is attached as a ROOT to a VM * Addressed review
1 parent 0dbd761 commit 54b44cc

File tree

29 files changed

+929
-189
lines changed

29 files changed

+929
-189
lines changed

api/src/main/java/com/cloud/vm/UserVmService.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// under the License.
1717
package com.cloud.vm;
1818

19+
import com.cloud.storage.Snapshot;
20+
import com.cloud.storage.Volume;
1921
import java.util.LinkedHashMap;
2022
import java.util.List;
2123
import java.util.Map;
@@ -222,7 +224,7 @@ UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering s
222224
String userData, Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIp, Boolean displayVm, String keyboard,
223225
List<Long> affinityGroupIdList, Map<String, String> customParameter, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
224226
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
225-
Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId) throws InsufficientCapacityException,
227+
Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException,
226228
ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
227229

228230
/**
@@ -298,7 +300,7 @@ UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOfferin
298300
List<Long> securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor,
299301
HTTPMethod httpmethod, String userData, Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard,
300302
List<Long> affinityGroupIdList, Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap,
301-
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
303+
Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap, Map<String, String> userVmOVFProperties, boolean dynamicScalingEnabled, Long overrideDiskOfferingId, String vmType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
302304

303305
/**
304306
* Creates a User VM in Advanced Zone (Security Group feature is disabled)
@@ -370,7 +372,7 @@ UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffe
370372
String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, HTTPMethod httpmethod, String userData,
371373
Long userDataId, String userDataDetails, List<String> sshKeyPairs, Map<Long, IpAddresses> requestedIps, IpAddresses defaultIps, Boolean displayVm, String keyboard, List<Long> affinityGroupIdList,
372374
Map<String, String> customParameters, String customId, Map<String, Map<Integer, String>> dhcpOptionMap, Map<Long, DiskOffering> dataDiskTemplateToDiskOfferingMap,
373-
Map<String, String> templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId)
375+
Map<String, String> templateOvfPropertiesMap, boolean dynamicScalingEnabled, String vmType, Long overrideDiskOfferingId, Volume volume, Snapshot snapshot)
374376

375377
throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException;
376378

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

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,10 +30,14 @@
3030
import com.cloud.offering.DiskOffering;
3131
import com.cloud.template.VirtualMachineTemplate;
3232
import com.cloud.uservm.UserVm;
33+
import com.cloud.utils.exception.CloudRuntimeException;
3334
import com.cloud.utils.net.Dhcp;
3435
import com.cloud.utils.net.NetUtils;
3536
import com.cloud.vm.VirtualMachine;
3637
import com.cloud.vm.VmDetailConstants;
38+
39+
import java.util.Objects;
40+
import java.util.stream.Stream;
3741
import org.apache.cloudstack.acl.RoleType;
3842
import org.apache.cloudstack.affinity.AffinityGroupResponse;
3943
import org.apache.cloudstack.api.ACL;
@@ -55,9 +59,11 @@
5559
import org.apache.cloudstack.api.response.ProjectResponse;
5660
import org.apache.cloudstack.api.response.SecurityGroupResponse;
5761
import org.apache.cloudstack.api.response.ServiceOfferingResponse;
62+
import org.apache.cloudstack.api.response.SnapshotResponse;
5863
import org.apache.cloudstack.api.response.TemplateResponse;
5964
import org.apache.cloudstack.api.response.UserDataResponse;
6065
import org.apache.cloudstack.api.response.UserVmResponse;
66+
import org.apache.cloudstack.api.response.VolumeResponse;
6167
import org.apache.cloudstack.api.response.ZoneResponse;
6268
import org.apache.cloudstack.context.CallContext;
6369
import org.apache.cloudstack.vm.lease.VMLeaseManager;
@@ -95,7 +101,7 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
95101
private Long serviceOfferingId;
96102

97103
@ACL
98-
@Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, required = true, description = "the ID of the template for the virtual machine")
104+
@Parameter(name = ApiConstants.TEMPLATE_ID, type = CommandType.UUID, entityType = TemplateResponse.class, description = "the ID of the template for the virtual machine")
99105
private Long templateId;
100106

101107
@Parameter(name = ApiConstants.NAME, type = CommandType.STRING, description = "host name for the virtual machine", validations = {ApiArgValidator.RFCComplianceDomainName})
@@ -286,6 +292,11 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
286292
description = "Lease expiry action, valid values are STOP and DESTROY")
287293
private String leaseExpiryAction;
288294

295+
@Parameter(name = ApiConstants.VOLUME_ID, type = CommandType.UUID, entityType = VolumeResponse.class, since = "4.21")
296+
private Long volumeId;
297+
298+
@Parameter(name = ApiConstants.SNAPSHOT_ID, type = CommandType.UUID, entityType = SnapshotResponse.class, since = "4.21")
299+
private Long snapshotId;
289300
/////////////////////////////////////////////////////
290301
/////////////////// Accessors ///////////////////////
291302
/////////////////////////////////////////////////////
@@ -744,6 +755,18 @@ public ApiConstants.IoDriverPolicy getIoDriverPolicy() {
744755
}
745756
return null;
746757
}
758+
759+
public Long getVolumeId() {
760+
return volumeId;
761+
}
762+
763+
public Long getSnapshotId() {
764+
return snapshotId;
765+
}
766+
767+
public boolean isVolumeOrSnapshotProvided() {
768+
return volumeId != null || snapshotId != null;
769+
}
747770
/////////////////////////////////////////////////////
748771
/////////////// API Implementation///////////////////
749772
/////////////////////////////////////////////////////
@@ -840,6 +863,10 @@ public void execute() {
840863

841864
@Override
842865
public void create() throws ResourceAllocationException {
866+
if (Stream.of(templateId, snapshotId, volumeId).filter(Objects::nonNull).count() != 1) {
867+
throw new CloudRuntimeException("Please provide only one of the following parameters - template ID, volume ID or snapshot ID");
868+
}
869+
843870
try {
844871
UserVm vm = _userVmService.createVirtualMachine(this);
845872

engine/api/src/main/java/com/cloud/vm/VirtualMachineManager.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
// under the License.
1717
package com.cloud.vm;
1818

19+
import com.cloud.storage.Snapshot;
20+
import com.cloud.storage.Volume;
1921
import java.net.URI;
2022
import java.util.HashMap;
2123
import java.util.LinkedHashMap;
@@ -129,11 +131,11 @@ interface Topics {
129131
* @throws InsufficientCapacityException If there are insufficient capacity to deploy this vm.
130132
*/
131133
void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering, DiskOfferingInfo rootDiskOfferingInfo,
132-
List<DiskOfferingInfo> dataDiskOfferings, LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, DeploymentPlan plan,
133-
HypervisorType hyperType, Map<String, Map<Integer, String>> extraDhcpOptions, Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap) throws InsufficientCapacityException;
134+
List<DiskOfferingInfo> dataDiskOfferings, LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, DeploymentPlan plan,
135+
HypervisorType hyperType, Map<String, Map<Integer, String>> extraDhcpOptions, Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap, Volume volume, Snapshot snapshot) throws InsufficientCapacityException;
134136

135137
void allocate(String vmInstanceName, VirtualMachineTemplate template, ServiceOffering serviceOffering,
136-
LinkedHashMap<? extends Network, List<? extends NicProfile>> networkProfiles, DeploymentPlan plan, HypervisorType hyperType) throws InsufficientCapacityException;
138+
LinkedHashMap<? extends Network, List<? extends NicProfile>> networkProfiles, DeploymentPlan plan, HypervisorType hyperType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException;
137139

138140
void start(String vmUuid, Map<VirtualMachineProfile.Param, Object> params);
139141

engine/api/src/main/java/org/apache/cloudstack/engine/orchestration/service/VolumeOrchestrationService.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ DiskProfile allocateRawVolume(Type type, String name, DiskOffering offering, Lon
149149
* Allocate a volume or multiple volumes in case of template is registered with the 'deploy-as-is' option, allowing multiple disks
150150
*/
151151
List<DiskProfile> allocateTemplatedVolumes(Type type, String name, DiskOffering offering, Long rootDisksize, Long minIops, Long maxIops, VirtualMachineTemplate template, VirtualMachine vm,
152-
Account owner);
152+
Account owner, Volume volume, Snapshot snapshot);
153153

154154
String getVmNameFromVolumeId(long volumeId);
155155

engine/api/src/main/java/org/apache/cloudstack/engine/service/api/OrchestrationService.java

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
*/
1919
package org.apache.cloudstack.engine.service.api;
2020

21+
import com.cloud.storage.Snapshot;
22+
import com.cloud.storage.Volume;
2123
import java.net.URL;
2224
import java.util.List;
2325
import java.util.Map;
@@ -62,20 +64,20 @@ public interface OrchestrationService {
6264
@POST
6365
@Path("/createvm")
6466
VirtualMachineEntity createVirtualMachine(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("template-id") String templateId,
65-
@QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor,
66-
@QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize,
67-
@QueryParam("compute-tags") List<String> computeTags, @QueryParam("root-disk-tags") List<String> rootDiskTags,
68-
@QueryParam("network-nic-map") Map<String, List<NicProfile>> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan,
69-
@QueryParam("root-disk-size") Long rootDiskSize, @QueryParam("extra-dhcp-option-map") Map<String, Map<Integer, String>> extraDhcpOptionMap,
70-
@QueryParam("datadisktemplate-diskoffering-map") Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap, @QueryParam("disk-offering-id") Long diskOfferingId, @QueryParam("root-disk-offering-id") Long rootDiskOfferingId) throws InsufficientCapacityException;
67+
@QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor,
68+
@QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize,
69+
@QueryParam("compute-tags") List<String> computeTags, @QueryParam("root-disk-tags") List<String> rootDiskTags,
70+
@QueryParam("network-nic-map") Map<String, List<NicProfile>> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan,
71+
@QueryParam("root-disk-size") Long rootDiskSize, @QueryParam("extra-dhcp-option-map") Map<String, Map<Integer, String>> extraDhcpOptionMap,
72+
@QueryParam("datadisktemplate-diskoffering-map") Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap, @QueryParam("disk-offering-id") Long diskOfferingId, @QueryParam("root-disk-offering-id") Long rootDiskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException;
7173

7274
@POST
7375
VirtualMachineEntity createVirtualMachineFromScratch(@QueryParam("id") String id, @QueryParam("owner") String owner, @QueryParam("iso-id") String isoId,
7476
@QueryParam("host-name") String hostName, @QueryParam("display-name") String displayName, @QueryParam("hypervisor") String hypervisor,
7577
@QueryParam("os") String os, @QueryParam("cpu") int cpu, @QueryParam("speed") int speed, @QueryParam("ram") long memory, @QueryParam("disk-size") Long diskSize,
7678
@QueryParam("compute-tags") List<String> computeTags, @QueryParam("root-disk-tags") List<String> rootDiskTags,
7779
@QueryParam("network-nic-map") Map<String, List<NicProfile>> networkNicMap, @QueryParam("deploymentplan") DeploymentPlan plan,
78-
@QueryParam("extra-dhcp-option-map") Map<String, Map<Integer, String>> extraDhcpOptionMap, @QueryParam("disk-offering-id") Long diskOfferingId) throws InsufficientCapacityException;
80+
@QueryParam("extra-dhcp-option-map") Map<String, Map<Integer, String>> extraDhcpOptionMap, @QueryParam("disk-offering-id") Long diskOfferingId, Volume volume, Snapshot snapshot) throws InsufficientCapacityException;
7981

8082
@POST
8183
NetworkEntity createNetwork(String id, String name, String domainName, String cidr, String gateway);

engine/orchestration/src/main/java/com/cloud/vm/VirtualMachineManagerImpl.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
import javax.naming.ConfigurationException;
5050
import javax.persistence.EntityExistsException;
5151

52+
5253
import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao;
5354
import org.apache.cloudstack.annotation.AnnotationService;
5455
import org.apache.cloudstack.annotation.dao.AnnotationDao;
@@ -230,6 +231,7 @@
230231
import com.cloud.service.dao.ServiceOfferingDao;
231232
import com.cloud.storage.DiskOfferingVO;
232233
import com.cloud.storage.ScopeType;
234+
import com.cloud.storage.Snapshot;
233235
import com.cloud.storage.Storage;
234236
import com.cloud.storage.Storage.ImageFormat;
235237
import com.cloud.storage.StorageManager;
@@ -291,6 +293,7 @@
291293
import com.cloud.vm.snapshot.dao.VMSnapshotDao;
292294
import com.google.gson.Gson;
293295

296+
294297
public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMachineManager, VmWorkJobHandler, Listener, Configurable {
295298

296299
public static final String VM_WORK_JOB_HANDLER = VirtualMachineManagerImpl.class.getSimpleName();
@@ -503,8 +506,8 @@ public void registerGuru(final VirtualMachine.Type type, final VirtualMachineGur
503506
@Override
504507
@DB
505508
public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering,
506-
final DiskOfferingInfo rootDiskOfferingInfo, final List<DiskOfferingInfo> dataDiskOfferings,
507-
final LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, final DeploymentPlan plan, final HypervisorType hyperType, final Map<String, Map<Integer, String>> extraDhcpOptions, final Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap)
509+
final DiskOfferingInfo rootDiskOfferingInfo, final List<DiskOfferingInfo> dataDiskOfferings,
510+
final LinkedHashMap<? extends Network, List<? extends NicProfile>> auxiliaryNetworks, final DeploymentPlan plan, final HypervisorType hyperType, final Map<String, Map<Integer, String>> extraDhcpOptions, final Map<Long, DiskOffering> datadiskTemplateToDiskOfferingMap, Volume volume, Snapshot snapshot)
508511
throws InsufficientCapacityException {
509512

510513
logger.info("allocating virtual machine from template: {} with hostname: {} and {} networks", template, vmInstanceName, auxiliaryNetworks.size());
@@ -542,7 +545,7 @@ public void allocate(final String vmInstanceName, final VirtualMachineTemplate t
542545

543546
logger.debug("Allocating disks for {}", persistedVm);
544547

545-
allocateRootVolume(persistedVm, template, rootDiskOfferingInfo, owner, rootDiskSizeFinal);
548+
allocateRootVolume(persistedVm, template, rootDiskOfferingInfo, owner, rootDiskSizeFinal, volume, snapshot);
546549

547550
// Create new Volume context and inject event resource type, id and details to generate VOLUME.CREATE event for the ROOT disk.
548551
CallContext volumeContext = CallContext.register(CallContext.current(), ApiCommandResourceType.Volume);
@@ -583,7 +586,7 @@ public void allocate(final String vmInstanceName, final VirtualMachineTemplate t
583586
}
584587
}
585588

586-
private void allocateRootVolume(VMInstanceVO vm, VirtualMachineTemplate template, DiskOfferingInfo rootDiskOfferingInfo, Account owner, Long rootDiskSizeFinal) {
589+
private void allocateRootVolume(VMInstanceVO vm, VirtualMachineTemplate template, DiskOfferingInfo rootDiskOfferingInfo, Account owner, Long rootDiskSizeFinal, Volume volume, Snapshot snapshot) {
587590
// Create new Volume context and inject event resource type, id and details to generate VOLUME.CREATE event for the ROOT disk.
588591
CallContext volumeContext = CallContext.register(CallContext.current(), ApiCommandResourceType.Volume);
589592
try {
@@ -595,7 +598,7 @@ private void allocateRootVolume(VMInstanceVO vm, VirtualMachineTemplate template
595598
logger.debug("%s has format [{}]. Skipping ROOT volume [{}] allocation.", template.toString(), ImageFormat.BAREMETAL, rootVolumeName);
596599
} else {
597600
volumeMgr.allocateTemplatedVolumes(Type.ROOT, rootVolumeName, rootDiskOfferingInfo.getDiskOffering(), rootDiskSizeFinal,
598-
rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vm, owner);
601+
rootDiskOfferingInfo.getMinIops(), rootDiskOfferingInfo.getMaxIops(), template, vm, owner, volume, snapshot);
599602
}
600603
} finally {
601604
// Remove volumeContext and pop vmContext back
@@ -605,9 +608,9 @@ private void allocateRootVolume(VMInstanceVO vm, VirtualMachineTemplate template
605608

606609
@Override
607610
public void allocate(final String vmInstanceName, final VirtualMachineTemplate template, final ServiceOffering serviceOffering,
608-
final LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, final DeploymentPlan plan, final HypervisorType hyperType) throws InsufficientCapacityException {
611+
final LinkedHashMap<? extends Network, List<? extends NicProfile>> networks, final DeploymentPlan plan, final HypervisorType hyperType, Volume volume, Snapshot snapshot) throws InsufficientCapacityException {
609612
DiskOffering diskOffering = _diskOfferingDao.findById(serviceOffering.getDiskOfferingId());
610-
allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(diskOffering), new ArrayList<>(), networks, plan, hyperType, null, null);
613+
allocate(vmInstanceName, template, serviceOffering, new DiskOfferingInfo(diskOffering), new ArrayList<>(), networks, plan, hyperType, null, null, volume, snapshot);
611614
}
612615

613616
VirtualMachineGuru getVmGuru(final VirtualMachine vm) {

0 commit comments

Comments
 (0)