From fe2a86871fde27832b8ed99f5fe965b905b874c0 Mon Sep 17 00:00:00 2001 From: Prachi Damle Date: Wed, 13 Mar 2013 13:57:47 -0700 Subject: [PATCH] Changes to add AffinityGroupprocessor, deployVM changes --- api/src/com/cloud/event/EventTypes.java | 1 + .../exception/AffinityConflictException.java | 18 ++ api/src/com/cloud/vm/UserVmService.java | 183 ++++++++++-------- .../affinity/AffinityGroupProcessor.java | 14 ++ .../affinity/AffinityGroupService.java | 3 + .../apache/cloudstack/api/ApiConstants.java | 2 + .../affinitygroup/ListAffinityGroupsCmd.java | 3 +- .../UpdateVMAffinityGroupCmd.java | 158 +++++++++++++++ .../api/command/user/vm/DeployVMCmd.java | 43 +++- .../deploy/UserPreferrenceProcessor.java | 27 +++ .../deploy/DeploymentPlanningManager.java | 29 +++ .../deploy/DeploymentPlanningManagerImpl.java | 80 ++++++++ .../src/com/cloud/vm/UserVmManagerImpl.java | 62 ++++-- .../cloud/vm/VirtualMachineManagerImpl.java | 97 +++++----- .../affinity/AffinityGroupServiceImpl.java | 71 +++++-- .../affinity/HostAntiAffinityProcessor.java | 66 +++++++ .../affinity/dao/AffinityGroupVMMapDao.java | 4 + .../dao/AffinityGroupVMMapDaoImpl.java | 43 ++++ .../com/cloud/vm/MockUserVmManagerImpl.java | 16 +- .../src/com/cloud/utils/SerialVersionUID.java | 1 + 20 files changed, 755 insertions(+), 166 deletions(-) create mode 100644 api/src/com/cloud/exception/AffinityConflictException.java create mode 100644 api/src/org/apache/cloudstack/affinity/AffinityGroupProcessor.java create mode 100644 api/src/org/apache/cloudstack/api/command/user/affinitygroup/UpdateVMAffinityGroupCmd.java create mode 100644 api/src/org/apache/cloudstack/deploy/UserPreferrenceProcessor.java create mode 100644 server/src/com/cloud/deploy/DeploymentPlanningManager.java create mode 100644 server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java create mode 100644 server/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java index bd5f2204fcf..6a26212ecc7 100755 --- a/api/src/com/cloud/event/EventTypes.java +++ b/api/src/com/cloud/event/EventTypes.java @@ -386,6 +386,7 @@ public class EventTypes { public static final String EVENT_AFFINITY_GROUP_DELETE = "AG.DELETE"; public static final String EVENT_AFFINITY_GROUP_ASSIGN = "AG.ASSIGN"; public static final String EVENT_AFFINITY_GROUP_REMOVE = "AG.REMOVE"; + public static final String EVENT_VM_AFFINITY_GROUP_UPDATE = "VM.AG.UPDATE"; static { diff --git a/api/src/com/cloud/exception/AffinityConflictException.java b/api/src/com/cloud/exception/AffinityConflictException.java new file mode 100644 index 00000000000..75df78419ab --- /dev/null +++ b/api/src/com/cloud/exception/AffinityConflictException.java @@ -0,0 +1,18 @@ +package com.cloud.exception; + +import com.cloud.exception.CloudException; +import com.cloud.utils.SerialVersionUID; + +public class AffinityConflictException extends CloudException { + + private static final long serialVersionUID = SerialVersionUID.AffinityConflictException; + + public AffinityConflictException(String message) { + super(message); + } + + public AffinityConflictException(String message, Throwable th) { + super(message, th); + } + +} diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java index 2c33d41cd35..d963b74b080 100755 --- a/api/src/com/cloud/vm/UserVmService.java +++ b/api/src/com/cloud/vm/UserVmService.java @@ -26,9 +26,6 @@ import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.vm.*; import org.apache.cloudstack.api.command.user.vmgroup.CreateVMGroupCmd; import org.apache.cloudstack.api.command.user.vmgroup.DeleteVMGroupCmd; -import org.apache.cloudstack.api.command.user.volume.AttachVolumeCmd; -import org.apache.cloudstack.api.command.user.volume.DetachVolumeCmd; - import com.cloud.dc.DataCenter; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.InsufficientCapacityException; @@ -42,7 +39,6 @@ import com.cloud.hypervisor.Hypervisor.HypervisorType; import com.cloud.network.Network.IpAddresses; import com.cloud.offering.ServiceOffering; import com.cloud.storage.StoragePool; -import com.cloud.storage.Volume; import com.cloud.template.VirtualMachineTemplate; import com.cloud.user.Account; import com.cloud.uservm.UserVm; @@ -104,14 +100,14 @@ public interface UserVmService { * @return the vm object if successful, null otherwise */ UserVm addNicToVirtualMachine(AddNicToVMCmd cmd); - + /** * Removes a NIC on the given network from the virtual machine * @param cmd the command object that defines the vm and the given network * @return the vm object if successful, null otherwise */ UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd); - + /** * Updates default Nic to the given network for given virtual machine * @param cmd the command object that defines the vm and the given network @@ -123,7 +119,8 @@ public interface UserVmService { /** - * Creates a Basic Zone 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 zone * - availability zone for the virtual machine @@ -132,61 +129,69 @@ public interface UserVmService { * @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 + * - comma separated list of security groups id that going to be + * applied to the virtual machine * @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 + * - 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 + * - 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 + * - 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 + * - name of the ssh key pair used to login to the virtual + * machine * @param requestedIps * TODO * @param defaultIp * TODO + * @param affinityGroupIdList * @param accountName - * - an optional account for the virtual machine. Must be used with domainId + * - 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 + * - an optional domainId for the virtual machine. If the account + * parameter is used, domainId must also be used * @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. + * 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. + * 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, Map requestedIps, IpAddresses defaultIp, String keyboard) + String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIp, + String keyboard, List affinityGroupIdList) 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. + * 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 @@ -197,63 +202,69 @@ public interface UserVmService { * @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 + * - comma separated list of security groups id that going to be + * applied to the virtual machine * @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 + * - 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 + * - 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 + * - 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 + * - name of the ssh key pair used to login to the virtual + * machine * @param requestedIps * TODO * @param defaultIps * TODO + * @param affinityGroupIdList * @param accountName - * - an optional account for the virtual machine. Must be used with domainId + * - 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 + * - an optional domainId for the virtual machine. If the account + * parameter is used, domainId must also be used * @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. + * 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. + * 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, Map requestedIps, - IpAddresses defaultIps, String keyboard) + IpAddresses defaultIps, String keyboard, List affinityGroupIdList) 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. - * + * 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 @@ -267,49 +278,57 @@ public interface UserVmService { * @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 + * - 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 + * - 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 + * - 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 + * - name of the ssh key pair used to login to the virtual + * machine * @param requestedIps * TODO - * @param defaultIps TODO + * @param defaultIps + * TODO + * @param affinityGroupIdList * @param accountName - * - an optional account for the virtual machine. Must be used with domainId + * - 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 + * - an optional domainId for the virtual machine. If the account + * parameter is used, domainId must also be used * @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. + * 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. + * if the resources required to deploy the VM is not currently + * available. * @throws InsufficientResourcesException */ 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, Map requestedIps, IpAddresses defaultIps, String keyboard) + String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, + String keyboard, List affinityGroupIdList) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException; /** diff --git a/api/src/org/apache/cloudstack/affinity/AffinityGroupProcessor.java b/api/src/org/apache/cloudstack/affinity/AffinityGroupProcessor.java new file mode 100644 index 00000000000..e6c64b3ff9c --- /dev/null +++ b/api/src/org/apache/cloudstack/affinity/AffinityGroupProcessor.java @@ -0,0 +1,14 @@ +package org.apache.cloudstack.affinity; + +import org.apache.cloudstack.deploy.UserPreferrenceProcessor; + +public interface AffinityGroupProcessor extends UserPreferrenceProcessor { + + /** + * getType() should return the affinity/anti-affinity group being + * implemented + * + * @return String Affinity/Anti-affinity type + */ + String getType(); +} \ No newline at end of file diff --git a/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java b/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java index 368abe28a8f..b6bf2ab1d78 100644 --- a/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java +++ b/api/src/org/apache/cloudstack/affinity/AffinityGroupService.java @@ -3,6 +3,7 @@ package org.apache.cloudstack.affinity; import java.util.List; import com.cloud.exception.ResourceInUseException; +import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; public interface AffinityGroupService { @@ -57,4 +58,6 @@ public interface AffinityGroupService { AffinityGroup getAffinityGroup(Long groupId); + UserVm updateVMAffinityGroups(Long vmId, List affinityGroupIds); + } diff --git a/api/src/org/apache/cloudstack/api/ApiConstants.java b/api/src/org/apache/cloudstack/api/ApiConstants.java index c518830c92c..cfabc61d54c 100755 --- a/api/src/org/apache/cloudstack/api/ApiConstants.java +++ b/api/src/org/apache/cloudstack/api/ApiConstants.java @@ -473,6 +473,8 @@ public class ApiConstants { public static final String HEALTHCHECK_HEALTHY_THRESHOLD = "healthythreshold"; public static final String HEALTHCHECK_UNHEALTHY_THRESHOLD = "unhealthythreshold"; public static final String HEALTHCHECK_PINGPATH = "pingpath"; + public static final String AFFINITY_GROUP_IDS = "affinitygroupids"; + public static final String AFFINITY_GROUP_NAMES = "affinitygroupnames"; public enum HostDetails { all, capacity, events, stats, min; diff --git a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java index 09d1f8f6635..effbd869b95 100644 --- a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/ListAffinityGroupsCmd.java @@ -83,7 +83,8 @@ public class ListAffinityGroupsCmd extends BaseListCmd { @Override public void execute(){ - Pair, Integer> result = _affinityGroupService.listAffinityGroups(id, affinityGroupName, + Pair, Integer> result = _affinityGroupService.listAffinityGroups(id, + affinityGroupName, affinityGroupType, virtualMachineId, this.getStartIndex(), this.getPageSizeVal()); if (result != null) { ListResponse response = new ListResponse(); diff --git a/api/src/org/apache/cloudstack/api/command/user/affinitygroup/UpdateVMAffinityGroupCmd.java b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/UpdateVMAffinityGroupCmd.java new file mode 100644 index 00000000000..94f8446fa26 --- /dev/null +++ b/api/src/org/apache/cloudstack/api/command/user/affinitygroup/UpdateVMAffinityGroupCmd.java @@ -0,0 +1,158 @@ +// 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.affinitygroup; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.cloudstack.affinity.AffinityGroupResponse; +import org.apache.cloudstack.api.ACL; +import org.apache.cloudstack.api.APICommand; +import org.apache.cloudstack.api.ApiConstants; +import org.apache.cloudstack.api.ApiErrorCode; +import org.apache.cloudstack.api.BaseAsyncCmd; +import org.apache.cloudstack.api.Parameter; +import org.apache.cloudstack.api.ServerApiException; +import org.apache.cloudstack.api.response.UserVmResponse; +import org.apache.log4j.Logger; + +import com.cloud.async.AsyncJob; +import com.cloud.event.EventTypes; +import com.cloud.exception.InsufficientCapacityException; +import com.cloud.exception.InvalidParameterValueException; +import com.cloud.exception.ResourceUnavailableException; +import com.cloud.user.Account; +import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; + + +@APICommand(name = "updateVMAffinityGroup", description = "Updates the affinity/anti-affinity group associations of a virtual machine. The VM has to be stopped and restarted for the " + + "new properties to take effect.", responseObject = UserVmResponse.class) +public class UpdateVMAffinityGroupCmd extends BaseAsyncCmd { + public static final Logger s_logger = Logger.getLogger(UpdateVMAffinityGroupCmd.class.getName()); + private static final String s_name = "updatevirtualmachineresponse"; + + ///////////////////////////////////////////////////// + //////////////// API parameters ///////////////////// + ///////////////////////////////////////////////////// + + + @ACL + @Parameter(name=ApiConstants.ID, type=CommandType.UUID, entityType=UserVmResponse.class, + required=true, description="The ID of the virtual machine") + private Long id; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are 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 securitygroupnames parameter") + private List affinityGroupIdList; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are 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 affinityGroupNameList; + + ///////////////////////////////////////////////////// + /////////////////// Accessors /////////////////////// + ///////////////////////////////////////////////////// + + + public Long getId() { + return id; + } + + + public List getAffinityGroupIdList() { + if (affinityGroupNameList != null && affinityGroupIdList != null) { + throw new InvalidParameterValueException( + "affinitygroupids parameter is mutually exclusive with affinitygroupnames parameter"); + } + + // transform group names to ids here + if (affinityGroupNameList != null) { + List affinityGroupIds = new ArrayList(); + for (String groupName : affinityGroupNameList) { + Long groupId = _responseGenerator.getAffinityGroupId(groupName, getEntityOwnerId()); + if (groupId == null) { + throw new InvalidParameterValueException("Unable to find group by name " + groupName + + " for account " + getEntityOwnerId()); + } else { + affinityGroupIds.add(groupId); + } + } + return affinityGroupIds; + } else { + return affinityGroupIdList; + } + } + + ///////////////////////////////////////////////////// + /////////////// API Implementation/////////////////// + ///////////////////////////////////////////////////// + + + @Override + public String getCommandName() { + return s_name; + } + + public static String getResultObjectName() { + return "virtualmachine"; + } + + @Override + public long getEntityOwnerId() { + UserVm userVm = _entityMgr.findById(UserVm.class, getId()); + if (userVm != null) { + return userVm.getAccountId(); + } + + return Account.ACCOUNT_ID_SYSTEM; // no account info given, parent this command to SYSTEM so ERROR events are tracked + } + + @Override + public void execute() throws ResourceUnavailableException, + InsufficientCapacityException, ServerApiException { + UserContext.current().setEventDetails("Vm Id: "+getId()); + UserVm result = _affinityGroupService.updateVMAffinityGroups(getId(), getAffinityGroupIdList()); + if (result != null){ + UserVmResponse response = _responseGenerator.createUserVmResponse("virtualmachine", result).get(0); + response.setResponseName(getCommandName()); + this.setResponseObject(response); + } else { + throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, "Failed to update vm's affinity groups"); + } + } + + @Override + public String getEventType() { + return EventTypes.EVENT_VM_AFFINITY_GROUP_UPDATE; + } + + @Override + public String getEventDescription() { + return "updating VM Affinity Group"; + } + + @Override + public AsyncJob.Type getInstanceType() { + return AsyncJob.Type.AffinityGroup; + } + +} diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java index 21a45f8cc7f..22fe1ba3a08 100755 --- a/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java +++ b/api/src/org/apache/cloudstack/api/command/user/vm/DeployVMCmd.java @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.affinity.AffinityGroupResponse; import org.apache.cloudstack.api.ACL; import org.apache.cloudstack.api.APICommand; import org.apache.cloudstack.api.ApiConstants; @@ -172,6 +173,18 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { @Parameter(name=ApiConstants.START_VM, type=CommandType.BOOLEAN, description="true if network offering supports specifying ip ranges; defaulted to true if not specified") private Boolean startVm; + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_IDS, type = CommandType.LIST, collectionType = CommandType.UUID, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups id that are 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 securitygroupnames parameter") + private List affinityGroupIdList; + + @ACL + @Parameter(name = ApiConstants.AFFINITY_GROUP_NAMES, type = CommandType.LIST, collectionType = CommandType.STRING, entityType = AffinityGroupResponse.class, description = "comma separated list of affinity groups names that are 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 affinityGroupNameList; + ///////////////////////////////////////////////////// /////////////////// Accessors /////////////////////// @@ -321,6 +334,30 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { return ip6Address.toLowerCase(); } + public List getAffinityGroupIdList() { + if (affinityGroupNameList != null && affinityGroupIdList != null) { + throw new InvalidParameterValueException( + "affinitygroupids parameter is mutually exclusive with affinitygroupnames parameter"); + } + + // transform group names to ids here + if (affinityGroupNameList != null) { + List affinityGroupIds = new ArrayList(); + for (String groupName : affinityGroupNameList) { + Long groupId = _responseGenerator.getAffinityGroupId(groupName, getEntityOwnerId()); + if (groupId == null) { + throw new InvalidParameterValueException("Unable to find group by name " + groupName + + " for account " + getEntityOwnerId()); + } else { + affinityGroupIds.add(groupId); + } + } + return affinityGroupIds; + } else { + return affinityGroupIdList; + } + } + ///////////////////////////////////////////////////// /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// @@ -447,18 +484,18 @@ public class DeployVMCmd extends BaseAsyncCreateCmd { 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(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard); + displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList()); } } else { if (zone.isSecurityGroupEnabled()) { vm = _userVmService.createAdvancedSecurityGroupVirtualMachine(zone, serviceOffering, template, getNetworkIds(), getSecurityGroupIdList(), - owner, name, displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard); + owner, name, displayName, diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList()); } 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(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard); + diskOfferingId, size, group, getHypervisor(), userData, sshKeyPairName, getIpToNetworkMap(), addrs, keyboard, getAffinityGroupIdList()); } } diff --git a/api/src/org/apache/cloudstack/deploy/UserPreferrenceProcessor.java b/api/src/org/apache/cloudstack/deploy/UserPreferrenceProcessor.java new file mode 100644 index 00000000000..ad26f0e06d8 --- /dev/null +++ b/api/src/org/apache/cloudstack/deploy/UserPreferrenceProcessor.java @@ -0,0 +1,27 @@ +package org.apache.cloudstack.deploy; + + +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.exception.AffinityConflictException; +import com.cloud.utils.component.Adapter; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +public interface UserPreferrenceProcessor extends Adapter { + + /** + * process() is called to apply any user preferences to the deployment plan + * and avoid set for the given VM placement. + * + * @param vm + * virtual machine. + * @param plan + * deployment plan that tells you where it's being deployed to. + * @param avoid + * avoid these data centers, pods, clusters, or hosts. + */ + void process(VirtualMachineProfile vm, DeploymentPlan plan, ExcludeList avoid) + throws AffinityConflictException; + +} \ No newline at end of file diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManager.java b/server/src/com/cloud/deploy/DeploymentPlanningManager.java new file mode 100644 index 00000000000..4ac71e3ac81 --- /dev/null +++ b/server/src/com/cloud/deploy/DeploymentPlanningManager.java @@ -0,0 +1,29 @@ +package com.cloud.deploy; + +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.exception.AffinityConflictException; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.utils.component.Manager; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; + +public interface DeploymentPlanningManager extends Manager { + + /** + * Manages vm deployment stages: First Process Affinity/Anti-affinity - Call + * the chain of AffinityGroupProcessor adapters to set deploymentplan scope + * and exclude list Secondly, Call DeploymentPlanner - to use heuristics to + * find the best spot to place the vm/volume. Planner will drill down to the + * write set of clusters to look for placement based on various heuristics. + * Lastly, Call Allocators - Given a cluster, allocators matches the + * requirements to capabilities of the physical resource (host, storage + * pool). + * + * @throws AffinityConflictException + * + * + * + */ + DeployDestination planDeployment(VirtualMachineProfile vmProfile, DeploymentPlan plan, + ExcludeList avoids) throws InsufficientServerCapacityException, AffinityConflictException; +} diff --git a/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java new file mode 100644 index 00000000000..11e5b82d176 --- /dev/null +++ b/server/src/com/cloud/deploy/DeploymentPlanningManagerImpl.java @@ -0,0 +1,80 @@ +package com.cloud.deploy; + +import java.util.List; + +import javax.ejb.Local; +import javax.inject.Inject; + +import org.apache.cloudstack.affinity.AffinityGroupProcessor; +import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.log4j.Logger; + +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.exception.AffinityConflictException; +import com.cloud.exception.InsufficientServerCapacityException; +import com.cloud.utils.component.Manager; +import com.cloud.utils.component.ManagerBase; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value = { DeploymentPlanningManager.class }) +public class DeploymentPlanningManagerImpl extends ManagerBase implements DeploymentPlanningManager, Manager { + + private static final Logger s_logger = Logger.getLogger(DeploymentPlanningManagerImpl.class); + @Inject + protected UserVmDao _vmDao; + @Inject + protected VMInstanceDao _vmInstanceDao; + @Inject + protected AffinityGroupDao _affinityGroupDao; + @Inject + protected AffinityGroupVMMapDao _affinityGroupVMMapDao; + + @Inject + protected List _planners; + + @Inject + protected List _affinityProcessors; + + @Override + public DeployDestination planDeployment(VirtualMachineProfile vmProfile, + DeploymentPlan plan, ExcludeList avoids) throws InsufficientServerCapacityException, + AffinityConflictException { + + // call affinitygroup chain + VirtualMachine vm = vmProfile.getVirtualMachine(); + long vmGroupCount = _affinityGroupVMMapDao.countAffinityGroupsForVm(vm.getId()); + + if (vmGroupCount > 0) { + for (AffinityGroupProcessor processor : _affinityProcessors) { + processor.process(vmProfile, plan, avoids); + } + } + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Deploy avoids pods: " + avoids.getPodsToAvoid() + ", clusters: " + + avoids.getClustersToAvoid() + ", hosts: " + avoids.getHostsToAvoid()); + } + + // call planners + DeployDestination dest = null; + for (DeploymentPlanner planner : _planners) { + if (planner.canHandle(vmProfile, plan, avoids)) { + dest = planner.plan(vmProfile, plan, avoids); + } else { + continue; + } + if (dest != null) { + avoids.addHost(dest.getHost().getId()); + break; + } + + } + return dest; + } + +} diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java index 1c3764a8391..ca158f1995d 100755 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -35,6 +35,9 @@ import javax.naming.ConfigurationException; import com.cloud.api.ApiDBUtils; import org.apache.cloudstack.acl.ControlledEntity.ACLType; import org.apache.cloudstack.acl.SecurityChecker.AccessType; +import org.apache.cloudstack.affinity.AffinityGroupVO; +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.cloudstack.api.command.admin.vm.AssignVMCmd; import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd; import org.apache.cloudstack.api.command.user.vm.*; @@ -369,7 +372,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use VpcManager _vpcMgr; @Inject TemplateManager templateMgr; - @Inject + @Inject protected GuestOSCategoryDao _guestOSCategoryDao; @Inject UsageEventDao _usageEventDao; @@ -378,6 +381,11 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use @Inject protected VMSnapshotManager _vmSnapshotMgr; + @Inject + AffinityGroupVMMapDao _affinityGroupVMMapDao; + @Inject + AffinityGroupDao _affinityGroupDao; + @Inject List plannerSelectors; @@ -684,7 +692,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use try { VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); - status = vmEntity.stop(new Long(userId).toString()); + status = vmEntity.stop(new Long(userId).toString()); } catch (ResourceUnavailableException e) { s_logger.debug("Unable to stop due to ", e); status = false; @@ -1916,7 +1924,10 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use @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, Map requestedIps, IpAddresses defaultIps, String keyboard) + String hostName, + String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, + String keyboard, List affinityGroupIdList) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = UserContext.current().getCaller(); @@ -1966,13 +1977,17 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, - diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, keyboard); + diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, + requestedIps, defaultIps, keyboard, affinityGroupIdList); } @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, Map requestedIps, IpAddresses defaultIps, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, + String sshKeyPair, Map requestedIps, + IpAddresses defaultIps, String keyboard, List affinityGroupIdList) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, + StorageUnavailableException, ResourceAllocationException { Account caller = UserContext.current().getCaller(); @@ -2018,7 +2033,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use isSecurityGroupEnabledNetworkUsed = true; } else { - // Verify that all the networks are Shared/Guest; can't create combination of SG enabled and disabled networks + // Verify that all the networks are Shared/Guest; can't create combination of SG enabled and disabled networks for (Long networkId : networkIdList) { NetworkVO network = _networkDao.findById(networkId); @@ -2034,7 +2049,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } isSecurityGroupEnabledNetworkUsed = true; - } + } if (!(network.getTrafficType() == TrafficType.Guest && network.getGuestType() == Network.GuestType.Shared)) { throw new InvalidParameterValueException("Can specify only Shared Guest networks when" + @@ -2079,12 +2094,15 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, - diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, keyboard); + diskSize, networkList, securityGroupIdList, group, userData, sshKeyPair, hypervisor, caller, + requestedIps, defaultIps, keyboard, affinityGroupIdList); } @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, Map requestedIps, IpAddresses defaultIps, String keyboard) + String displayName, Long diskOfferingId, Long diskSize, String group, HypervisorType hypervisor, + String userData, String sshKeyPair, Map requestedIps, IpAddresses defaultIps, + String keyboard, List affinityGroupIdList) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { Account caller = UserContext.current().getCaller(); @@ -2192,7 +2210,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } - return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, diskSize, networkList, null, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, keyboard); + return createVirtualMachine(zone, serviceOffering, template, hostName, displayName, owner, diskOfferingId, + diskSize, networkList, null, group, userData, sshKeyPair, hypervisor, caller, requestedIps, defaultIps, + keyboard, affinityGroupIdList); } @@ -2205,7 +2225,9 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use @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, Map requestedIps, IpAddresses defaultIps, String keyboard) + Long diskSize, List networkList, List securityGroupIdList, String group, String userData, + String sshKeyPair, HypervisorType hypervisor, Account caller, Map requestedIps, + IpAddresses defaultIps, String keyboard, List affinityGroupIdList) throws InsufficientCapacityException, ResourceUnavailableException, ConcurrentOperationException, StorageUnavailableException, ResourceAllocationException { _accountMgr.checkAccess(caller, null, true, owner); @@ -2261,6 +2283,14 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use } } + // check that the affinity groups exist + for (Long affinityGroupId : affinityGroupIdList) { + AffinityGroupVO ag = _affinityGroupDao.findById(affinityGroupId); + if (ag == null) { + throw new InvalidParameterValueException("Unable to find affinity group by id " + affinityGroupId); + } + } + if (template.getHypervisorType() != null && template.getHypervisorType() != HypervisorType.BareMetal) { // check if we have available pools for vm deployment long availablePools = _storagePoolDao.countPoolsByStatus(StoragePoolStatus.Up); @@ -2438,7 +2468,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use // * verify that there are no duplicates if (hostNames.contains(hostName)) { throw new InvalidParameterValueException("The vm with hostName " + hostName - + " already exists in the network domain: " + ntwkDomain + "; network=" + + " already exists in the network domain: " + ntwkDomain + "; network=" + _networkModel.getNetwork(ntwkId)); } } @@ -2510,7 +2540,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use List computeTags = new ArrayList(); computeTags.add(offering.getHostTag()); - List rootDiskTags = new ArrayList(); + List rootDiskTags = new ArrayList(); rootDiskTags.add(offering.getTags()); if(isIso){ @@ -2552,6 +2582,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use _securityGroupMgr.addInstanceToGroups(vm.getId(), securityGroupIdList); + _affinityGroupVMMapDao.updateMap(vm.getId(), affinityGroupIdList); + return vm; } @@ -2829,7 +2861,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use try { VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); - vmEntity.stop(new Long(userId).toString()); + vmEntity.stop(new Long(userId).toString()); } catch (ResourceUnavailableException e) { throw new CloudRuntimeException( "Unable to contact the agent to stop the virtual machine " @@ -3044,7 +3076,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use try { VirtualMachineEntity vmEntity = _orchSrvc.getVirtualMachine(vm.getUuid()); - status = vmEntity.destroy(new Long(userId).toString()); + status = vmEntity.destroy(new Long(userId).toString()); } catch (CloudException e) { CloudRuntimeException ex = new CloudRuntimeException( "Unable to destroy with specified vmId", e); diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java index af2271660fa..08c6e7c1208 100755 --- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java +++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java @@ -5,7 +5,7 @@ // 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, @@ -68,7 +68,9 @@ import com.cloud.deploy.DeployDestination; import com.cloud.deploy.DeploymentPlan; import com.cloud.deploy.DeploymentPlanner; import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.deploy.DeploymentPlanningManager; import com.cloud.domain.dao.DomainDao; +import com.cloud.exception.AffinityConflictException; import com.cloud.exception.AgentUnavailableException; import com.cloud.exception.ConcurrentOperationException; import com.cloud.exception.ConnectionException; @@ -226,8 +228,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject protected ResourceManager _resourceMgr; - - @Inject + + @Inject protected VMSnapshotManager _vmSnapshotMgr = null; @Inject protected ClusterDetailsDao _clusterDetailsDao; @@ -239,6 +241,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Inject VolumeManager volumeMgr; + @Inject + DeploymentPlanningManager _dpMgr; + Map> _vmGurus = new HashMap>(); protected StateMachine2 _stateMachine; @@ -586,7 +591,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac ConcurrentOperationException, ResourceUnavailableException { return advanceStart(vm, params, caller, account, null); } - + @Override public T advanceStart(T vm, Map params, User caller, Account account, DeploymentPlan planToDeploy) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException { @@ -695,17 +700,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vm, template, offering, account, params); DeployDestination dest = null; - for (DeploymentPlanner planner : _planners) { - if (planner.canHandle(vmProfile, plan, avoids)) { - dest = planner.plan(vmProfile, plan, avoids); - } else { - continue; - } - if (dest != null) { - avoids.addHost(dest.getHost().getId()); - journal.record("Deployment found ", vmProfile, dest); - break; - } + try { + dest = _dpMgr.planDeployment(vmProfile, plan, avoids); + } catch (AffinityConflictException e2) { + // TODO Auto-generated catch block + e2.printStackTrace(); } if (dest == null) { @@ -739,7 +738,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac if (s_logger.isDebugEnabled()) { s_logger.debug("VM is being created in podId: " + vm.getPodIdToDeployIn()); } - _networkMgr.prepare(vmProfile, dest, ctx); + _networkMgr.prepare(vmProfile, dest, ctx); if (vm.getHypervisorType() != HypervisorType.BareMetal) { this.volumeMgr.prepare(vmProfile, dest); } @@ -797,7 +796,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac StopCommand cmd = new StopCommand(vm); StopAnswer answer = (StopAnswer) _agentMgr.easySend(destHostId, cmd); if (answer == null || !answer.getResult()) { - s_logger.warn("Unable to stop " + vm + " due to " + (answer != null ? answer.getDetails() : "no answers")); + s_logger.warn("Unable to stop " + vm + " due to " + (answer != null ? answer.getDetails() : "no answers")); _haMgr.scheduleStop(vm, destHostId, WorkType.ForceStop); throw new ExecutionException("Unable to stop " + vm + " so we are unable to retry the start operation"); } @@ -1144,7 +1143,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.error("State transit with event: " + e + " failed due to: " + vm.getInstanceName() + " has active VM snapshots tasks"); return false; } - + State oldState = vm.getState(); if (oldState == State.Starting) { if (e == Event.OperationSucceeded) { @@ -1179,12 +1178,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.debug("Unable to stop " + vm); return false; } - + if (!_vmSnapshotMgr.deleteAllVMSnapshots(vm.getId(),null)){ s_logger.debug("Unable to delete all snapshots for " + vm); return false; } - + try { if (!stateTransitTo(vm, VirtualMachine.Event.DestroyRequested, vm.getHostId())) { s_logger.debug("Unable to destroy the vm because it is not in the correct state: " + vm); @@ -1559,7 +1558,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public boolean isVirtualMachineUpgradable(VirtualMachine vm, ServiceOffering offering) { - boolean isMachineUpgradable = true; + boolean isMachineUpgradable = true; for(HostAllocator allocator : _hostAllocators) { isMachineUpgradable = allocator.isVirtualMachineUpgradable(vm, offering); if(isMachineUpgradable) @@ -1628,7 +1627,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return new StopCommand(vmName); } - public Commands fullHostSync(final long hostId, StartupRoutingCommand startup) { + public Commands fullHostSync(final long hostId, StartupRoutingCommand startup) { Commands commands = new Commands(OnError.Continue); Map infos = convertToInfos(startup); @@ -1637,7 +1636,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.debug("Found " + vms.size() + " VMs for host " + hostId); for (VMInstanceVO vm : vms) { AgentVmInfo info = infos.remove(vm.getId()); - + // sync VM Snapshots related transient states List vmSnapshotsInTrasientStates = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging,VMSnapshot.State.Reverting, VMSnapshot.State.Creating); if(vmSnapshotsInTrasientStates.size() > 1){ @@ -1649,11 +1648,11 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.info("Successfully sync VM with transient snapshot: " + vm.getInstanceName()); } } - + VMInstanceVO castedVm = null; if (info == null) { info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); - } + } castedVm = info.guru.findById(vm.getId()); HypervisorGuru hvGuru = _hvGuruMgr.getGuru(castedVm.getHypervisorType()); @@ -1767,7 +1766,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac for (VMInstanceVO vm : set_vms) { AgentVmInfo info = infos.remove(vm.getId()); VMInstanceVO castedVm = null; - + // sync VM Snapshots related transient states List vmSnapshotsInExpungingStates = _vmSnapshotDao.listByInstanceId(vm.getId(), VMSnapshot.State.Expunging, VMSnapshot.State.Creating,VMSnapshot.State.Reverting); if(vmSnapshotsInExpungingStates.size() > 0){ @@ -1785,9 +1784,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.info("Successfully sync VM with transient snapshot: " + vm.getInstanceName()); } } - - if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting )) - || (info != null && (info.state == State.Running && vm.getState() == State.Starting))) + + if ((info == null && (vm.getState() == State.Running || vm.getState() == State.Starting )) + || (info != null && (info.state == State.Running && vm.getState() == State.Starting))) { s_logger.info("Found vm " + vm.getInstanceName() + " in inconsistent state. " + vm.getState() + " on CS while " + (info == null ? "Stopped" : "Running") + " on agent"); info = new AgentVmInfo(vm.getInstanceName(), getVmGuru(vm), vm, State.Stopped); @@ -1925,7 +1924,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac return map; } - protected Map convertToInfos(StartupRoutingCommand cmd) { + protected Map convertToInfos(StartupRoutingCommand cmd) { final Map states = cmd.getVmStates(); final HashMap map = new HashMap(); if (states == null) { @@ -1989,7 +1988,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac * compareState does as its name suggests and compares the states between * management server and agent. It returns whether something should be * cleaned up - * + * */ protected Command compareState(long hostId, VMInstanceVO vm, final AgentVmInfo info, final boolean fullSync, boolean trackExternalChange) { State agentState = info.state; @@ -2192,7 +2191,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac List nics = _nicsDao.listByVmId(profile.getId()); for (NicVO nic : nics) { Network network = _networkModel.getNetwork(nic.getNetworkId()); - NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), null, _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(profile.getHypervisorType(), network)); profile.addNic(nicProfile); } @@ -2319,7 +2318,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac s_logger.fatal("The Cluster VM sync process failed for cluster id " + clusterId + " with ", e); } } - else { // for others KVM and VMWare + else { // for others KVM and VMWare StartupRoutingCommand startup = (StartupRoutingCommand) cmd; Commands commands = fullHostSync(agentId, startup); @@ -2492,7 +2491,7 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } @Override - public NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, + public NicProfile addVmToNetwork(VirtualMachine vm, Network network, NicProfile requested) throws ConcurrentOperationException, ResourceUnavailableException, InsufficientCapacityException { s_logger.debug("Adding vm " + vm + " to network " + network + "; requested nic profile " + requested); @@ -2502,14 +2501,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac } else { vmVO = _vmDao.findById(vm.getId()); } - ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), + ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null); DataCenter dc = _configMgr.getZone(network.getDataCenterId()); - Host host = _hostDao.findById(vm.getHostId()); + Host host = _hostDao.findById(vm.getHostId()); DeployDestination dest = new DeployDestination(dc, null, null, host); //check vm state @@ -2557,14 +2556,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac public boolean removeNicFromVm(VirtualMachine vm, NicVO nic) throws ConcurrentOperationException, ResourceUnavailableException { VMInstanceVO vmVO = _vmDao.findById(vm.getId()); NetworkVO network = _networkDao.findById(nic.getNetworkId()); - ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), + ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null); DataCenter dc = _configMgr.getZone(network.getDataCenterId()); - Host host = _hostDao.findById(vm.getHostId()); + Host host = _hostDao.findById(vm.getHostId()); DeployDestination dest = new DeployDestination(dc, null, null, host); VirtualMachineGuru vmGuru = getVmGuru(vmVO); HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); @@ -2576,9 +2575,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new CloudRuntimeException("Failed to remove nic from " + vm + " in " + network + ", nic is default."); } - NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), - _networkModel.getNetworkRate(network.getId(), vm.getId()), - _networkModel.isSecurityGroupSupportedInNetwork(network), + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), + _networkModel.getNetworkRate(network.getId(), vm.getId()), + _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); //1) Unplug the nic @@ -2611,14 +2610,14 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac @Override public boolean removeVmFromNetwork(VirtualMachine vm, Network network, URI broadcastUri) throws ConcurrentOperationException, ResourceUnavailableException { VMInstanceVO vmVO = _vmDao.findById(vm.getId()); - ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), + ReservationContext context = new ReservationContextImpl(null, null, _accountMgr.getActiveUser(User.UID_SYSTEM), _accountMgr.getAccount(Account.ACCOUNT_ID_SYSTEM)); - VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, + VirtualMachineProfileImpl vmProfile = new VirtualMachineProfileImpl(vmVO, null, null, null, null); DataCenter dc = _configMgr.getZone(network.getDataCenterId()); - Host host = _hostDao.findById(vm.getHostId()); + Host host = _hostDao.findById(vm.getHostId()); DeployDestination dest = new DeployDestination(dc, null, null, host); VirtualMachineGuru vmGuru = getVmGuru(vmVO); HypervisorGuru hvGuru = _hvGuruMgr.getGuru(vmProfile.getVirtualMachine().getHypervisorType()); @@ -2643,9 +2642,9 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac throw new CloudRuntimeException("Failed to remove nic from " + vm + " in " + network + ", nic is default."); } - NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), - _networkModel.getNetworkRate(network.getId(), vm.getId()), - _networkModel.isSecurityGroupSupportedInNetwork(network), + NicProfile nicProfile = new NicProfile(nic, network, nic.getBroadcastUri(), nic.getIsolationUri(), + _networkModel.getNetworkRate(network.getId(), vm.getId()), + _networkModel.isSecurityGroupSupportedInNetwork(network), _networkModel.getNetworkTag(vmProfile.getVirtualMachine().getHypervisorType(), network)); //1) Unplug the nic diff --git a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java index 994f2256552..f6c87cb9fe3 100644 --- a/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java +++ b/server/src/org/apache/cloudstack/affinity/AffinityGroupServiceImpl.java @@ -1,6 +1,5 @@ package org.apache.cloudstack.affinity; -import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -13,36 +12,34 @@ import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; import org.apache.log4j.Logger; -import com.cloud.api.query.vo.SecurityGroupJoinVO; import com.cloud.event.ActionEvent; import com.cloud.event.EventTypes; import com.cloud.exception.InvalidParameterValueException; import com.cloud.exception.ResourceInUseException; -import com.cloud.network.PhysicalNetwork; -import com.cloud.network.dao.PhysicalNetworkVO; -import com.cloud.network.security.SecurityGroupManager; -import com.cloud.network.security.SecurityGroupRuleVO; -import com.cloud.network.security.SecurityGroupVMMapVO; -import com.cloud.network.security.SecurityGroupVO; +import com.cloud.network.security.SecurityGroup; import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.UserContext; +import com.cloud.uservm.UserVm; import com.cloud.utils.Pair; import com.cloud.utils.component.Manager; import com.cloud.utils.component.ManagerBase; import com.cloud.utils.db.DB; import com.cloud.utils.db.Filter; -import com.cloud.utils.db.JoinBuilder.JoinType; import com.cloud.utils.db.JoinBuilder; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.Transaction; +import com.cloud.utils.fsm.StateListener; import com.cloud.vm.UserVmVO; -import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachine.Event; +import com.cloud.vm.VirtualMachine.State; import com.cloud.vm.dao.UserVmDao; @Local(value = { AffinityGroupService.class }) -public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGroupService, Manager { +public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGroupService, Manager, + StateListener { public static final Logger s_logger = Logger.getLogger(AffinityGroupServiceImpl.class); private String _name; @@ -221,4 +218,56 @@ public class AffinityGroupServiceImpl extends ManagerBase implements AffinityGro return _affinityGroupDao.findById(groupId); } + @Override + public boolean preStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, + boolean status, Object opaque) { + return true; + } + + @Override + public boolean postStateTransitionEvent(State oldState, Event event, State newState, VirtualMachine vo, + boolean status, Object opaque) { + if (!status) { + return false; + } + if ((newState == State.Expunging)) { + // cleanup all affinity groups associated to the Expunged VM + SearchCriteria sc = _affinityGroupVMMapDao.createSearchCriteria(); + sc.addAnd("instanceId", SearchCriteria.Op.EQ, vo.getId()); + _affinityGroupVMMapDao.expunge(sc); + } + return true; + } + + @Override + public UserVm updateVMAffinityGroups(Long vmId, List affinityGroupIds) { + // Verify input parameters + UserVmVO vmInstance = _userVmDao.findById(vmId); + if (vmInstance == null) { + throw new InvalidParameterValueException("unable to find a virtual machine with id " + vmId); + } + + // Check that the VM is stopped + if (!vmInstance.getState().equals(State.Stopped)) { + s_logger.warn("Unable to update affinity groups of the virtual machine " + vmInstance.toString() + + " in state " + vmInstance.getState()); + throw new InvalidParameterValueException("Unable update affinity groups of the virtual machine " + + vmInstance.toString() + " " + "in state " + vmInstance.getState() + + "; make sure the virtual machine is stopped and not in an error state before updating."); + } + + // check that the affinity groups exist + for (Long affinityGroupId : affinityGroupIds) { + AffinityGroupVO ag = _affinityGroupDao.findById(affinityGroupId); + if (ag == null) { + throw new InvalidParameterValueException("Unable to find affinity group by id " + affinityGroupId); + } + } + _affinityGroupVMMapDao.updateMap(vmId, affinityGroupIds); + + // APIResponseHelper will pull out the updated affinitygroups. + return vmInstance; + + } + } diff --git a/server/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java b/server/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java new file mode 100644 index 00000000000..93bd3580f2f --- /dev/null +++ b/server/src/org/apache/cloudstack/affinity/HostAntiAffinityProcessor.java @@ -0,0 +1,66 @@ +package org.apache.cloudstack.affinity; + +import java.util.List; + +import javax.ejb.Local; +import javax.inject.Inject; + +import org.apache.cloudstack.affinity.dao.AffinityGroupDao; +import org.apache.cloudstack.affinity.dao.AffinityGroupVMMapDao; +import org.apache.log4j.Logger; + +import com.cloud.deploy.DeploymentPlan; +import com.cloud.deploy.DeploymentPlanner.ExcludeList; +import com.cloud.exception.AffinityConflictException; +import com.cloud.utils.component.AdapterBase; +import com.cloud.vm.VMInstanceVO; +import com.cloud.vm.VirtualMachine; +import com.cloud.vm.VirtualMachineProfile; +import com.cloud.vm.dao.UserVmDao; +import com.cloud.vm.dao.VMInstanceDao; + +@Local(value = AffinityGroupProcessor.class) +public class HostAntiAffinityProcessor extends AdapterBase implements AffinityGroupProcessor { + + private static final Logger s_logger = Logger.getLogger(HostAntiAffinityProcessor.class); + @Inject + protected UserVmDao _vmDao; + @Inject + protected VMInstanceDao _vmInstanceDao; + @Inject + protected AffinityGroupDao _affinityGroupDao; + @Inject + protected AffinityGroupVMMapDao _affinityGroupVMMapDao; + + @Override + public void process(VirtualMachineProfile vmProfile, DeploymentPlan plan, + ExcludeList avoid) + throws AffinityConflictException { + VirtualMachine vm = vmProfile.getVirtualMachine(); + AffinityGroupVMMapVO vmGroupMapping = _affinityGroupVMMapDao.findByVmIdType(vm.getId(), getType()); + + if (vmGroupMapping != null) { + AffinityGroupVO group = _affinityGroupDao.findById(vmGroupMapping.getAffinityGroupId()); + + if (s_logger.isDebugEnabled()) { + s_logger.debug("Processing affinity group " + group.getName() + " for VM Id: " + vm.getId()); + } + + List groupVMIds = _affinityGroupVMMapDao.listVmIdsByAffinityGroup(group.getId()); + + for (Long groupVMId : groupVMIds) { + VMInstanceVO groupVM = _vmInstanceDao.findById(groupVMId); + if (groupVM != null && !groupVM.isRemoved() && groupVM.getHostId() != null) { + avoid.addHost(groupVM.getHostId()); + } + } + } + + } + + @Override + public String getType() { + return "HostAntiAffinity"; + } + +} diff --git a/server/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java b/server/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java index 9841de44680..a98ae0ff7bd 100644 --- a/server/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java +++ b/server/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDao.java @@ -40,4 +40,8 @@ public interface AffinityGroupVMMapDao extends GenericDao affinityGroupIds); } diff --git a/server/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java b/server/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java index e9286dbc726..0fb4e7902b4 100644 --- a/server/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java +++ b/server/src/org/apache/cloudstack/affinity/dao/AffinityGroupVMMapDaoImpl.java @@ -19,17 +19,22 @@ package org.apache.cloudstack.affinity.dao; import java.util.List; import javax.ejb.Local; +import javax.inject.Inject; import org.apache.cloudstack.affinity.AffinityGroupVMMapVO; +import org.apache.cloudstack.affinity.AffinityGroupVO; import org.springframework.stereotype.Component; +import com.cloud.host.HostTagVO; import com.cloud.utils.Pair; import com.cloud.utils.db.Filter; import com.cloud.utils.db.GenericDaoBase; import com.cloud.utils.db.GenericSearchBuilder; +import com.cloud.utils.db.JoinBuilder.JoinType; import com.cloud.utils.db.SearchBuilder; import com.cloud.utils.db.SearchCriteria; import com.cloud.utils.db.SearchCriteria.Func; +import com.cloud.utils.db.Transaction; @Component @Local(value = { AffinityGroupVMMapDao.class }) @@ -40,6 +45,10 @@ public class AffinityGroupVMMapDaoImpl extends GenericDaoBase CountSGForVm; private GenericSearchBuilder ListVmIdByAffinityGroup; private SearchBuilder ListByAffinityGroup; + private SearchBuilder ListByVmIdType; + + @Inject + protected AffinityGroupDao _affinityGroupDao; protected AffinityGroupVMMapDaoImpl() { ListVmIdByAffinityGroup = createSearchBuilder(Long.class); @@ -62,6 +71,14 @@ public class AffinityGroupVMMapDaoImpl extends GenericDaoBase groupSearch = _affinityGroupDao.createSearchBuilder(); + groupSearch.and("type", groupSearch.entity().getType(), SearchCriteria.Op.EQ); + ListByVmIdType.join("groupSearch", groupSearch, ListByVmIdType.entity().getAffinityGroupId(), groupSearch + .entity().getId(), JoinType.INNER); + ListByVmIdType.done(); + CountSGForVm = createSearchBuilder(Long.class); CountSGForVm.select(null, Func.COUNT, null); CountSGForVm.and("vmId", CountSGForVm.entity().getInstanceId(), SearchCriteria.Op.EQ); @@ -117,4 +134,30 @@ public class AffinityGroupVMMapDaoImpl extends GenericDaoBase sc = ListByVmIdType.create(); + sc.setParameters("instanceId", instanceId); + sc.setJoinParameters("groupSearch", "type", type); + return customSearch(sc, null).get(0); + } + + @Override + public void updateMap(Long vmId, List affinityGroupIds) { + Transaction txn = Transaction.currentTxn(); + txn.start(); + + SearchCriteria sc = createSearchCriteria(); + sc.addAnd("instanceId", SearchCriteria.Op.EQ, vmId); + expunge(sc); + + for (Long groupId : affinityGroupIds) { + AffinityGroupVMMapVO vo = new AffinityGroupVMMapVO(groupId, vmId); + persist(vo); + } + + txn.commit(); + + } } diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java index dd8dd83df58..fd826d9e86e 100644 --- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java +++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java @@ -253,13 +253,13 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager, // TODO Auto-generated method stub return null; } - + @Override public UserVm removeNicFromVirtualMachine(RemoveNicFromVMCmd cmd) throws InvalidParameterValueException, PermissionDeniedException, CloudRuntimeException { // TODO Auto-generated method stub return null; } - + @Override public UserVm updateDefaultNicForVirtualMachine(UpdateDefaultNicForVMCmd cmd) throws InvalidParameterValueException, CloudRuntimeException { // TODO Auto-generated method stub @@ -330,7 +330,9 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager, @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, Map requestedIps, - IpAddresses defaultIp, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, + IpAddresses defaultIp, + String keyboard, List affinityGroupIdList) throws InsufficientCapacityException, + ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { // TODO Auto-generated method stub return null; @@ -339,7 +341,9 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager, @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, Map requestedIps, IpAddresses defaultIps, String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, + String sshKeyPair, Map requestedIps, + IpAddresses defaultIps, String keyboard, List affinityGroupIdList) + throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { // TODO Auto-generated method stub return null; @@ -348,7 +352,9 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager, @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, Map requestedIps, IpAddresses defaultIps, - String keyboard) throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, ResourceAllocationException { + String keyboard, List affinityGroupIdList) throws InsufficientCapacityException, + ConcurrentOperationException, ResourceUnavailableException, StorageUnavailableException, + ResourceAllocationException { // TODO Auto-generated method stub return null; } diff --git a/utils/src/com/cloud/utils/SerialVersionUID.java b/utils/src/com/cloud/utils/SerialVersionUID.java index 35e9446b17e..856d5639c50 100755 --- a/utils/src/com/cloud/utils/SerialVersionUID.java +++ b/utils/src/com/cloud/utils/SerialVersionUID.java @@ -61,4 +61,5 @@ public interface SerialVersionUID { public static final long CloudExecutionException = Base | 0x27; public static final long CallFailedException = Base | 0x28; public static final long UnableDeleteHostException = Base | 0x29; + public static final long AffinityConflictException = Base | 0x2a; }