Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.util.List;
import java.util.Map;

import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.PodResponse;
import org.apache.log4j.Logger;

import org.apache.cloudstack.acl.RoleType;
Expand Down Expand Up @@ -138,6 +140,12 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityG
@Parameter(name = ApiConstants.SSH_KEYPAIR, type = CommandType.STRING, description = "name of the ssh key pair used to login to the virtual machine")
private String sshKeyPairName;

@Parameter(name = ApiConstants.POD_ID, type = CommandType.UUID, entityType = PodResponse.class, description = "destination Pod ID to deploy the VM to - parameter available for root admin only")
private Long podId;

@Parameter(name = ApiConstants.CLUSTER_ID, type = CommandType.UUID, entityType = ClusterResponse.class, description = "destination Cluster ID to deploy the VM to - parameter available for root admin only")
private Long clusterId;

@Parameter(name = ApiConstants.HOST_ID, type = CommandType.UUID, entityType = HostResponse.class, description = "destination Host ID to deploy the VM to - parameter available for root admin only")
private Long hostId;

Expand Down Expand Up @@ -317,6 +325,14 @@ public String getSSHKeyPairName() {
return sshKeyPairName;
}

public Long getPodId() {
return podId;
}

public Long getClusterId() {
return clusterId;
}

public Long getHostId() {
return hostId;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
// under the License.
package org.apache.cloudstack.api.command.user.vm;

import org.apache.cloudstack.api.response.ClusterResponse;
import org.apache.cloudstack.api.response.PodResponse;
import org.apache.log4j.Logger;

import org.apache.cloudstack.acl.RoleType;
Expand Down Expand Up @@ -60,6 +62,18 @@ public class StartVMCmd extends BaseAsyncCmd {
required = true, description = "The ID of the virtual machine")
private Long id;

@Parameter(name = ApiConstants.POD_ID,
type = CommandType.UUID,
entityType = PodResponse.class,
description = "destination Pod ID to deploy the VM to - parameter available for root admin only")
private Long podId;

@Parameter(name = ApiConstants.CLUSTER_ID,
type = CommandType.UUID,
entityType = ClusterResponse.class,
description = "destination Cluster ID to deploy the VM to - parameter available for root admin only")
private Long clusterId;

@Parameter(name = ApiConstants.HOST_ID,
type = CommandType.UUID,
entityType = HostResponse.class,
Expand All @@ -82,6 +96,14 @@ public Long getHostId() {
return hostId;
}

public Long getPodId() {
return podId;
}

public Long getClusterId() {
return clusterId;
}

// ///////////////////////////////////////////////////
// ///////////// API Implementation///////////////////
// ///////////////////////////////////////////////////
Expand Down
3 changes: 3 additions & 0 deletions server/src/main/java/com/cloud/vm/UserVmManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ public interface UserVmManager extends UserVmService {
Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long hostId, Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;

Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId, Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException;

boolean upgradeVirtualMachine(Long id, Long serviceOfferingId, Map<String, String> customParameters) throws ResourceUnavailableException,
ConcurrentOperationException, ManagementServerException,
VirtualMachineMigrationException;
Expand Down
119 changes: 100 additions & 19 deletions server/src/main/java/com/cloud/vm/UserVmManagerImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,6 @@
import javax.inject.Inject;
import javax.naming.ConfigurationException;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import org.apache.cloudstack.acl.ControlledEntity.ACLType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
import org.apache.cloudstack.affinity.AffinityGroupService;
Expand Down Expand Up @@ -96,6 +91,10 @@
import org.apache.cloudstack.storage.datastore.db.StoragePoolVO;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreDao;
import org.apache.cloudstack.storage.datastore.db.TemplateDataStoreVO;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.collections.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

import com.cloud.agent.AgentManager;
import com.cloud.agent.api.Answer;
Expand Down Expand Up @@ -134,6 +133,7 @@
import com.cloud.dc.DataCenterVO;
import com.cloud.dc.DedicatedResourceVO;
import com.cloud.dc.HostPodVO;
import com.cloud.dc.Pod;
import com.cloud.dc.Vlan;
import com.cloud.dc.Vlan.VlanType;
import com.cloud.dc.VlanVO;
Expand Down Expand Up @@ -2685,7 +2685,7 @@ protected boolean applyUserData(HypervisorType hyperVisorType, UserVm vm, Nic ni
@Override
@ActionEvent(eventType = EventTypes.EVENT_VM_START, eventDescription = "starting Vm", async = true)
public UserVm startVirtualMachine(StartVMCmd cmd) throws ExecutionException, ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
return startVirtualMachine(cmd.getId(), cmd.getHostId(), null, cmd.getDeploymentPlanner()).first();
return startVirtualMachine(cmd.getId(), cmd.getPodId(), cmd.getClusterId(), cmd.getHostId(), null, cmd.getDeploymentPlanner()).first();
}

@Override
Expand Down Expand Up @@ -4051,12 +4051,14 @@ private UserVm startVirtualMachine(DeployVMCmd cmd, Map<VirtualMachineProfile.Pa
InsufficientCapacityException, ConcurrentOperationException {

long vmId = cmd.getEntityId();
Long podId = cmd.getPodId();
Long clusterId = cmd.getClusterId();
Long hostId = cmd.getHostId();
UserVmVO vm = _vmDao.findById(vmId);

Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> vmParamPair = null;
try {
vmParamPair = startVirtualMachine(vmId, hostId, additonalParams, deploymentPlannerToUse);
vmParamPair = startVirtualMachine(vmId, podId, clusterId, hostId, additonalParams, deploymentPlannerToUse);
vm = vmParamPair.first();

// At this point VM should be in "Running" state
Expand Down Expand Up @@ -4381,6 +4383,12 @@ public void finalizeStop(VirtualMachineProfile profile, Answer answer) {
@Override
public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long hostId, Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
return startVirtualMachine(vmId, null, null, hostId, additionalParams, deploymentPlannerToUse);
}

@Override
public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMachine(long vmId, Long podId, Long clusterId, Long hostId, Map<VirtualMachineProfile.Param, Object> additionalParams, String deploymentPlannerToUse)
throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException {
// Input validation
Account callerAccount = CallContext.current().getCallingAccount();
UserVO callerUser = _userDao.findById(CallContext.current().getCallingUserId());
Expand All @@ -4407,18 +4415,14 @@ public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMach
throw new PermissionDeniedException("The owner of " + vm + " is disabled: " + vm.getAccountId());
}

Host destinationHost = null;
if (hostId != null) {
Account account = CallContext.current().getCallingAccount();
if (!_accountService.isRootAdmin(account.getId())) {
throw new PermissionDeniedException(
"Parameter hostid can only be specified by a Root Admin, permission denied");
}
destinationHost = _hostDao.findById(hostId);
if (destinationHost == null) {
throw new InvalidParameterValueException("Unable to find the host to deploy the VM, host id=" + hostId);
}
}
Account account = CallContext.current().getCallingAccount();
boolean isRootAdmin = _accountService.isRootAdmin(account.getId());

Pod destinationPod = getDestinationPod(podId, isRootAdmin);
Cluster destinationCluster = getDestinationCluster(clusterId, isRootAdmin);
Host destinationHost = getDestinationHost(hostId, isRootAdmin);

validateDeploymentDestination(destinationPod, destinationCluster, destinationHost);

// check if vm is security group enabled
if (_securityGroupMgr.isVmSecurityGroupEnabled(vmId) && _securityGroupMgr.getSecurityGroupsForVm(vmId).isEmpty()
Expand All @@ -4444,6 +4448,18 @@ public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMach
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
} else if (destinationCluster != null) {
s_logger.debug("Destination Cluster to deploy the VM is specified, specifying a deployment plan to deploy the VM");
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationCluster.getPodId(), destinationCluster.getId(), null, null, null);
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
} else if (destinationPod != null) {
s_logger.debug("Destination Pod to deploy the VM is specified, specifying a deployment plan to deploy the VM");
plan = new DataCenterDeployment(vm.getDataCenterId(), destinationPod.getId(), null, null, null, null);
if (!AllowDeployVmIfGivenHostFails.value()) {
deployOnGivenHost = true;
}
}

// Set parameters
Expand Down Expand Up @@ -4510,6 +4526,71 @@ public Pair<UserVmVO, Map<VirtualMachineProfile.Param, Object>> startVirtualMach
return vmParamPair;
}

private void validateDeploymentDestination(Pod destinationPod, Cluster destinationCluster, Host destinationHost) {

if (destinationHost != null) {
if (destinationCluster != null) {
if (destinationHost.getClusterId() != destinationCluster.getId()) {
throw new InvalidParameterValueException("Host " + destinationHost.getId() + " is not a child of provided cluster " + destinationCluster.getId());
}
if (destinationPod != null) {
if (destinationCluster.getPodId() != destinationPod.getId()) {
throw new InvalidParameterValueException("Cluster " + destinationCluster.getId() + " is not a child of provided pod " + destinationPod.getId());
}
}
}
}
}

private Pod getDestinationPod(Long podId, boolean isRootAdmin) {

Pod destinationPod = null;
if (podId != null) {
if (!isRootAdmin) {
throw new PermissionDeniedException(
"Parameter " + ApiConstants.POD_ID + " can only be specified by a Root Admin, permission denied");
}
destinationPod = _podDao.findById(podId);
if (destinationPod == null) {
throw new InvalidParameterValueException("Unable to find the pod to deploy the VM, pod id=" + podId);
}
}
return destinationPod;
}

private Cluster getDestinationCluster(Long clusterId, boolean isRootAdmin) {

Cluster destinationCluster = null;
if (clusterId != null) {
if (!isRootAdmin) {
throw new PermissionDeniedException(
"Parameter " + ApiConstants.CLUSTER_ID + " can only be specified by a Root Admin, permission denied");
}
destinationCluster = _clusterDao.findById(clusterId);
if (destinationCluster == null) {
throw new InvalidParameterValueException("Unable to find the cluster to deploy the VM, cluster id=" + clusterId);
}
}
return destinationCluster;
}

private Host getDestinationHost(Long hostId, boolean isRootAdmin) {

Host destinationHost = null;
if (hostId != null) {

if (!isRootAdmin) {
throw new PermissionDeniedException(
"Parameter " + ApiConstants.HOST_ID + " can only be specified by a Root Admin, permission denied");
}
destinationHost = _hostDao.findById(hostId);
if (destinationHost == null) {
throw new InvalidParameterValueException("Unable to find the host to deploy the VM, host id=" + hostId);
}
}
return destinationHost;
}

@Override
public UserVm destroyVm(long vmId, boolean expunge) throws ResourceUnavailableException, ConcurrentOperationException {
// Account caller = CallContext.current().getCallingAccount();
Expand Down
37 changes: 35 additions & 2 deletions ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -97,15 +97,48 @@
<div class="step setup" wizard-step-id="setup">
<div class="content">
<!-- Select a zone -->
<div class="section select-zone">
<div class="section select-deployment">
<h3><translate key="label.select.a.zone"/></h3>
<p><translate key="message.select.a.zone"/></p>
<div class="select-area">
<div class="desc"></div>
<select name="zoneid" class="required">
<select name="zoneid" class="zoneid required">
</select>
<select name="podid" class="podid">
</select>
<select name="clusterid" class="clusterid">
</select>
<select name="hostid" class="hostid">
</select>
</div>
</div>
<!--<div class="section select-pod">-->
<!--<h3><translate key="label.select.a.pod"/></h3>-->
<!--&lt;!&ndash;<p><translate/></p>&ndash;&gt;-->
<!--<div class="select-area">-->
<!--<div class="desc"></div>-->
<!--<select name="podid">-->
<!--</select>-->
<!--</div>-->
<!--</div>-->
<!--<div class="section select-cluster">-->
<!--<h3><translate key="label.select.a.cluster"/></h3>-->
<!--&lt;!&ndash;<p><translate/></p>&ndash;&gt;-->
<!--<div class="select-area">-->
<!--<div class="desc"></div>-->
<!--<select name="clusterid">-->
<!--</select>-->
<!--</div>-->
<!--</div>-->
<!--<div class="section select-host">-->
<!--<h3><translate key="label.select.a.host"/></h3>-->
<!--&lt;!&ndash;<p><translate/></p>&ndash;&gt;-->
<!--<div class="select-area">-->
<!--<div class="desc"></div>-->
<!--<select name="hostid">-->
<!--</select>-->
<!--</div>-->
<!--</div>-->
<!-- Select template -->
<div class="section select-template">
<h3><translate key="label.select.iso.or.template" /></h3>
Expand Down
Loading