Merge pull request #1297 from DaanHoogland/CLOUDSTACK-9203

CLOUDSTACK-9203 Implement security group move on updateVM API call  cherry-picked from a exoscale internal fix

Conflicts:
	api/src/org/apache/cloudstack/api/command/user/vm/UpdateVMCmd.java
	server/src/com/cloud/vm/UserVmManager.java
	server/src/com/cloud/vm/UserVmManagerImpl.java

* pr/1297:
  CLOUDSTACK-9203 refactorred DeployVM code to be used by UpdateVM as well
  CLOUDSTACK-9203 security group update on running instance

Signed-off-by: Will Stevens <williamstevens@gmail.com>
This commit is contained in:
Will Stevens 2016-05-25 22:59:35 -04:00
commit de205c5805
5 changed files with 309 additions and 186 deletions

View File

@ -16,25 +16,14 @@
// under the License.
package org.apache.cloudstack.api.command.user.vm;
import com.cloud.dc.DataCenter;
import com.cloud.dc.DataCenter.NetworkType;
import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.Network;
import com.cloud.network.Network.IpAddresses;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.ServiceOffering;
import com.cloud.template.VirtualMachineTemplate;
import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.VirtualMachine;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.affinity.AffinityGroupResponse;
import org.apache.cloudstack.api.ACL;
@ -59,17 +48,23 @@ import org.apache.cloudstack.api.response.ZoneResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.log4j.Logger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import com.cloud.event.EventTypes;
import com.cloud.exception.ConcurrentOperationException;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.InsufficientServerCapacityException;
import com.cloud.exception.InvalidParameterValueException;
import com.cloud.exception.ResourceAllocationException;
import com.cloud.exception.ResourceUnavailableException;
import com.cloud.hypervisor.Hypervisor.HypervisorType;
import com.cloud.network.Network;
import com.cloud.network.Network.IpAddresses;
import com.cloud.uservm.UserVm;
import com.cloud.utils.net.NetUtils;
import com.cloud.vm.VirtualMachine;
@APICommand(name = "deployVirtualMachine", description = "Creates and automatically starts a virtual machine based on a service offering, disk offering, and template.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd implements SecurityGroupAction {
public static final Logger s_logger = Logger.getLogger(DeployVMCmd.class.getName());
private static final String s_name = "deployvirtualmachineresponse";
@ -257,26 +252,12 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
return displayVm;
}
public List<Long> getSecurityGroupIdList() {
if (securityGroupNameList != null && securityGroupIdList != null) {
throw new InvalidParameterValueException("securitygroupids parameter is mutually exclusive with securitygroupnames parameter");
}
public List<String> getSecurityGroupNameList() {
return securityGroupNameList;
}
//transform group names to ids here
if (securityGroupNameList != null) {
List<Long> securityGroupIds = new ArrayList<Long>();
for (String groupName : securityGroupNameList) {
Long groupId = _responseGenerator.getSecurityGroupId(groupName, getEntityOwnerId());
if (groupId == null) {
throw new InvalidParameterValueException("Unable to find group by name " + groupName);
} else {
securityGroupIds.add(groupId);
}
}
return securityGroupIds;
} else {
return securityGroupIdList;
}
public List<Long> getSecurityGroupIdList() {
return securityGroupIdList;
}
public Long getServiceOfferingId() {
@ -328,7 +309,7 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
return startVm == null ? true : startVm;
}
private Map<Long, IpAddresses> getIpToNetworkMap() {
public Map<Long, IpAddresses> getIpToNetworkMap() {
if ((networkIds != null || ipAddress != null || getIp6Address() != null) && ipToNetworkList != null) {
throw new InvalidParameterValueException("NetworkIds and ipAddress can't be specified along with ipToNetworkMap parameter");
}
@ -363,6 +344,10 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
return ipToNetworkMap;
}
public String getIpAddress() {
return ipAddress;
}
public String getIp6Address() {
if (ip6Address == null) {
return null;
@ -392,6 +377,11 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
}
}
public String getKeyboard() {
// TODO Auto-generated method stub
return keyboard;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////
@ -478,137 +468,11 @@ public class DeployVMCmd extends BaseAsyncCreateCustomIdCmd {
}
}
// this is an opportunity to verify that parameters that came in via the Details Map are OK
// for example, minIops and maxIops should either both be specified or neither be specified and,
// if specified, minIops should be <= maxIops
private void verifyDetails() {
Map<String, String> map = getDetails();
if (map != null) {
String minIops = (String)map.get("minIops");
String maxIops = (String)map.get("maxIops");
verifyMinAndMaxIops(minIops, maxIops);
minIops = (String)map.get("minIopsDo");
maxIops = (String)map.get("maxIopsDo");
verifyMinAndMaxIops(minIops, maxIops);
}
}
private void verifyMinAndMaxIops(String minIops, String maxIops) {
if ((minIops != null && maxIops == null) || (minIops == null && maxIops != null)) {
throw new InvalidParameterValueException("Either 'Min IOPS' and 'Max IOPS' must both be specified or neither be specified.");
}
long lMinIops;
try {
if (minIops != null) {
lMinIops = Long.parseLong(minIops);
}
else {
lMinIops = 0;
}
}
catch (NumberFormatException ex) {
throw new InvalidParameterValueException("'Min IOPS' must be a whole number.");
}
long lMaxIops;
try {
if (maxIops != null) {
lMaxIops = Long.parseLong(maxIops);
}
else {
lMaxIops = 0;
}
}
catch (NumberFormatException ex) {
throw new InvalidParameterValueException("'Max IOPS' must be a whole number.");
}
if (lMinIops > lMaxIops) {
throw new InvalidParameterValueException("'Min IOPS' must be less than or equal to 'Max IOPS'.");
}
}
@Override
public void create() throws ResourceAllocationException {
try {
//Verify that all objects exist before passing them to the service
Account owner = _accountService.getActiveAccountById(getEntityOwnerId());
verifyDetails();
DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Unable to find zone by id=" + zoneId);
}
ServiceOffering serviceOffering = _entityMgr.findById(ServiceOffering.class, serviceOfferingId);
if (serviceOffering == null) {
throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId);
}
if(!serviceOffering.isDynamic()) {
for(String detail: getDetails().keySet()) {
if(detail.equalsIgnoreCase("cpuNumber") || detail.equalsIgnoreCase("cpuSpeed") || detail.equalsIgnoreCase("memory")) {
throw new InvalidParameterValueException("cpuNumber or cpuSpeed or memory should not be specified for static service offering");
}
}
}
VirtualMachineTemplate template = _entityMgr.findById(VirtualMachineTemplate.class, templateId);
// Make sure a valid template ID was specified
if (template == null) {
throw new InvalidParameterValueException("Unable to find the template " + templateId);
}
DiskOffering diskOffering = null;
if (diskOfferingId != null) {
diskOffering = _entityMgr.findById(DiskOffering.class, diskOfferingId);
if (diskOffering == null) {
throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId);
}
}
if (!zone.isLocalStorageEnabled()) {
if (serviceOffering.getUseLocalStorage()) {
throw new InvalidParameterValueException("Zone is not configured to use local storage but service offering " + serviceOffering.getName() + " uses it");
}
if (diskOffering != null && diskOffering.getUseLocalStorage()) {
throw new InvalidParameterValueException("Zone is not configured to use local storage but disk offering " + diskOffering.getName() + " uses it");
}
}
UserVm vm = null;
IpAddresses addrs = new IpAddresses(ipAddress, getIp6Address());
if (zone.getNetworkType() == NetworkType.Basic) {
if (getNetworkIds() != null) {
throw new InvalidParameterValueException("Can't specify network Ids in Basic zone");
} else {
vm = _userVmService.createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(), owner, name, displayName, diskOfferingId,
size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(),
getDetails(), getCustomId());
}
} else {
if (zone.isSecurityGroupEnabled()) {
vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, getNetworkIds(), getSecurityGroupIdList(), owner, name,
displayName, diskOfferingId, size, group, getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard,
getAffinityGroupIdList(), getDetails(), getCustomId());
} else {
if (getSecurityGroupIdList() != null && !getSecurityGroupIdList().isEmpty()) {
throw new InvalidParameterValueException("Can't create vm with security groups; security group feature is not enabled per zone");
}
vm = _userVmService.createAdvancedVirtualMachine(zone, serviceOffering, template, getNetworkIds(), owner, name, displayName, diskOfferingId, size, group,
getHypervisor(), getHttpMethod(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, displayVm, keyboard, getAffinityGroupIdList(), getDetails(),
getCustomId());
}
}
UserVm vm = _userVmService.createVirtualMachine(this);
if (vm != null) {
setEntityId(vm.getId());

View File

@ -0,0 +1,25 @@
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
package org.apache.cloudstack.api.command.user.vm;
import java.util.List;
public interface SecurityGroupAction {
List<Long> getSecurityGroupIdList();
List<String> getSecurityGroupNameList();
long getEntityOwnerId();
}

View File

@ -16,7 +16,9 @@
// under the License.
package org.apache.cloudstack.api.command.user.vm;
import org.apache.log4j.Logger;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.cloudstack.acl.RoleType;
import org.apache.cloudstack.acl.SecurityChecker.AccessType;
@ -29,8 +31,10 @@ import org.apache.cloudstack.api.Parameter;
import org.apache.cloudstack.api.ResponseObject.ResponseView;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.response.GuestOSResponse;
import org.apache.cloudstack.api.response.SecurityGroupResponse;
import org.apache.cloudstack.api.response.UserVmResponse;
import org.apache.cloudstack.context.CallContext;
import org.apache.log4j.Logger;
import com.cloud.exception.InsufficientCapacityException;
import com.cloud.exception.ResourceUnavailableException;
@ -38,14 +42,11 @@ import com.cloud.user.Account;
import com.cloud.uservm.UserVm;
import com.cloud.vm.VirtualMachine;
import java.util.Collection;
import java.util.Map;
@APICommand(name = "updateVirtualMachine", description="Updates properties of a virtual machine. The VM has to be stopped and restarted for the " +
"new properties to take effect. UpdateVirtualMachine does not first check whether the VM is stopped. " +
"Therefore, stop the VM manually before issuing this call.", responseObject = UserVmResponse.class, responseView = ResponseView.Restricted, entityType = {VirtualMachine.class},
requestHasSensitiveInfo = false, responseHasSensitiveInfo = true)
public class UpdateVMCmd extends BaseCustomIdCmd {
public class UpdateVMCmd extends BaseCustomIdCmd implements SecurityGroupAction {
public static final Logger s_logger = Logger.getLogger(UpdateVMCmd.class.getName());
private static final String s_name = "updatevirtualmachineresponse";
@ -96,6 +97,25 @@ public class UpdateVMCmd extends BaseCustomIdCmd {
@Parameter(name = ApiConstants.DETAILS, type = CommandType.MAP, description = "Details in key/value pairs.")
protected Map<String, String> details;
@ACL
@Parameter(name = ApiConstants.SECURITY_GROUP_IDS,
type = CommandType.LIST,
collectionType = CommandType.UUID,
entityType = SecurityGroupResponse.class,
description = "list of security group ids to be applied on the virtual machine.")
private List<Long> securityGroupIdList;
@ACL
@Parameter(name = ApiConstants.SECURITY_GROUP_NAMES,
type = CommandType.LIST,
collectionType = CommandType.STRING,
entityType = SecurityGroupResponse.class,
description = "comma separated list of security groups names that going to be applied to the virtual machine. " +
"Should be passed only when vm is created from a zone with Basic Network support. " +
"Mutually exclusive with securitygroupids parameter"
)
private List<String> securityGroupNameList;
/////////////////////////////////////////////////////
/////////////////// Accessors ///////////////////////
/////////////////////////////////////////////////////
@ -145,7 +165,15 @@ public class UpdateVMCmd extends BaseCustomIdCmd {
return (Map<String, String>) (paramsCollection.toArray())[0];
}
/////////////////////////////////////////////////////
public List<Long> getSecurityGroupIdList() {
return securityGroupIdList;
}
public List<String> getSecurityGroupNameList() {
return securityGroupNameList;
}
/////////////////////////////////////////////////////
/////////////// API Implementation///////////////////
/////////////////////////////////////////////////////

View File

@ -103,7 +103,7 @@ public interface UserVmManager extends UserVmService {
void collectVmDiskStatistics(UserVmVO userVm);
UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, Boolean isDisplayVmEnabled, Long osTypeId, String userData,
Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName) throws ResourceUnavailableException, InsufficientCapacityException;
Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List<Long> securityGroupIdList) throws ResourceUnavailableException, InsufficientCapacityException;
//the validateCustomParameters, save and remove CustomOfferingDetils functions can be removed from the interface once we can
//find a common place for all the scaling and upgrading code of both user and systemvms.

View File

@ -55,6 +55,7 @@ import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd;
import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd;
import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
import org.apache.cloudstack.api.command.user.vm.SecurityGroupAction;
import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
@ -194,6 +195,7 @@ import com.cloud.network.security.dao.SecurityGroupDao;
import com.cloud.network.security.dao.SecurityGroupVMMapDao;
import com.cloud.network.vpc.VpcManager;
import com.cloud.network.vpc.dao.VpcDao;
import com.cloud.offering.DiskOffering;
import com.cloud.offering.NetworkOffering;
import com.cloud.offering.NetworkOffering.Availability;
import com.cloud.offering.ServiceOffering;
@ -2289,6 +2291,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
String hostName = cmd.getHostName();
Map<String,String> details = cmd.getDetails();
Account caller = CallContext.current().getCallingAccount();
List<Long> securityGroupIdList = getSecurityGroupIdList(cmd);
// Input validation and permission checks
UserVmVO vmInstance = _vmDao.findById(id);
@ -2339,7 +2342,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
}
return updateVirtualMachine(id, displayName, group, ha, isDisplayVm, osTypeId, userData, isDynamicallyScalable,
cmd.getHttpMethod(), cmd.getCustomId(), hostName, cmd.getInstanceName());
cmd.getHttpMethod(), cmd.getCustomId(), hostName, cmd.getInstanceName(), securityGroupIdList);
}
private void saveUsageEvent(UserVmVO vm) {
@ -2389,10 +2392,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
@Override
public UserVm updateVirtualMachine(long id, String displayName, String group, Boolean ha, Boolean isDisplayVmEnabled, Long osTypeId, String userData,
Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName) throws ResourceUnavailableException, InsufficientCapacityException {
Boolean isDynamicallyScalable, HTTPMethod httpMethod, String customId, String hostName, String instanceName, List<Long> securityGroupIdList)
throws ResourceUnavailableException, InsufficientCapacityException {
UserVmVO vm = _vmDao.findById(id);
if (vm == null) {
throw new CloudRuntimeException("Unable to find virual machine with id " + id);
throw new CloudRuntimeException("Unable to find virtual machine with id " + id);
}
if(instanceName != null){
@ -2451,6 +2455,44 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
isDynamicallyScalable = vm.isDynamicallyScalable();
}
boolean isVMware = (vm.getHypervisorType() == HypervisorType.VMware);
if (securityGroupIdList != null && isVMware) {
throw new InvalidParameterValueException("Security group feature is not supported for vmWare hypervisor");
} else {
// Get default guest network in Basic zone
Network defaultNetwork = null;
try {
DataCenterVO zone = _dcDao.findById(vm.getDataCenterId());
if (zone.getNetworkType() == NetworkType.Basic) {
// Get default guest network in Basic zone
defaultNetwork = _networkModel.getExclusiveGuestNetwork(zone.getId());
} else if (zone.isSecurityGroupEnabled()) {
NicVO defaultNic = _nicDao.findDefaultNicForVM(vm.getId());
if (defaultNic != null) {
defaultNetwork = _networkDao.findById(defaultNic.getNetworkId());
}
}
} catch (InvalidParameterValueException e) {
if(s_logger.isDebugEnabled()) {
s_logger.debug(e.getMessage(),e);
}
defaultNetwork = _networkModel.getDefaultNetworkForVm(id);
}
if (securityGroupIdList != null && _networkModel.isSecurityGroupSupportedInNetwork(defaultNetwork) && _networkModel.canAddDefaultSecurityGroup()) {
if (vm.getState() == State.Stopped) {
// Remove instance from security groups
_securityGroupMgr.removeInstanceFromGroups(id);
// Add instance in provided groups
_securityGroupMgr.addInstanceToGroups(id, securityGroupIdList);
} else {
throw new InvalidParameterValueException("Virtual machine must be stopped prior to update security groups ");
}
}
}
if (hostName != null) {
// Check is hostName is RFC compliant
checkNameForRFCCompliance(hostName);
@ -4280,8 +4322,172 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Vir
@Override
public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException,
StorageUnavailableException, ResourceAllocationException {
// TODO Auto-generated method stub
return null;
//Verify that all objects exist before passing them to the service
Account owner = _accountService.getActiveAccountById(cmd.getEntityOwnerId());
verifyDetails(cmd.getDetails());
Long zoneId = cmd.getZoneId();
DataCenter zone = _entityMgr.findById(DataCenter.class, zoneId);
if (zone == null) {
throw new InvalidParameterValueException("Unable to find zone by id=" + zoneId);
}
Long serviceOfferingId = cmd.getServiceOfferingId();
ServiceOffering serviceOffering = _entityMgr.findById(ServiceOffering.class, serviceOfferingId);
if (serviceOffering == null) {
throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId);
}
Long templateId = cmd.getTemplateId();
if(!serviceOffering.isDynamic()) {
for(String detail: cmd.getDetails().keySet()) {
if(detail.equalsIgnoreCase("cpuNumber") || detail.equalsIgnoreCase("cpuSpeed") || detail.equalsIgnoreCase("memory")) {
throw new InvalidParameterValueException("cpuNumber or cpuSpeed or memory should not be specified for static service offering");
}
}
}
VirtualMachineTemplate template = _entityMgr.findById(VirtualMachineTemplate.class, templateId);
// Make sure a valid template ID was specified
if (template == null) {
throw new InvalidParameterValueException("Unable to use template " + templateId);
}
Long diskOfferingId = cmd.getDiskOfferingId();
DiskOffering diskOffering = null;
if (diskOfferingId != null) {
diskOffering = _entityMgr.findById(DiskOffering.class, diskOfferingId);
if (diskOffering == null) {
throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId);
}
}
if (!zone.isLocalStorageEnabled()) {
if (serviceOffering.getUseLocalStorage()) {
throw new InvalidParameterValueException("Zone is not configured to use local storage but service offering " + serviceOffering.getName() + " uses it");
}
if (diskOffering != null && diskOffering.getUseLocalStorage()) {
throw new InvalidParameterValueException("Zone is not configured to use local storage but disk offering " + diskOffering.getName() + " uses it");
}
}
String ipAddress = cmd.getIpAddress();
String ip6Address = cmd.getIp6Address();
String name = cmd.getName();
String displayName = cmd.getDisplayName();
UserVm vm = null;
IpAddresses addrs = new IpAddresses(ipAddress, ip6Address);
Long size = cmd.getSize();
String group = cmd.getGroup();
String userData = cmd.getUserData();
String sshKeyPairName = cmd.getSSHKeyPairName();
Boolean displayVm = cmd.getDisplayVm();
String keyboard = cmd.getKeyboard();
if (zone.getNetworkType() == NetworkType.Basic) {
if (cmd.getNetworkIds() != null) {
throw new InvalidParameterValueException("Can't specify network Ids in Basic zone");
} else {
vm = createBasicSecurityGroupVirtualMachine(zone, serviceOffering, template, getSecurityGroupIdList(cmd), owner, name, displayName, diskOfferingId,
size , group , cmd.getHypervisor(), cmd.getHttpMethod(), userData , sshKeyPairName , cmd.getIpToNetworkMap(), addrs, displayVm , keyboard , cmd.getAffinityGroupIdList(),
cmd.getDetails(), cmd.getCustomId());
}
} else {
if (zone.isSecurityGroupEnabled()) {
vm = createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, cmd.getNetworkIds(), getSecurityGroupIdList(cmd), owner, name,
displayName, diskOfferingId, size, group, cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard,
cmd.getAffinityGroupIdList(), cmd.getDetails(), cmd.getCustomId());
} else {
if (cmd.getSecurityGroupIdList() != null && !cmd.getSecurityGroupIdList().isEmpty()) {
throw new InvalidParameterValueException("Can't create vm with security groups; security group feature is not enabled per zone");
}
vm = createAdvancedVirtualMachine(zone, serviceOffering, template, cmd.getNetworkIds(), owner, name, displayName, diskOfferingId, size, group,
cmd.getHypervisor(), cmd.getHttpMethod(), userData, sshKeyPairName, cmd.getIpToNetworkMap(), addrs, displayVm, keyboard, cmd.getAffinityGroupIdList(), cmd.getDetails(),
cmd.getCustomId());
}
}
return vm;
}
private List<Long> getSecurityGroupIdList(SecurityGroupAction cmd) {
if (cmd.getSecurityGroupNameList() != null && cmd.getSecurityGroupIdList() != null) {
throw new InvalidParameterValueException("securitygroupids parameter is mutually exclusive with securitygroupnames parameter");
}
//transform group names to ids here
if (cmd.getSecurityGroupNameList() != null) {
List<Long> securityGroupIds = new ArrayList<Long>();
for (String groupName : cmd.getSecurityGroupNameList()) {
SecurityGroup sg = _securityGroupMgr.getSecurityGroup(groupName, cmd.getEntityOwnerId());
if (sg == null) {
throw new InvalidParameterValueException("Unable to find group by name " + groupName);
} else {
securityGroupIds.add(sg.getId());
}
}
return securityGroupIds;
} else {
return cmd.getSecurityGroupIdList();
}
}
// this is an opportunity to verify that parameters that came in via the Details Map are OK
// for example, minIops and maxIops should either both be specified or neither be specified and,
// if specified, minIops should be <= maxIops
private void verifyDetails(Map<String,String> details) {
if (details != null) {
String minIops = (String)details.get("minIops");
String maxIops = (String)details.get("maxIops");
verifyMinAndMaxIops(minIops, maxIops);
minIops = (String)details.get("minIopsDo");
maxIops = (String)details.get("maxIopsDo");
verifyMinAndMaxIops(minIops, maxIops);
}
}
private void verifyMinAndMaxIops(String minIops, String maxIops) {
if ((minIops != null && maxIops == null) || (minIops == null && maxIops != null)) {
throw new InvalidParameterValueException("Either 'Min IOPS' and 'Max IOPS' must both be specified or neither be specified.");
}
long lMinIops;
try {
if (minIops != null) {
lMinIops = Long.parseLong(minIops);
}
else {
lMinIops = 0;
}
}
catch (NumberFormatException ex) {
throw new InvalidParameterValueException("'Min IOPS' must be a whole number.");
}
long lMaxIops;
try {
if (maxIops != null) {
lMaxIops = Long.parseLong(maxIops);
}
else {
lMaxIops = 0;
}
}
catch (NumberFormatException ex) {
throw new InvalidParameterValueException("'Max IOPS' must be a whole number.");
}
if (lMinIops > lMaxIops) {
throw new InvalidParameterValueException("'Min IOPS' must be less than or equal to 'Max IOPS'.");
}
}
@Override