diff --git a/server/src/com/cloud/api/commands/UpgradeVMCmd.java b/server/src/com/cloud/api/commands/UpgradeVMCmd.java index cf6c153ae79..aecf0aee460 100644 --- a/server/src/com/cloud/api/commands/UpgradeVMCmd.java +++ b/server/src/com/cloud/api/commands/UpgradeVMCmd.java @@ -18,33 +18,21 @@ package com.cloud.api.commands; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; +import java.util.Date; import org.apache.log4j.Logger; import com.cloud.api.BaseCmd; +import com.cloud.api.Implementation; import com.cloud.api.Parameter; -import com.cloud.api.ServerApiException; -import com.cloud.exception.InvalidParameterValueException; -import com.cloud.user.Account; -import com.cloud.user.User; -import com.cloud.utils.Pair; +import com.cloud.api.BaseCmd.Manager; +import com.cloud.serializer.Param; import com.cloud.vm.UserVmVO; - + +@Implementation(method="upgradeVirtualMachine", manager=Manager.UserVmManager) public class UpgradeVMCmd extends BaseCmd { public static final Logger s_logger = Logger.getLogger(UpgradeVMCmd.class.getName()); private static final String s_name = "changeserviceforvirtualmachineresponse"; - private static final List> s_properties = new ArrayList>(); - - static { - s_properties.add(new Pair(BaseCmd.Properties.ID, Boolean.TRUE)); - s_properties.add(new Pair(BaseCmd.Properties.SERVICE_OFFERING_ID, Boolean.TRUE)); - - s_properties.add(new Pair(BaseCmd.Properties.ACCOUNT_OBJ, Boolean.FALSE)); - s_properties.add(new Pair(BaseCmd.Properties.USER_ID, Boolean.FALSE)); - } ///////////////////////////////////////////////////// //////////////// API parameters ///////////////////// @@ -72,6 +60,8 @@ public class UpgradeVMCmd extends BaseCmd { /////////////// API Implementation/////////////////// ///////////////////////////////////////////////////// + private UserVmVO responseObject = null; + public String getName() { return s_name; } @@ -79,53 +69,343 @@ public class UpgradeVMCmd extends BaseCmd { public static String getResultObjectName() { return "virtualmachine"; } + + @Override + public String getResponse() + { + UpgradeVmResponse response = new UpgradeVmResponse(); + return null;//TODO -- construct response +// if (responseObject != null) { +// response.set +// +// Account acct = ms.findAccountById(Long.valueOf(vm.getAccountId())); +// resultObj.setAccount(acct.getAccountName()); +// +// ServiceOfferingVO offering = ms.findServiceOfferingById(vm.getServiceOfferingId()); +// resultObj.setCpuSpeed(offering.getSpeed()); +// resultObj.setMemory(offering.getRamSize()); +// if(offering.getDisplayText()!=null) +// resultObj.setServiceOfferingName(offering.getDisplayText()); +// else +// resultObj.setServiceOfferingName(offering.getName()); +// resultObj.setServiceOfferingId(vm.getServiceOfferingId()); +// +// VmStats vmStats = ms.getVmStatistics(vm.getId()); +// if(vmStats != null) +// { +// resultObj.setCpuUsed((long) vmStats.getCPUUtilization()); +// resultObj.setNetworkKbsRead((long) vmStats.getNetworkReadKBs()); +// resultObj.setNetworkKbsWrite((long) vmStats.getNetworkWriteKBs()); +// } +// +// resultObj.setCreated(vm.getCreated()); +// resultObj.setDisplayName(vm.getDisplayName()); +// resultObj.setDomain(ms.findDomainIdById(acct.getDomainId()).getName()); +// resultObj.setDomainId(acct.getDomainId()); +// resultObj.setHaEnable(vm.isHaEnabled()); +// if(vm.getHostId() != null) +// { +// resultObj.setHostId(vm.getHostId()); +// resultObj.setHostName(ms.getHostBy(vm.getHostId()).getName()); +// } +// resultObj.setIpAddress(vm.getPrivateIpAddress()); +// resultObj.setName(vm.getName()); +// resultObj.setState(vm.getState().toString()); +// resultObj.setZoneId(vm.getDataCenterId()); +// resultObj.setZoneName(ms.findDataCenterById(vm.getDataCenterId()).getName()); +// +// VMTemplateVO template = ms.findTemplateById(vm.getTemplateId()); +// resultObj.setPasswordEnabled(template.getEnablePassword()); +// resultObj.setTemplateDisplayText(template.getDisplayText()); +// resultObj.setTemplateId(template.getId()); +// resultObj.setTemplateName(template.getName()); +// } +// else +// { +// throw new ServerApiException(BaseCmd.INTERNAL_ERROR, "Failed to update zone; internal error."); +// } +// +// return SerializerHelper.toSerializedString(responseObject); + } + + public void setResponseObject(UserVmVO userVm) { + responseObject = userVm; + } + + // helper class for the response object + private class UpgradeVmResponse + { + @Param(name="id") + private long id; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public Date getCreated() { + return created; + } + + public void setCreated(Date created) { + this.created = created; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getState() { + return state; + } + + public void setState(String state) { + this.state = state; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public long getDomainId() { + return domainId; + } + + public void setDomainId(long domainId) { + this.domainId = domainId; + } + + public String getDomain() { + return domain; + } + + public void setDomain(String domain) { + this.domain = domain; + } + + public boolean isHaEnable() { + return haEnable; + } + + public void setHaEnable(boolean haEnable) { + this.haEnable = haEnable; + } + + public long getZoneId() { + return zoneId; + } + + public void setZoneId(long zoneId) { + this.zoneId = zoneId; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getZoneName() { + return zoneName; + } + + public void setZoneName(String zoneName) { + this.zoneName = zoneName; + } + + public long getHostId() { + return hostId; + } + + public void setHostId(long hostId) { + this.hostId = hostId; + } + + public String getHostName() { + return hostName; + } + + public void setHostName(String hostName) { + this.hostName = hostName; + } + + public long getTemplateId() { + return templateId; + } + + public void setTemplateId(long templateId) { + this.templateId = templateId; + } + + public String getTemplateName() { + return templateName; + } + + public void setTemplateName(String templateName) { + this.templateName = templateName; + } + + public String getTemplateDisplayText() { + return templateDisplayText; + } + + public void setTemplateDisplayText(String templateDisplayText) { + this.templateDisplayText = templateDisplayText; + } + + public boolean isPasswordEnabled() { + return passwordEnabled; + } + + public void setPasswordEnabled(boolean passwordEnabled) { + this.passwordEnabled = passwordEnabled; + } + + public long getServiceOfferingId() { + return serviceOfferingId; + } + + public void setServiceOfferingId(long serviceOfferingId) { + this.serviceOfferingId = serviceOfferingId; + } + + public String getServiceOfferingName() { + return serviceOfferingName; + } + + public void setServiceOfferingName(String serviceOfferingName) { + this.serviceOfferingName = serviceOfferingName; + } + + public long getCpuSpeed() { + return cpuSpeed; + } + + public void setCpuSpeed(long cpuSpeed) { + this.cpuSpeed = cpuSpeed; + } + + public long getMemory() { + return memory; + } + + public void setMemory(long memory) { + this.memory = memory; + } + + public long getCpuUsed() { + return cpuUsed; + } + + public void setCpuUsed(long cpuUsed) { + this.cpuUsed = cpuUsed; + } + + public long getNetworkKbsRead() { + return networkKbsRead; + } + + public void setNetworkKbsRead(long networkKbsRead) { + this.networkKbsRead = networkKbsRead; + } + + public long getNetworkKbsWrite() { + return networkKbsWrite; + } + + public void setNetworkKbsWrite(long networkKbsWrite) { + this.networkKbsWrite = networkKbsWrite; + } + + public long isId() + { + return id; + } + + @Param(name="name") + private String name; + + @Param(name="created") + private Date created; + + @Param(name="ipaddress") + private String ipAddress; + + @Param(name="state") + private String state; + + @Param(name="account") + private String account; + + @Param(name="domainid") + private long domainId; + + @Param(name="domain") + private String domain; + + @Param(name="haenable") + private boolean haEnable; + + @Param(name="zoneid") + private long zoneId; + + @Param(name="displayname") + private String displayName; + + @Param(name="zonename") + private String zoneName; + + @Param(name="hostid") + private long hostId; + + @Param(name="hostname") + private String hostName; + + @Param(name="templateid") + private long templateId; + + @Param(name="templatename") + private String templateName; + + @Param(name="templatedisplaytext") + private String templateDisplayText; + + @Param(name="passwordenabled") + private boolean passwordEnabled; + + @Param(name="serviceofferingid") + private long serviceOfferingId; + + @Param(name="serviceofferingname") + private String serviceOfferingName; + + @Param(name="cpunumber") + private long cpuSpeed; + + @Param(name="memory") + private long memory; + + @Param(name="cpuused") + private long cpuUsed; + + @Param(name="networkkbsread") + private long networkKbsRead; + + @Param(name="networkkbswrite") + private long networkKbsWrite; + } - public List> getProperties() { - return s_properties; - } - - @Override - public List> execute(Map params) { - Long virtualMachineId = (Long)params.get(BaseCmd.Properties.ID.getName()); - Long serviceOfferingId = (Long)params.get(BaseCmd.Properties.SERVICE_OFFERING_ID.getName()); - Account account = (Account)params.get(BaseCmd.Properties.ACCOUNT_OBJ.getName()); - Long userId = (Long)params.get(BaseCmd.Properties.USER_ID.getName()); - - // Verify input parameters - UserVmVO vmInstance = getManagementServer().findUserVMInstanceById(virtualMachineId.longValue()); - if (vmInstance == null) { - throw new ServerApiException(BaseCmd.VM_INVALID_PARAM_ERROR, "unable to find a virtual machine with id " + virtualMachineId); - } - - if (account != null) { - if (!isAdmin(account.getType()) && (account.getId().longValue() != vmInstance.getAccountId())) { - throw new ServerApiException(BaseCmd.VM_INVALID_PARAM_ERROR, "unable to find a virtual machine with id " + virtualMachineId + " for this account"); - } else if (!getManagementServer().isChildDomain(account.getDomainId(), vmInstance.getDomainId())) { - throw new ServerApiException(BaseCmd.PARAM_ERROR, "Invalid virtual machine id (" + virtualMachineId + ") given, unable to upgrade virtual machine."); - } - } - - // If command is executed via 8096 port, set userId to the id of System account (1) - if (userId == null) { - userId = Long.valueOf(User.UID_SYSTEM); - } - - long jobId = 0; - try { - jobId = getManagementServer().upgradeVirtualMachineAsync(userId, virtualMachineId, serviceOfferingId); - } catch (InvalidParameterValueException e) { - throw new ServerApiException(BaseCmd.VM_INVALID_PARAM_ERROR, "Failed to upgrade VM: " + e.getMessage()); - } - - if (jobId == 0) { - s_logger.warn("Unable to schedule async-job for UpgradeVM comamnd"); - } else { - if(s_logger.isDebugEnabled()) - s_logger.debug("UpgradeVM command has been accepted, job id: " + jobId); - } - - List> returnValues = new ArrayList>(); - returnValues.add(new Pair(BaseCmd.Properties.JOB_ID.getName(), Long.valueOf(jobId))); - return returnValues; - } } diff --git a/server/src/com/cloud/vm/UserVmManager.java b/server/src/com/cloud/vm/UserVmManager.java index 4c2a3e98e3d..de760347254 100644 --- a/server/src/com/cloud/vm/UserVmManager.java +++ b/server/src/com/cloud/vm/UserVmManager.java @@ -21,6 +21,8 @@ import java.util.HashMap; import java.util.List; import com.cloud.agent.api.VmStatsEntry; +import com.cloud.api.ServerApiException; +import com.cloud.api.commands.UpgradeVMCmd; import com.cloud.async.executor.DestroyVMExecutor; import com.cloud.async.executor.OperationResponse; import com.cloud.async.executor.RebootVMExecutor; @@ -165,11 +167,11 @@ public interface UserVmManager extends Manager, VirtualMachineManager /** * upgrade the service offering of the virtual machine - * @param vmId id of the virtual machine being upgraded - * @param serviceOfferingId id of the service offering the vm should now run under + * @param cmd * @return success/failure + * @throws InvalidParameterValueException */ - boolean upgradeVirtualMachine(long vmId, long serviceOfferingId); + boolean upgradeVirtualMachine(UpgradeVMCmd cmd) throws ServerApiException, InvalidParameterValueException; /** * Obtains statistics for a list of host or VMs; CPU and network utilization diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java old mode 100755 new mode 100644 index 89ae3351502..969df2d42e5 --- a/server/src/com/cloud/vm/UserVmManagerImpl.java +++ b/server/src/com/cloud/vm/UserVmManagerImpl.java @@ -37,6 +37,8 @@ import java.util.concurrent.TimeUnit; import javax.ejb.Local; import javax.naming.ConfigurationException; +import net.sf.ehcache.config.Configuration; + import org.apache.log4j.Logger; import com.cloud.agent.AgentManager; @@ -63,6 +65,8 @@ import com.cloud.agent.api.storage.CreatePrivateTemplateAnswer; import com.cloud.agent.api.storage.CreatePrivateTemplateCommand; import com.cloud.alert.AlertManager; import com.cloud.api.BaseCmd; +import com.cloud.api.ServerApiException; +import com.cloud.api.commands.UpgradeVMCmd; import com.cloud.async.AsyncJobExecutor; import com.cloud.async.AsyncJobManager; import com.cloud.async.AsyncJobResult; @@ -73,10 +77,12 @@ import com.cloud.async.executor.OperationResponse; import com.cloud.async.executor.RebootVMExecutor; import com.cloud.async.executor.StartVMExecutor; import com.cloud.async.executor.StopVMExecutor; +import com.cloud.async.executor.UpgradeVMParam; import com.cloud.async.executor.VMExecutorHelper; import com.cloud.async.executor.VMOperationListener; import com.cloud.async.executor.VMOperationParam; import com.cloud.capacity.dao.CapacityDao; +import com.cloud.configuration.ConfigurationManager; import com.cloud.configuration.ResourceCount.ResourceType; import com.cloud.configuration.dao.ConfigurationDao; import com.cloud.configuration.dao.ResourceLimitDao; @@ -90,6 +96,7 @@ import com.cloud.dc.dao.VlanDao; import com.cloud.domain.dao.DomainDao; import com.cloud.event.EventState; import com.cloud.event.EventTypes; +import com.cloud.event.EventUtils; import com.cloud.event.EventVO; import com.cloud.event.dao.EventDao; import com.cloud.exception.AgentUnavailableException; @@ -123,6 +130,8 @@ import com.cloud.network.security.NetworkGroupVO; import com.cloud.offering.ServiceOffering; import com.cloud.offering.ServiceOffering.GuestIpType; import com.cloud.offerings.NetworkOfferingVO; +import com.cloud.serializer.GsonHelper; +import com.cloud.server.ManagementServer; import com.cloud.service.ServiceOfferingVO; import com.cloud.service.dao.ServiceOfferingDao; import com.cloud.storage.DiskOfferingVO; @@ -151,9 +160,11 @@ import com.cloud.storage.dao.StoragePoolHostDao; import com.cloud.storage.dao.VMTemplateDao; import com.cloud.storage.dao.VMTemplateHostDao; import com.cloud.storage.dao.VolumeDao; +import com.cloud.user.Account; import com.cloud.user.AccountManager; import com.cloud.user.AccountVO; import com.cloud.user.User; +import com.cloud.user.UserContext; import com.cloud.user.UserVO; import com.cloud.user.dao.AccountDao; import com.cloud.user.dao.UserDao; @@ -177,6 +188,7 @@ import com.cloud.vm.VirtualMachine.Event; import com.cloud.vm.VirtualMachine.Type; import com.cloud.vm.dao.DomainRouterDao; import com.cloud.vm.dao.UserVmDao; +import com.google.gson.Gson; @Local(value={UserVmManager.class}) public class UserVmManagerImpl implements UserVmManager { @@ -208,6 +220,7 @@ public class UserVmManagerImpl implements UserVmManager { @Inject NetworkManager _networkMgr = null; @Inject StorageManager _storageMgr = null; @Inject AgentManager _agentMgr = null; + @Inject ConfigurationManager _configMgr = null; @Inject AccountDao _accountDao = null; @Inject UserDao _userDao = null; @Inject SnapshotDao _snapshotDao = null; @@ -224,7 +237,8 @@ public class UserVmManagerImpl implements UserVmManager { @Inject NetworkGroupManager _networkGroupManager; @Inject ServiceOfferingDao _serviceOfferingDao; @Inject EventDao _eventDao = null; - + @Inject UserVmDao _userVmDao = null; + private IpAddrAllocator _IpAllocator; ScheduledExecutorService _executor = null; int _expungeInterval; @@ -1077,13 +1091,95 @@ public class UserVmManagerImpl implements UserVmManager { AsyncJobResult.STATUS_FAILED, 0, resultDescription); return new OperationResponse(OperationResponse.STATUS_FAILED, resultDescription); } - + @Override - public boolean upgradeVirtualMachine(long vmId, long serviceOfferingId) { - UserVmVO vm = _vmDao.createForUpdate(vmId); - vm.setServiceOfferingId(serviceOfferingId); - vm.setHaEnabled(_serviceOfferingDao.findById(serviceOfferingId).getOfferHA()); - return _vmDao.update(vmId, vm); + public boolean upgradeVirtualMachine(UpgradeVMCmd cmd) throws ServerApiException, InvalidParameterValueException + { + Long virtualMachineId = cmd.getId(); + Long serviceOfferingId = cmd.getServiceOfferingId(); + Account account = (Account)UserContext.current().getAccountObject(); + Long userId = UserContext.current().getUserId(); + + // Verify input parameters + + UserVmVO vmInstance = _userVmDao.createForUpdate(virtualMachineId.longValue()); + if (vmInstance == null) { + throw new ServerApiException(BaseCmd.VM_INVALID_PARAM_ERROR, "unable to find a virtual machine with id " + virtualMachineId); + } + + if (account != null) + { + if (!isAdmin(account.getType()) && (account.getId().longValue() != vmInstance.getAccountId())) + { + throw new ServerApiException(BaseCmd.VM_INVALID_PARAM_ERROR, "unable to find a virtual machine with id " + virtualMachineId + " for this account"); + } + else if (_domainDao.isChildDomain(account.getDomainId(),vmInstance.getDomainId())) + { + throw new ServerApiException(BaseCmd.PARAM_ERROR, "Invalid virtual machine id (" + virtualMachineId + ") given, unable to upgrade virtual machine."); + } + } + + // If command is executed via 8096 port, set userId to the id of System account (1) + if (userId == null) { + userId = Long.valueOf(User.UID_SYSTEM); + } + + // Check that the specified service offering ID is valid + ServiceOfferingVO newServiceOffering = _offeringDao.findById(serviceOfferingId); + if (newServiceOffering == null) { + throw new InvalidParameterValueException("Unable to find a service offering with id " + serviceOfferingId); + } + + // Check that the VM is stopped + if (!vmInstance.getState().equals(State.Stopped)) { + s_logger.warn("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState()); + throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " in state " + vmInstance.getState() + "; make sure the virtual machine is stopped and not in an error state before upgrading."); + } + + // Check if the service offering being upgraded to is what the VM is already running with + if (vmInstance.getServiceOfferingId() == newServiceOffering.getId()) { + if (s_logger.isInfoEnabled()) { + s_logger.info("Not upgrading vm " + vmInstance.toString() + " since it already has the requested service offering (" + newServiceOffering.getName() + ")"); + } + + throw new InvalidParameterValueException("Not upgrading vm " + vmInstance.toString() + " since it already has the requested service offering (" + newServiceOffering.getName() + ")"); + } + + // Check that the service offering being upgraded to has the same Guest IP type as the VM's current service offering + ServiceOfferingVO currentServiceOffering = _offeringDao.findById(vmInstance.getServiceOfferingId()); + if (!currentServiceOffering.getGuestIpType().equals(newServiceOffering.getGuestIpType())) { + String errorMsg = "The service offering being upgraded to has a guest IP type: " + newServiceOffering.getGuestIpType(); + errorMsg += ". Please select a service offering with the same guest IP type as the VM's current service offering (" + currentServiceOffering.getGuestIpType() + ")."; + throw new InvalidParameterValueException(errorMsg); + } + + // Check that the service offering being upgraded to has the same storage pool preference as the VM's current service offering + if (currentServiceOffering.getUseLocalStorage() != newServiceOffering.getUseLocalStorage()) { + throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + ", cannot switch between local storage and shared storage service offerings. Current offering useLocalStorage=" + + currentServiceOffering.getUseLocalStorage() + ", target offering useLocalStorage=" + newServiceOffering.getUseLocalStorage()); + } + + // Check that there are enough resources to upgrade the service offering + if (!_agentMgr.isVirtualMachineUpgradable(vmInstance, newServiceOffering)) { + throw new InvalidParameterValueException("Unable to upgrade virtual machine, not enough resources available for an offering of " + + newServiceOffering.getCpu() + " cpu(s) at " + newServiceOffering.getSpeed() + " Mhz, and " + newServiceOffering.getRamSize() + " MB of memory"); + } + + // Check that the service offering being upgraded to has all the tags of the current service offering + List currentTags = _configMgr.csvTagsToList(currentServiceOffering.getTags()); + List newTags = _configMgr.csvTagsToList(newServiceOffering.getTags()); + if (!newTags.containsAll(currentTags)) { + throw new InvalidParameterValueException("Unable to upgrade virtual machine; the new service offering does not have all the tags of the " + + "current service offering. Current service offering tags: " + currentTags + "; " + + "new service offering tags: " + newTags); + } + + long eventId = EventUtils.saveScheduledEvent(userId, vmInstance.getAccountId(), EventTypes.EVENT_VM_UPGRADE, "upgrading Vm with Id: "+vmInstance.getId()); + + vmInstance.setServiceOfferingId(serviceOfferingId); + vmInstance.setHaEnabled(_serviceOfferingDao.findById(serviceOfferingId).getOfferHA()); + return _vmDao.update(vmInstance.getId(), vmInstance); + } @Override @@ -2939,4 +3035,10 @@ public class UserVmManagerImpl implements UserVmManager { } } } + + private static boolean isAdmin(short accountType) { + return ((accountType == Account.ACCOUNT_TYPE_ADMIN) || + (accountType == Account.ACCOUNT_TYPE_DOMAIN_ADMIN) || + (accountType == Account.ACCOUNT_TYPE_READ_ONLY_ADMIN)); + } }