diff --git a/api/src/com/cloud/api/ApiConstants.java b/api/src/com/cloud/api/ApiConstants.java index 08fe4176ec6..db4c90d3a50 100755 --- a/api/src/com/cloud/api/ApiConstants.java +++ b/api/src/com/cloud/api/ApiConstants.java @@ -194,5 +194,6 @@ public class ApiConstants { public static final String NETWORKRATE = "networkrate"; public static final String GUEST_IP_TYPE = "guestiptype"; public static final String HOST_TAGS = "hosttags"; + public static final String SSH_KEYPAIR = "keypair"; } diff --git a/api/src/com/cloud/api/commands/DeployVMCmd.java b/api/src/com/cloud/api/commands/DeployVMCmd.java index 661bceab051..ef830ecdb67 100644 --- a/api/src/com/cloud/api/commands/DeployVMCmd.java +++ b/api/src/com/cloud/api/commands/DeployVMCmd.java @@ -30,12 +30,17 @@ import com.cloud.api.Parameter; import com.cloud.api.ServerApiException; import com.cloud.api.response.UserVmResponse; import com.cloud.async.AsyncJob; +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.InvalidParameterValueException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.offering.ServiceOffering; +import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.user.UserContext; import com.cloud.uservm.UserVm; @@ -50,51 +55,54 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { //////////////// API parameters ///////////////////// ///////////////////////////////////////////////////// - @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="an optional account for the virtual machine. Must be used with domainId.") - private String accountName; - - @Parameter(name=ApiConstants.DISK_OFFERING_ID, type=CommandType.LONG, description="the ID of the disk offering for the virtual machine. If the template is of ISO format, the diskOfferingId is for the root disk volume. Otherwise this parameter is used to dinidcate the offering for the data disk volume. If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") - private Long diskOfferingId; - - @Parameter(name=ApiConstants.DISPLAY_NAME, type=CommandType.STRING, description="an optional user generated name for the virtual machine") - private String displayName; + @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=true, description="availability zone for the virtual machine") + private Long zoneId; + + @Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine") + private Long serviceOfferingId; + + @Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine") + private Long templateId; @Parameter(name=ApiConstants.NAME, type=CommandType.STRING, description="host name for the virtual machine") private String name; - + + @Parameter(name=ApiConstants.DISPLAY_NAME, type=CommandType.STRING, description="an optional user generated name for the virtual machine") + private String displayName; + + //Owner information + @Parameter(name=ApiConstants.ACCOUNT, type=CommandType.STRING, description="an optional account for the virtual machine. Must be used with domainId.") + private String accountName; + @Parameter(name=ApiConstants.DOMAIN_ID, type=CommandType.LONG, description="an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used.") private Long domainId; + + //Network information + @Parameter(name=ApiConstants.NETWORK_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="list of network ids used by virtual machine") + private List networkIds; + + @Parameter(name=ApiConstants.SECURITY_GROUP_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="comma separated list of security groups id that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support") + private List securityGroupIdList; + + //DataDisk information + @Parameter(name=ApiConstants.DISK_OFFERING_ID, type=CommandType.LONG, description="the ID of the disk offering for the virtual machine. If the template is of ISO format, the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created.") + private Long diskOfferingId; + + @Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, description="the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") + private Long size; @Parameter(name=ApiConstants.GROUP, type=CommandType.STRING, description="an optional group for the virtual machine") private String group; @Parameter(name=ApiConstants.HYPERVISOR, type=CommandType.STRING, description="the hypervisor on which to deploy the virtual machine") private String hypervisor; - - @Parameter(name=ApiConstants.SECURITY_GROUP_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="comma separated list of security groups id that going to be applied to the virtual machine. Should be passed only when vm is created from a zone with Basic Network support") - private List securityGroupIdList; - - @Parameter(name=ApiConstants.SERVICE_OFFERING_ID, type=CommandType.LONG, required=true, description="the ID of the service offering for the virtual machine") - private Long serviceOfferingId; - - @Parameter(name=ApiConstants.SIZE, type=CommandType.LONG, description="the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId") - private Long size; - - @Parameter(name=ApiConstants.TEMPLATE_ID, type=CommandType.LONG, required=true, description="the ID of the template for the virtual machine") - private Long templateId; - + @Parameter(name=ApiConstants.USER_DATA, type=CommandType.STRING, description="an optional binary data that can be sent to the virtual machine upon a successful deployment. This binary data must be base64 encoded before adding it to the request. Currently only HTTP GET is supported. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding.") private String userData; - @Parameter(name=ApiConstants.ZONE_ID, type=CommandType.LONG, required=true, description="availability zone for the virtual machine") - private Long zoneId; - - @Parameter(name=ApiConstants.NETWORK_IDS, type=CommandType.LIST, collectionType=CommandType.LONG, description="list of network ids used by virtual machine") - private List networkIds; - - @Parameter(name="keypair", type=CommandType.STRING, description="name of the ssh key pair used to login to the virtual machine") + @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; - + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// ///////////////////////////////////////////////////// @@ -252,9 +260,50 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { @Override public void create() throws ResourceAllocationException{ try { - UserVm result = _userVmService.createVirtualMachine(this); - if (result != null){ - setEntityId(result.getId()); + + //Verify that all objects exist before passing them to the service + Account owner = _accountService.getActiveAccount(getAccountName(), getDomainId()); + if (owner == null) { + throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); + } + + DataCenter zone = _configService.getZone(zoneId); + if (zone == null) { + throw new InvalidParameterValueException("Unable to find zone by id=" + zoneId); + } + + ServiceOffering serviceOffering = _configService.getServiceOffering(serviceOfferingId); + if (serviceOffering == null) { + throw new InvalidParameterValueException("Unable to find service offering: " + serviceOfferingId); + } + + VirtualMachineTemplate template = _templateService.getTemplate(templateId); + // Make sure a valid template ID was specified + if (template == null) { + throw new InvalidParameterValueException("Unable to use template " + templateId); + } + + UserVm vm = null; + + 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, securityGroupIdList, owner, name, displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName); + } + } else { + if (zone.isSecurityGroupEnabled()) { + vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, getNetworkIds(), securityGroupIdList, owner, name, displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName); + } else { + if (securityGroupIdList != null && !securityGroupIdList.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(), userData, sshKeyPairName); + } + } + + if (vm != null){ + setEntityId(vm.getId()); } else { throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to deploy vm"); } diff --git a/api/src/com/cloud/configuration/ConfigurationService.java b/api/src/com/cloud/configuration/ConfigurationService.java index 899c87c32a6..57586af0aa4 100644 --- a/api/src/com/cloud/configuration/ConfigurationService.java +++ b/api/src/com/cloud/configuration/ConfigurationService.java @@ -187,5 +187,9 @@ public interface ConfigurationService { Account getVlanAccount(long vlanId); List listNetworkOfferings(TrafficType trafficType, boolean systemOnly); + + DataCenter getZone(long id); + + ServiceOffering getServiceOffering(long serviceOfferingId); } diff --git a/api/src/com/cloud/template/TemplateService.java b/api/src/com/cloud/template/TemplateService.java index 76ad1303d28..3513989ea7c 100755 --- a/api/src/com/cloud/template/TemplateService.java +++ b/api/src/com/cloud/template/TemplateService.java @@ -72,4 +72,6 @@ public interface TemplateService { * @throws InvalidParameterValueException, InternalErrorException, PermissionDeniedException */ Long extract(ExtractTemplateCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, InternalErrorException; + + VirtualMachineTemplate getTemplate(long templateId); } diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index 643d63761b9..d1e1e9b996a 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -35,6 +35,7 @@ import com.cloud.api.commands.ResetVMPasswordCmd; import com.cloud.api.commands.StartVMCmd; import com.cloud.api.commands.UpdateVMCmd; import com.cloud.api.commands.UpgradeVMCmd; +import com.cloud.dc.DataCenter; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; import com.cloud.exception.InvalidParameterValueException; @@ -42,8 +43,11 @@ import com.cloud.exception.PermissionDeniedException; import com.cloud.exception.ResourceAllocationException; import com.cloud.exception.ResourceUnavailableException; import com.cloud.exception.StorageUnavailableException; +import com.cloud.hypervisor.Hypervisor.HypervisorType; +import com.cloud.offering.ServiceOffering; import com.cloud.storage.Volume; import com.cloud.template.VirtualMachineTemplate; +import com.cloud.user.Account; import com.cloud.uservm.UserVm; import com.cloud.utils.exception.ExecutionException; @@ -115,19 +119,110 @@ public interface UserVmService { VirtualMachineTemplate createPrivateTemplate(CreateTemplateCmd cmd); /** - * Creates a User VM in the database and returns the VM to the caller. + * Creates a Basic Zone User VM in the database and returns the VM to the caller. * - * @param cmd Command to deploy. + * @param zone - availability zone for the virtual machine + * @param serviceOffering - the service offering for the virtual machine + * @param template - the template for the virtual machine + * @param securityGroupIdList - comma separated list of security groups id that going to be applied to the virtual machine + * @param accountName - an optional account for the virtual machine. Must be used with domainId + * @param domainId - an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used + * @param hostName - host name for the virtual machine + * @param displayName - an optional user generated name for the virtual machine + * @param diskOfferingId - the ID of the disk offering for the virtual machine. If the template is of ISO format, + the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. + If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. + If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created + * @param diskSize - the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId + * @param group - an optional group for the virtual machine + * @param hypervisor - the hypervisor on which to deploy the virtual machine + * @param userData - an optional binary data that can be sent to the virtual machine upon a successful deployment. + This binary data must be base64 encoded before adding it to the request. + Currently only HTTP GET is supported. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding + * @param sshKeyPair - name of the ssh key pair used to login to the virtual machine + * + * @return UserVm object if successful. + * + * @throws InsufficientCapacityException if there is insufficient capacity to deploy the VM. + * @throws ConcurrentOperationException if there are multiple users working on the same VM or in the same environment. + * @throws ResourceUnavailableException if the resources required to deploy the VM is not currently available. + * @throws InsufficientResourcesException + */ + UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List securityGroupIdList, Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, + HypervisorType hypervisor, String userData, String sshKeyPair) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; + + + /** + * Creates a User VM in Advanced Zone (Security Group feature is enabled) in the database and returns the VM to the caller. + * + * @param zone - availability zone for the virtual machine + * @param serviceOffering - the service offering for the virtual machine + * @param template - the template for the virtual machine + * @param networkIdList - list of network ids used by virtual machine + * @param securityGroupIdList - comma separated list of security groups id that going to be applied to the virtual machine + * @param accountName - an optional account for the virtual machine. Must be used with domainId + * @param domainId - an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used + * @param hostName - host name for the virtual machine + * @param displayName - an optional user generated name for the virtual machine + * @param diskOfferingId - the ID of the disk offering for the virtual machine. If the template is of ISO format, + the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. + If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. + If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created + * @param diskSize - the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId + * @param group - an optional group for the virtual machine + * @param hypervisor - the hypervisor on which to deploy the virtual machine + * @param userData - an optional binary data that can be sent to the virtual machine upon a successful deployment. + This binary data must be base64 encoded before adding it to the request. + Currently only HTTP GET is supported. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding + * @param sshKeyPair - name of the ssh key pair used to login to the virtual machine + * + * @return UserVm object if successful. + * + * @throws InsufficientCapacityException if there is insufficient capacity to deploy the VM. + * @throws ConcurrentOperationException if there are multiple users working on the same VM or in the same environment. + * @throws ResourceUnavailableException if the resources required to deploy the VM is not currently available. + * @throws InsufficientResourcesException + */ + UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, List securityGroupIdList, + Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, + HypervisorType hypervisor, String userData, String sshKeyPair) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; + + + /** + * Creates a User VM in Advanced Zone (Security Group feature is disabled) in the database and returns the VM to the caller. + * + * @param zone - availability zone for the virtual machine + * @param serviceOffering - the service offering for the virtual machine + * @param template - the template for the virtual machine + * @param networkIdList - list of network ids used by virtual machine + * @param accountName - an optional account for the virtual machine. Must be used with domainId + * @param domainId - an optional domainId for the virtual machine. If the account parameter is used, domainId must also be used + * @param hostName - host name for the virtual machine + * @param displayName - an optional user generated name for the virtual machine + * @param diskOfferingId - the ID of the disk offering for the virtual machine. If the template is of ISO format, + the diskOfferingId is for the root disk volume. Otherwise this parameter is used to indicate the offering for the data disk volume. + If the templateId parameter passed is from a Template object, the diskOfferingId refers to a DATA Disk Volume created. + If the templateId parameter passed is from an ISO object, the diskOfferingId refers to a ROOT Disk Volume created + * @param diskSize - the arbitrary size for the DATADISK volume. Mutually exclusive with diskOfferingId + * @param group - an optional group for the virtual machine + * @param hypervisor - the hypervisor on which to deploy the virtual machine + * @param userData - an optional binary data that can be sent to the virtual machine upon a successful deployment. + This binary data must be base64 encoded before adding it to the request. + Currently only HTTP GET is supported. Using HTTP GET (via querystring), you can send up to 2KB of data after base64 encoding + * @param sshKeyPair - name of the ssh key pair used to login to the virtual machine + * * @return UserVm object if successful. * * @throws InsufficientCapacityException if there is insufficient capacity to deploy the VM. * @throws ConcurrentOperationException if there are multiple users working on the same VM or in the same environment. * @throws ResourceUnavailableException if the resources required to deploy the VM is not currently available. * @throws InsufficientResourcesException - * @throws PermissionDeniedException if the caller doesn't have any access rights to the VM. - * @throws InvalidParameterValueException if the parameters are incorrect. */ - UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; + UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, Account owner, String hostName, + String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** * Starts the virtual machine created from createVirtualMachine. diff --git a/server/src/com/cloud/configuration/ConfigurationManager.java b/server/src/com/cloud/configuration/ConfigurationManager.java index 7e65c4162cf..194526cfd6d 100644 --- a/server/src/com/cloud/configuration/ConfigurationManager.java +++ b/server/src/com/cloud/configuration/ConfigurationManager.java @@ -19,7 +19,6 @@ package com.cloud.configuration; import java.util.List; -import com.cloud.api.commands.UpdatePodCmd; import com.cloud.dc.ClusterVO; import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenter.NetworkType; @@ -178,14 +177,13 @@ public interface ConfigurationManager extends ConfigurationService, Manager { void createDefaultNetworks(long zoneId, boolean isSecurityGroupEnabled) throws ConcurrentOperationException; - DataCenterVO getZone(long id); - HostPodVO getPod(long id); ClusterVO getCluster(long id); boolean deleteAccountSpecificVirtualRanges(long accountId); + DataCenterVO getZone(long id); /** * Edits a pod in the database. Will not allow you to edit pods that are being used anywhere in the system. diff --git a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java index 2236a7c3559..7744463a8e4 100755 --- a/server/src/com/cloud/configuration/ConfigurationManagerImpl.java +++ b/server/src/com/cloud/configuration/ConfigurationManagerImpl.java @@ -2766,4 +2766,9 @@ public class ConfigurationManagerImpl implements ConfigurationManager, Configura return false; } } + + @Override + public ServiceOffering getServiceOffering(long serviceOfferingId) { + return _serviceOfferingDao.findById(serviceOfferingId); + } } diff --git a/server/src/com/cloud/network/NetworkManager.java b/server/src/com/cloud/network/NetworkManager.java index aa905e101fa..39e095a9b82 100644 --- a/server/src/com/cloud/network/NetworkManager.java +++ b/server/src/com/cloud/network/NetworkManager.java @@ -125,7 +125,7 @@ public interface NetworkManager extends NetworkService { boolean applyRules(List rules, boolean continueOnError) throws ResourceUnavailableException; - Network getSystemNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType); + NetworkVO getSystemNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType); List getRemoteAccessVpnElements(); @@ -179,7 +179,7 @@ public interface NetworkManager extends NetworkService { boolean isServiceSupported(long networkId, Network.Service service); - Network getNetworkWithSecurityGroupEnabled(Long zoneId); + NetworkVO getNetworkWithSecurityGroupEnabled(Long zoneId); boolean startNetwork(long networkId, DeployDestination dest, ReservationContext context) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException; diff --git a/server/src/com/cloud/network/NetworkManagerImpl.java b/server/src/com/cloud/network/NetworkManagerImpl.java index b7999002652..6695b2cb9b2 100755 --- a/server/src/com/cloud/network/NetworkManagerImpl.java +++ b/server/src/com/cloud/network/NetworkManagerImpl.java @@ -2330,7 +2330,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } @Override - public Network getSystemNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType) { + public NetworkVO getSystemNetworkByZoneAndTrafficType(long zoneId, TrafficType trafficType) { // find system public network offering Long networkOfferingId = null; List offerings = _networkOfferingDao.listSystemNetworkOfferings(); @@ -2353,7 +2353,7 @@ public class NetworkManagerImpl implements NetworkManager, NetworkService, Manag } @Override - public Network getNetworkWithSecurityGroupEnabled(Long zoneId) { + public NetworkVO getNetworkWithSecurityGroupEnabled(Long zoneId) { List networks = _networksDao.listByZoneSecurityGroup(zoneId); if (networks == null || networks.isEmpty()) { return null; diff --git a/server/src/com/cloud/template/TemplateManagerImpl.java b/server/src/com/cloud/template/TemplateManagerImpl.java index 6e0a160ecda..c37b5534af4 100755 --- a/server/src/com/cloud/template/TemplateManagerImpl.java +++ b/server/src/com/cloud/template/TemplateManagerImpl.java @@ -1307,4 +1307,9 @@ public class TemplateManagerImpl implements TemplateManager, Manager, TemplateSe } return delete(userId, templateId, zoneId); } + + @Override + public VirtualMachineTemplate getTemplate(long templateId) { + return _tmpltDao.findById(templateId); + } } diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 4a2e2d818e9..fc135be4040 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -74,7 +74,7 @@ import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceLimitDao; -import com.cloud.dc.DataCenter.NetworkType; +import com.cloud.dc.DataCenter; import com.cloud.dc.DataCenterVO; import com.cloud.dc.HostPodVO; import com.cloud.dc.dao.AccountVlanMapDao; @@ -108,6 +108,7 @@ import com.cloud.network.IPAddressVO; import com.cloud.network.Network; import com.cloud.network.NetworkManager; import com.cloud.network.NetworkVO; +import com.cloud.network.Network.GuestIpType; import com.cloud.network.Networks.TrafficType; import com.cloud.network.dao.FirewallRulesDao; import com.cloud.network.dao.IPAddressDao; @@ -119,6 +120,7 @@ import com.cloud.network.router.VirtualNetworkApplianceManager; import com.cloud.network.rules.RulesManager; import com.cloud.network.security.SecurityGroupManager; import com.cloud.network.vpn.PasswordResetElement; +import com.cloud.offering.ServiceOffering; import com.cloud.offerings.dao.NetworkOfferingDao; import com.cloud.server.Criteria; import com.cloud.service.ServiceOfferingVO; @@ -1834,32 +1836,171 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager return true; } - @Override @DB @ActionEvent (eventType=EventTypes.EVENT_VM_CREATE, eventDescription="deploying Vm", create=true) - public UserVm createVirtualMachine(DeployVMCmd cmd) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { + @Override + public UserVm createBasicSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List securityGroupIdList, + Account owner, String hostName, String displayName, Long diskOfferingId, + Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + Account caller = UserContext.current().getCaller(); + List networkList = new ArrayList(); - String accountName = cmd.getAccountName(); - Long domainId = cmd.getDomainId(); - List networkList = cmd.getNetworkIds(); - String group = cmd.getGroup(); + //Verify that caller can perform actions in behalf of vm owner + _accountMgr.checkAccess(caller, owner); - Account owner = _accountDao.findActiveAccount(accountName, domainId); - if (owner == null) { - throw new InvalidParameterValueException("Unable to find account " + accountName + " in domain " + domainId); + //Get default guest network in Basic zone + NetworkVO defaultNetwork = _networkMgr.getSystemNetworkByZoneAndTrafficType(zone.getId(), TrafficType.Guest); + + if (defaultNetwork == null) { + throw new InvalidParameterValueException("Unable to find a default network to start a vm"); + } else { + networkList.add(defaultNetwork); } + return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, caller, diskOfferingId, + diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller); + } + + + @Override + public UserVm createAdvancedSecurityGroupVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, List securityGroupIdList, + Account owner, String hostName, String displayName, Long diskOfferingId, Long diskSize, String group, + HypervisorType hypervisor, String userData, String sshKeyPair) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + + Account caller = UserContext.current().getCaller(); + List networkList = new ArrayList(); + + //Verify that caller can perform actions in behalf of vm owner + _accountMgr.checkAccess(caller, owner); + + //If no network is specified, find system security group enabled network + if (networkIdList == null || networkIdList.isEmpty()) { + NetworkVO networkWithSecurityGroup = _networkMgr.getNetworkWithSecurityGroupEnabled(zone.getId()); + if (networkWithSecurityGroup == null) { + throw new InvalidParameterValueException("No network with security enabled is found in zone id=" + zone.getId()); + } + + networkList.add(networkWithSecurityGroup); + + } else if (securityGroupIdList != null && !securityGroupIdList.isEmpty()) { + //Only one network can be specified, and it should be security group enabled + if (networkIdList.size() > 1) { + throw new InvalidParameterValueException("Only support one network per VM if security group enabled"); + } + + NetworkVO network = _networkDao.findById(networkIdList.get(0).longValue()); + + if (network == null) { + throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); + } + + if (!network.isSecurityGroupEnabled()) { + throw new InvalidParameterValueException("Network is not security group enabled: " + network.getId()); + } + + networkList.add(network); + + } else { + //Verify that all the networks are Direct/Guest/AccountSpecific; can't create combination of SG enabled network and regular networks + for (Long networkId : networkIdList) { + NetworkVO network = _networkDao.findById(networkId); + + if (network == null) { + throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); + } + + if (network.isSecurityGroupEnabled() && networkIdList.size() > 1) { + throw new InvalidParameterValueException("Can't create a vm with multiple networks one of which is Security Group enabled"); + } + + if (network.getTrafficType() != TrafficType.Guest || network.getGuestType() != GuestIpType.Direct || (network.isShared() && !network.isSecurityGroupEnabled())) { + throw new InvalidParameterValueException("Can specify only Direct Guest Account specific networks when deploy vm in Security Group enabled zone"); + } + + //Perform account permission check + if (!network.isShared()) { + //Check account permissions + List networkMap = _networkDao.listBy(owner.getId(), network.getId()); + if (networkMap == null || networkMap.isEmpty()) { + throw new PermissionDeniedException("Unable to create a vm using network with id " + network.getId() + ", permission denied"); + } + } + + networkList.add(network); + } + } + + return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, + diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller); + } + + + @Override + public UserVm createAdvancedVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, List networkIdList, Account owner, String hostName, + String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, String userData, String sshKeyPair) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + + Account caller = UserContext.current().getCaller(); + List networkList = new ArrayList(); + + //Verify that caller can perform actions in behalf of vm owner + _accountMgr.checkAccess(caller, owner); + + //if no network is passed in, find Account specific Guest Virtual network + if (networkIdList == null || networkIdList.isEmpty()) { + List networks = _networkDao.listByOwner(owner.getId()); + NetworkVO guestVirtualNetwork = null; + for (NetworkVO network : networks) { + if (!network.isShared() && network.getTrafficType() == TrafficType.Guest && network.getGuestType() == GuestIpType.Virtual) { + guestVirtualNetwork = network; + break; + } + } + + if (guestVirtualNetwork == null) { + throw new InvalidParameterValueException("Unable to find Guest Virtual network for account id=" + owner.getId() + "; please specify networkId(s)"); + } + + networkList.add(guestVirtualNetwork); + + } else { + for (Long networkId : networkIdList) { + NetworkVO network = _networkDao.findById(networkId); + if (network == null) { + throw new InvalidParameterValueException("Unable to find network by id " + networkIdList.get(0).longValue()); + } + + //Perform account permission check + if (!network.isShared()) { + List networkMap = _networkDao.listBy(owner.getId(), network.getId()); + if (networkMap == null || networkMap.isEmpty()) { + throw new PermissionDeniedException("Unable to create a vm using network with id " + network.getId() + ", permission denied"); + } + } + + networkList.add(network); + } + } + + return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, caller, diskOfferingId, + diskSize, networkList, null, group, userData, sshKeyPair, hypervisor, caller); + } + + + + @DB @ActionEvent (eventType=EventTypes.EVENT_VM_CREATE, eventDescription="deploying Vm", create=true) + protected UserVm createVirtualMachine(DataCenter zone, ServiceOffering serviceOffering, VirtualMachineTemplate template, String hostName, String displayName, Account owner, Long diskOfferingId, + Long diskSize, List networkList, List securityGroupIdList, String group, String userData, String sshKeyPair, HypervisorType hypervisor, Account caller) + throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { + _accountMgr.checkAccess(caller, owner); long accountId = owner.getId(); - - DataCenterVO dc = _dcDao.findById(cmd.getZoneId()); - if (dc == null) { - throw new InvalidParameterValueException("Unable to find zone: " + cmd.getZoneId()); - } - if (dc.getDomainId() != null) { - DomainVO domain = _domainDao.findById(dc.getDomainId()); + if (zone.getDomainId() != null) { + DomainVO domain = _domainDao.findById(zone.getDomainId()); if (domain == null) { - throw new CloudRuntimeException("Unable to find the domain " + dc.getDomainId() + " for the zone: " + dc); + throw new CloudRuntimeException("Unable to find the domain " + zone.getDomainId() + " for the zone: " + zone); } _accountMgr.checkAccess(caller, domain); _accountMgr.checkAccess(owner, domain); @@ -1880,19 +2021,10 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager throw new StorageUnavailableException("There are no available pools in the UP state for vm deployment",-1); } - ServiceOfferingVO offering = _serviceOfferingDao.findById(cmd.getServiceOfferingId()); - if (offering == null || offering.getRemoved() != null) { - throw new InvalidParameterValueException("Unable to find service offering: " + cmd.getServiceOfferingId()); - } - - VMTemplateVO template = _templateDao.findById(cmd.getTemplateId()); - // Make sure a valid template ID was specified - if (template == null || template.getRemoved() != null) { - throw new InvalidParameterValueException("Unable to use template " + cmd.getTemplateId()); - } + ServiceOfferingVO offering = _serviceOfferingDao.findById(serviceOffering.getId()); if (template.getTemplateType().equals(TemplateType.SYSTEM)) { - throw new InvalidParameterValueException("Unable to use system template " + cmd.getTemplateId()+" to deploy a user vm"); + throw new InvalidParameterValueException("Unable to use system template " + template.getId() + " to deploy a user vm"); } boolean isIso = Storage.ImageFormat.ISO == template.getFormat(); @@ -1906,16 +2038,16 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager List> dataDiskOfferings = new ArrayList>(); if (isIso) { - if (cmd.getDiskOfferingId() == null) { + if (diskOfferingId == null) { throw new InvalidParameterValueException("Installing from ISO requires a disk offering to be specified for the root disk."); } - DiskOfferingVO diskOffering = _diskOfferingDao.findById(cmd.getDiskOfferingId()); + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); if (diskOffering == null) { - throw new InvalidParameterValueException("Unable to find disk offering " + cmd.getDiskOfferingId()); + throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); } Long size = null; if (diskOffering.getDiskSize() == 0) { - size = cmd.getSize(); + size = diskSize; if (size == null) { throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); } @@ -1924,14 +2056,14 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager rootDiskOffering.second(size); } else { rootDiskOffering.first(offering); - if (cmd.getDiskOfferingId() != null) { - DiskOfferingVO diskOffering = _diskOfferingDao.findById(cmd.getDiskOfferingId()); + if (diskOfferingId != null) { + DiskOfferingVO diskOffering = _diskOfferingDao.findById(diskOfferingId); if (diskOffering == null) { - throw new InvalidParameterValueException("Unable to find disk offering " + cmd.getDiskOfferingId()); + throw new InvalidParameterValueException("Unable to find disk offering " + diskOfferingId); } Long size = null; if (diskOffering.getDiskSize() == 0) { - size = cmd.getSize(); + size = diskSize; if (size == null) { throw new InvalidParameterValueException("Disk offering " + diskOffering + " requires size parameter."); } @@ -1940,7 +2072,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager } } - String userData = cmd.getUserData(); byte [] decodedUserData = null; if (userData != null) { if (userData.length() >= 2 * MAX_USER_DATA_LENGTH_BYTES) { @@ -1957,11 +2088,11 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager // Find an SSH public key corresponding to the key pair name, if one is given String sshPublicKey = null; - if (cmd.getSSHKeyPairName() != null && !cmd.getSSHKeyPairName().equals("")) { + if (sshKeyPair != null && !sshKeyPair.equals("")) { Account account = UserContext.current().getCaller(); - SSHKeyPair pair = _sshKeyPairDao.findByName(account.getAccountId(), account.getDomainId(), cmd.getSSHKeyPairName()); + SSHKeyPair pair = _sshKeyPairDao.findByName(account.getAccountId(), account.getDomainId(), sshKeyPair); if (pair == null) { - throw new InvalidParameterValueException("A key pair with name '" + cmd.getSSHKeyPairName() + "' was not found."); + throw new InvalidParameterValueException("A key pair with name '" + sshKeyPair + "' was not found."); } sshPublicKey = pair.getPublicKey(); @@ -1969,74 +2100,23 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager _accountMgr.checkAccess(caller, template); - DataCenterDeployment plan = new DataCenterDeployment(dc.getId()); + DataCenterDeployment plan = new DataCenterDeployment(zone.getId()); s_logger.debug("Allocating in the DB for vm"); - if (dc.getNetworkType() == NetworkType.Basic && networkList == null) { - Network defaultNetwork = _networkMgr.getSystemNetworkByZoneAndTrafficType(dc.getId(), TrafficType.Guest); - if (defaultNetwork == null) { - throw new InvalidParameterValueException("Unable to find a default network to start a vm"); - } else { - networkList = new ArrayList(); - networkList.add(defaultNetwork.getId()); - } - } - - List sgs = cmd.getSecurityGroupIdList(); - if (sgs != null && !sgs.isEmpty()) { - if (networkList == null || networkList.isEmpty()) { - Network networkWithSecurityGroup = _networkMgr.getNetworkWithSecurityGroupEnabled(dc.getId()); - if (networkWithSecurityGroup == null) { - throw new InvalidParameterValueException("No network with security enabled"); - } - networkList = new ArrayList(); - networkList.add(networkWithSecurityGroup.getId()); - } else { - if (networkList.size() > 1) { - throw new InvalidParameterValueException("Only support one network per VM if security group enabled"); - } - - Network net = _networkMgr.getNetwork(networkList.get(0).longValue()); - if (!net.isSecurityGroupEnabled()) { - throw new InvalidParameterValueException("need to enable security group for network: " + net.getId()); - } - } - } - - if (networkList == null || networkList.isEmpty()) { - Network networkWithSecurityGroup = _networkMgr.getNetworkWithSecurityGroupEnabled(dc.getId()); - if (networkWithSecurityGroup != null) { - networkList = new ArrayList(); - networkList.add(networkWithSecurityGroup.getId()); - } else { - throw new InvalidParameterValueException("NetworkIds have to be specified"); - } - } - List> networks = new ArrayList>(); short defaultNetworkNumber = 0; - for (Long networkId : networkList) { - NetworkVO network = _networkDao.findById(networkId); - if (network == null) { - throw new InvalidParameterValueException("Unable to find network by id " + networkId); - } else { - if (!network.isShared()) { - //Check account permissions - List networkMap = _networkDao.listBy(accountId, networkId); - if (networkMap == null || networkMap.isEmpty()) { - throw new PermissionDeniedException("Unable to create a vm using network with id " + networkId + ", permission denied"); - } - } - - if (network.isDefault()) { - defaultNetworkNumber++; - } - networks.add(new Pair(network, null)); + for (NetworkVO network : networkList) { + + if (network.isDefault()) { + defaultNetworkNumber++; } + + networks.add(new Pair(network, null)); } - //at least one network default network has to be set + //Verify network information - network default network has to be set; and vm can't have more than one default network + //This is a part of business logic because default network is required by Agent Manager in order to configure default gateway for the vm if (defaultNetworkNumber == 0) { throw new InvalidParameterValueException("At least 1 default network has to be specified for the vm"); } else if (defaultNetworkNumber >1) { @@ -2045,7 +2125,6 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager long id = _vmDao.getNextInSequence(Long.class, "id"); - String hostName = cmd.getName(); String instanceName = VirtualMachineName.getVmName(id, owner.getId(), _instance); if (hostName == null) { hostName = instanceName; @@ -2059,13 +2138,13 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager HypervisorType hypervisorType = null; if (template == null || template.getHypervisorType() == null || template.getHypervisorType() == HypervisorType.None) { - hypervisorType = cmd.getHypervisor(); + hypervisorType = hypervisor; } else { hypervisorType = template.getHypervisorType(); } - UserVmVO vm = new UserVmVO(id, instanceName, cmd.getDisplayName(), template.getId(), hypervisorType, - template.getGuestOSId(), offering.getOfferHA(), domainId, owner.getId(), offering.getId(), userData, hostName); + UserVmVO vm = new UserVmVO(id, instanceName, displayName, template.getId(), hypervisorType, + template.getGuestOSId(), offering.getOfferHA(), owner.getDomainId(), owner.getId(), offering.getId(), userData, hostName); if (sshPublicKey != null) { vm.setDetail("SSH.PublicKey", sshPublicKey); @@ -2075,7 +2154,7 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager vm.setIsoId(template.getId()); } - if (_itMgr.allocate(vm, template, offering, rootDiskOffering, dataDiskOfferings, networks, null, plan, cmd.getHypervisor(), owner) == null) { + if (_itMgr.allocate(vm, _templateDao.findById(template.getId()), offering, rootDiskOffering, dataDiskOfferings, networks, null, plan, hypervisorType, owner) == null) { return null; } @@ -2085,13 +2164,13 @@ public class UserVmManagerImpl implements UserVmManager, UserVmService, Manager s_logger.debug("Successfully allocated DB entry for " + vm); } UserContext.current().setEventDetails("Vm Id: "+vm.getId()); - UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, dc.getId(), vm.getId(), vm.getName(), offering.getId(), template.getId(), hypervisorType.toString()); + UsageEventVO usageEvent = new UsageEventVO(EventTypes.EVENT_VM_CREATE, accountId, zone.getId(), vm.getId(), vm.getName(), offering.getId(), template.getId(), hypervisorType.toString()); _usageEventDao.persist(usageEvent); _accountMgr.incrementResourceCount(accountId, ResourceType.user_vm); //Assign instance to the group - try{ + try { if (group != null) { boolean addToGroup = addInstanceToGroup(Long.valueOf(id), group); if (!addToGroup) {