You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by ni...@apache.org on 2013/03/28 12:17:00 UTC
[50/50] [abbrv] git commit: updated refs/heads/master to 3e4430d
CLOUDSTACK-658 - Scaleup vm support for Xenserver
Added the framweork so that it can be extended for vmware and kvm as well.
Added unitests and marvin tests.
Project: http://git-wip-us.apache.org/repos/asf/cloudstack/repo
Commit: http://git-wip-us.apache.org/repos/asf/cloudstack/commit/3e4430d8
Tree: http://git-wip-us.apache.org/repos/asf/cloudstack/tree/3e4430d8
Diff: http://git-wip-us.apache.org/repos/asf/cloudstack/diff/3e4430d8
Branch: refs/heads/master
Commit: 3e4430d811d5e148c4fef1900bdd8dd07a38bdeb
Parents: f8b8f60
Author: Nitin Mehta <ni...@citrix.com>
Authored: Thu Mar 28 16:43:37 2013 +0530
Committer: Nitin Mehta <ni...@citrix.com>
Committed: Thu Mar 28 16:43:37 2013 +0530
----------------------------------------------------------------------
api/src/com/cloud/agent/api/ScaleVmCommand.java | 6 +-
api/src/com/cloud/event/EventTypes.java | 1 +
api/src/com/cloud/vm/UserVmService.java | 16 +-
api/src/com/cloud/vm/VirtualMachine.java | 2 +-
.../cloudstack/api/command/user/vm/ScaleVMCmd.java | 110 +++++
.../api/command/test/ScaleVMCmdTest.java | 122 ++++++
client/tomcatconf/commands.properties.in | 1 +
core/src/com/cloud/vm/VMInstanceVO.java | 2 +-
.../xen/resource/CitrixResourceBase.java | 82 ++++
.../xen/resource/XenServer56FP1Resource.java | 11 +-
.../xen/resource/CitrixResourceBaseTest.java | 148 +++++++
.../xenserver/Add-To-VCPUs-Params-Live.sh | 33 ++
scripts/vm/hypervisor/xenserver/vmops | 13 +
server/src/com/cloud/configuration/Config.java | 3 +-
.../src/com/cloud/server/ManagementServerImpl.java | 1 +
server/src/com/cloud/vm/ItWorkVO.java | 3 +-
server/src/com/cloud/vm/UserVmManagerImpl.java | 92 ++++-
server/src/com/cloud/vm/VirtualMachineManager.java | 12 +
.../com/cloud/vm/VirtualMachineManagerImpl.java | 311 +++++++++++++--
.../test/com/cloud/vm/MockUserVmManagerImpl.java | 17 +-
.../cloud/vm/MockVirtualMachineManagerImpl.java | 17 +
server/test/com/cloud/vm/UserVmManagerTest.java | 192 +++++++++-
.../cloud/vm/VirtualMachineManagerImplTest.java | 208 ++++++++++
test/integration/smoke/test_ScaleVm.py | 221 ++++++++++
24 files changed, 1545 insertions(+), 79 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/api/src/com/cloud/agent/api/ScaleVmCommand.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/agent/api/ScaleVmCommand.java b/api/src/com/cloud/agent/api/ScaleVmCommand.java
index e5078d5..35d22ad 100644
--- a/api/src/com/cloud/agent/api/ScaleVmCommand.java
+++ b/api/src/com/cloud/agent/api/ScaleVmCommand.java
@@ -40,14 +40,14 @@ public class ScaleVmCommand extends Command {
}
public ScaleVmCommand(String vmName, int cpus,
- Integer speed, long minRam, long maxRam) {
+ Integer speed, long minRam, long maxRam, boolean limitCpuUse) {
super();
this.vmName = vmName;
this.cpus = cpus;
- //this.speed = speed;
+ this.speed = speed;
this.minRam = minRam;
this.maxRam = maxRam;
- this.vm = new VirtualMachineTO(1L, vmName, null, cpus, null, minRam, maxRam, null, null, false, false, null);
+ this.vm = new VirtualMachineTO(1L, vmName, null, cpus, speed, minRam, maxRam, null, null, false, false, null);
/*vm.setName(vmName);
vm.setCpus(cpus);
vm.setRam(minRam, maxRam);*/
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/api/src/com/cloud/event/EventTypes.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/event/EventTypes.java b/api/src/com/cloud/event/EventTypes.java
index 6fdd28c..2e961e5 100755
--- a/api/src/com/cloud/event/EventTypes.java
+++ b/api/src/com/cloud/event/EventTypes.java
@@ -59,6 +59,7 @@ public class EventTypes {
public static final String EVENT_VM_REBOOT = "VM.REBOOT";
public static final String EVENT_VM_UPDATE = "VM.UPDATE";
public static final String EVENT_VM_UPGRADE = "VM.UPGRADE";
+ public static final String EVENT_VM_SCALE = "VM.SCALE";
public static final String EVENT_VM_RESETPASSWORD = "VM.RESETPASSWORD";
public static final String EVENT_VM_RESETSSHKEY = "VM.RESETSSHKEY";
public static final String EVENT_VM_MIGRATE = "VM.MIGRATE";
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/api/src/com/cloud/vm/UserVmService.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/UserVmService.java b/api/src/com/cloud/vm/UserVmService.java
index 9d6b221..2c33d41 100755
--- a/api/src/com/cloud/vm/UserVmService.java
+++ b/api/src/com/cloud/vm/UserVmService.java
@@ -23,18 +23,7 @@ import javax.naming.InsufficientResourcesException;
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.AddNicToVMCmd;
-import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
-import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
-import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
-import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd;
-import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd;
-import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd;
-import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
-import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
+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;
@@ -402,4 +391,7 @@ public interface UserVmService {
VirtualMachine vmStorageMigration(Long vmId, StoragePool destPool);
UserVm restoreVM(RestoreVMCmd cmd);
+
+ UserVm upgradeVirtualMachine(ScaleVMCmd scaleVMCmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException;
+
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/api/src/com/cloud/vm/VirtualMachine.java
----------------------------------------------------------------------
diff --git a/api/src/com/cloud/vm/VirtualMachine.java b/api/src/com/cloud/vm/VirtualMachine.java
index 4300dd5..8f807d4 100755
--- a/api/src/com/cloud/vm/VirtualMachine.java
+++ b/api/src/com/cloud/vm/VirtualMachine.java
@@ -176,7 +176,7 @@ public interface VirtualMachine extends RunningOn, ControlledEntity, Identity, I
AgentReportShutdowned,
AgentReportMigrated,
RevertRequested,
- SnapshotRequested
+ SnapshotRequested,
};
public enum Type {
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/api/src/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java
----------------------------------------------------------------------
diff --git a/api/src/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java b/api/src/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java
new file mode 100644
index 0000000..4fc65c3
--- /dev/null
+++ b/api/src/org/apache/cloudstack/api/command/user/vm/ScaleVMCmd.java
@@ -0,0 +1,110 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements. See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership. The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License. You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied. See the License for the
+// specific language governing permissions and limitations
+// under the License.
+package org.apache.cloudstack.api.command.user.vm;
+
+import com.cloud.exception.*;
+import com.cloud.user.Account;
+import com.cloud.user.UserContext;
+import com.cloud.uservm.UserVm;
+import org.apache.cloudstack.api.*;
+import org.apache.cloudstack.api.response.ServiceOfferingResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.log4j.Logger;
+
+
+@APICommand(name = "scaleVirtualMachine", description="Scales the virtual machine to a new service offering.", responseObject=UserVmResponse.class)
+public class ScaleVMCmd extends BaseCmd {
+ public static final Logger s_logger = Logger.getLogger(ScaleVMCmd.class.getName());
+ private static final String s_name = "scalevirtualmachineresponse";
+
+ /////////////////////////////////////////////////////
+ //////////////// 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.SERVICE_OFFERING_ID, type=CommandType.UUID, entityType=ServiceOfferingResponse.class,
+ required=true, description="the ID of the service offering for the virtual machine")
+ private Long serviceOfferingId;
+
+ /////////////////////////////////////////////////////
+ /////////////////// Accessors ///////////////////////
+ /////////////////////////////////////////////////////
+
+ public Long getId() {
+ return id;
+ }
+
+ public Long getServiceOfferingId() {
+ return serviceOfferingId;
+ }
+
+ /////////////////////////////////////////////////////
+ /////////////// 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(){
+ //UserContext.current().setEventDetails("Vm Id: "+getId());
+ UserVm result = null;
+ try {
+ result = _userVmService.upgradeVirtualMachine(this);
+ } catch (ResourceUnavailableException ex) {
+ s_logger.warn("Exception: ", ex);
+ throw new ServerApiException(ApiErrorCode.RESOURCE_UNAVAILABLE_ERROR, ex.getMessage());
+ } catch (ConcurrentOperationException ex) {
+ s_logger.warn("Exception: ", ex);
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
+ } catch (ManagementServerException ex) {
+ s_logger.warn("Exception: ", ex);
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
+ } catch (VirtualMachineMigrationException ex) {
+ s_logger.warn("Exception: ", ex);
+ throw new ServerApiException(ApiErrorCode.INTERNAL_ERROR, ex.getMessage());
+ }
+ 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 scale vm");
+ }
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/api/test/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java
----------------------------------------------------------------------
diff --git a/api/test/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java b/api/test/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java
new file mode 100644
index 0000000..301fa02
--- /dev/null
+++ b/api/test/org/apache/cloudstack/api/command/test/ScaleVMCmdTest.java
@@ -0,0 +1,122 @@
+// 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.test;
+
+import com.cloud.user.Account;
+import com.cloud.user.UserContext;
+import com.cloud.uservm.UserVm;
+import com.cloud.vm.UserVmService;
+import junit.framework.Assert;
+import junit.framework.TestCase;
+import org.apache.cloudstack.api.ResponseGenerator;
+import org.apache.cloudstack.api.ServerApiException;
+import org.apache.cloudstack.api.command.admin.region.AddRegionCmd;
+import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
+import org.apache.cloudstack.api.response.RegionResponse;
+import org.apache.cloudstack.api.response.UserVmResponse;
+import org.apache.cloudstack.region.Region;
+import org.apache.cloudstack.region.RegionService;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.mockito.Mockito;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+
+
+public class ScaleVMCmdTest extends TestCase{
+
+ private ScaleVMCmd scaleVMCmd;
+ private ResponseGenerator responseGenerator;
+
+ @Rule
+ public ExpectedException expectedException = ExpectedException.none();
+
+ @Before
+ public void setUp() {
+
+ scaleVMCmd = new ScaleVMCmd(){
+ @Override
+ public Long getId() {
+ return 2L;
+ }
+ };
+
+ //Account account = new AccountVO("testaccount", 1L, "networkdomain", (short) 0, "uuid");
+ //UserContext.registerContext(1, account, null, true);
+
+
+ }
+
+
+ @Test
+ public void testCreateSuccess() {
+
+ UserVmService userVmService = Mockito.mock(UserVmService.class);
+
+ UserVm uservm = Mockito.mock(UserVm.class);
+ try {
+ Mockito.when(
+ userVmService.upgradeVirtualMachine(scaleVMCmd))
+ .thenReturn(uservm);
+ }catch (Exception e){
+ Assert.fail("Received exception when success expected " +e.getMessage());
+ }
+
+ scaleVMCmd._userVmService = userVmService;
+ responseGenerator = Mockito.mock(ResponseGenerator.class);
+
+ UserVmResponse userVmResponse = Mockito.mock(UserVmResponse.class);
+ List<UserVmResponse> responseList = new ArrayList<UserVmResponse>();
+ responseList.add(userVmResponse);
+
+ Mockito.when(responseGenerator.createUserVmResponse("virtualmachine",uservm))
+ .thenReturn(responseList);
+
+ scaleVMCmd._responseGenerator = responseGenerator;
+ scaleVMCmd.execute();
+
+ }
+
+ @Test
+ public void testCreateFailure() {
+
+ UserVmService userVmService = Mockito.mock(UserVmService.class);
+
+ try {
+ UserVm uservm = Mockito.mock(UserVm.class);
+ Mockito.when(
+ userVmService.upgradeVirtualMachine(scaleVMCmd))
+ .thenReturn(null);
+ }catch (Exception e){
+ Assert.fail("Received exception when success expected " +e.getMessage());
+ }
+
+ scaleVMCmd._userVmService = userVmService;
+
+ try {
+ scaleVMCmd.execute();
+ } catch (ServerApiException exception) {
+ Assert.assertEquals("Failed to scale vm",
+ exception.getDescription());
+ }
+
+ }
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/client/tomcatconf/commands.properties.in
----------------------------------------------------------------------
diff --git a/client/tomcatconf/commands.properties.in b/client/tomcatconf/commands.properties.in
index 3b3ffdf..163c2ce 100644
--- a/client/tomcatconf/commands.properties.in
+++ b/client/tomcatconf/commands.properties.in
@@ -66,6 +66,7 @@ listVirtualMachines=15
getVMPassword=15
restoreVirtualMachine=15
changeServiceForVirtualMachine=15
+scaleVirtualMachine=15
assignVirtualMachine=1
migrateVirtualMachine=1
recoverVirtualMachine=7
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/core/src/com/cloud/vm/VMInstanceVO.java
----------------------------------------------------------------------
diff --git a/core/src/com/cloud/vm/VMInstanceVO.java b/core/src/com/cloud/vm/VMInstanceVO.java
index 6149e43..77e9c02 100644
--- a/core/src/com/cloud/vm/VMInstanceVO.java
+++ b/core/src/com/cloud/vm/VMInstanceVO.java
@@ -480,4 +480,4 @@ public class VMInstanceVO implements VirtualMachine, FiniteStateObject<State, Vi
return diskOfferingId;
}
-}
+}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
index 52d992f..d2f3f69 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/CitrixResourceBase.java
@@ -53,6 +53,7 @@ import javax.ejb.Local;
import javax.naming.ConfigurationException;
import javax.xml.parsers.DocumentBuilderFactory;
+import com.cloud.agent.api.*;
import org.apache.cloudstack.storage.command.StorageSubSystemCommand;
import com.cloud.agent.api.to.*;
import com.cloud.network.rules.FirewallRule;
@@ -600,12 +601,93 @@ public abstract class CitrixResourceBase implements ServerResource, HypervisorRe
return execute((RevertToVMSnapshotCommand)cmd);
} else if (clazz == NetworkRulesVmSecondaryIpCommand.class) {
return execute((NetworkRulesVmSecondaryIpCommand)cmd);
+ } else if (clazz == ScaleVmCommand.class) {
+ return execute((ScaleVmCommand) cmd);
} else {
return Answer.createUnsupportedCommandAnswer(cmd);
}
}
+ protected void scaleVM(Connection conn, VM vm, VirtualMachineTO vmSpec, Host host) throws XenAPIException, XmlRpcException {
+ vm.setMemoryDynamicRange(conn, vmSpec.getMinRam() * 1024 * 1024, vmSpec.getMaxRam() * 1024 * 1024);
+ vm.setVCPUsNumberLive(conn, (long)vmSpec.getCpus());
+
+ Integer speed = vmSpec.getSpeed();
+ if (speed != null) {
+
+ int cpuWeight = _maxWeight; //cpu_weight
+
+ // weight based allocation
+
+ cpuWeight = (int)((speed*0.99) / _host.speed * _maxWeight);
+ if (cpuWeight > _maxWeight) {
+ cpuWeight = _maxWeight;
+ }
+
+ if (vmSpec.getLimitCpuUse()) {
+ long utilization = 0; // max CPU cap, default is unlimited
+ utilization = ((long)speed * 100 * vmSpec.getCpus()) / _host.speed ;
+ vm.addToVCPUsParamsLive(conn, "cap", Long.toString(utilization));
+ }
+ //vm.addToVCPUsParamsLive(conn, "weight", Integer.toString(cpuWeight));
+ callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", Integer.toString(cpuWeight), "vmname", vmSpec.getName() );
+ }
+ }
+
+ public ScaleVmAnswer execute(ScaleVmCommand cmd) {
+ VirtualMachineTO vmSpec = cmd.getVirtualMachine();
+ String vmName = vmSpec.getName();
+ try {
+ Connection conn = getConnection();
+ Set<VM> vms = VM.getByNameLabel(conn, vmName);
+ Host host = Host.getByUuid(conn, _host.uuid);
+ // stop vm which is running on this host or is in halted state
+ Iterator<VM> iter = vms.iterator();
+ while ( iter.hasNext() ) {
+ VM vm = iter.next();
+ VM.Record vmr = vm.getRecord(conn);
+
+ if ((vmr.powerState == VmPowerState.HALTED) || (vmr.powerState == VmPowerState.RUNNING && !isRefNull(vmr.residentOn) && !vmr.residentOn.getUuid(conn).equals(_host.uuid))) {
+ iter.remove();
+ }
+ }
+
+ if (vms.size() == 0) {
+ s_logger.info("No running VM " + vmName +" exists on XenServer" + _host.uuid);
+ return new ScaleVmAnswer(cmd, false, "VM does not exist");
+ }
+
+ for (VM vm : vms) {
+ VM.Record vmr = vm.getRecord(conn);
+ try {
+ scaleVM(conn, vm, vmSpec, host);
+
+ } catch (Exception e) {
+ String msg = "Catch exception " + e.getClass().getName() + " when scaling VM:" + vmName + " due to " + e.toString();
+ s_logger.debug(msg);
+ return new ScaleVmAnswer(cmd, false, msg);
+ }
+
+ }
+ String msg = "scaling VM " + vmName + " is successful on host " + host;
+ s_logger.debug(msg);
+ return new ScaleVmAnswer(cmd, true, msg);
+
+ } catch (XenAPIException e) {
+ String msg = "Upgrade Vm " + vmName + " fail due to " + e.toString();
+ s_logger.warn(msg, e);
+ return new ScaleVmAnswer(cmd, false, msg);
+ } catch (XmlRpcException e) {
+ String msg = "Upgrade Vm " + vmName + " fail due to " + e.getMessage();
+ s_logger.warn(msg, e);
+ return new ScaleVmAnswer(cmd, false, msg);
+ } catch (Exception e) {
+ String msg = "Unable to upgrade " + vmName + " due to " + e.getMessage();
+ s_logger.warn(msg, e);
+ return new ScaleVmAnswer(cmd, false, msg);
+ }
+ }
private Answer execute(RevertToVMSnapshotCommand cmd) {
String vmName = cmd.getVmName();
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java
index 7040311..d64e173 100644
--- a/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java
+++ b/plugins/hypervisors/xen/src/com/cloud/hypervisor/xen/resource/XenServer56FP1Resource.java
@@ -138,9 +138,14 @@ public class XenServer56FP1Resource extends XenServer56Resource {
record.actionsAfterShutdown = Types.OnNormalExit.DESTROY;
record.memoryDynamicMax = vmSpec.getMaxRam();
record.memoryDynamicMin = vmSpec.getMinRam();
- record.memoryStaticMax = vmSpec.getMaxRam();
- record.memoryStaticMin = vmSpec.getMinRam();
- record.VCPUsMax = (long) vmSpec.getCpus();
+ record.memoryStaticMax = 8589934592L; //128GB
+ record.memoryStaticMin = 134217728L; //128MB
+ if (guestOsTypeName.toLowerCase().contains("windows")) {
+ record.VCPUsMax = (long) vmSpec.getCpus();
+ } else {
+ record.VCPUsMax = 32L;
+ }
+
record.VCPUsAtStartup = (long) vmSpec.getCpus();
record.consoles.clear();
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/plugins/hypervisors/xen/test/com/cloud/hypervisor/xen/resource/CitrixResourceBaseTest.java
----------------------------------------------------------------------
diff --git a/plugins/hypervisors/xen/test/com/cloud/hypervisor/xen/resource/CitrixResourceBaseTest.java b/plugins/hypervisors/xen/test/com/cloud/hypervisor/xen/resource/CitrixResourceBaseTest.java
new file mode 100644
index 0000000..7392cb1
--- /dev/null
+++ b/plugins/hypervisors/xen/test/com/cloud/hypervisor/xen/resource/CitrixResourceBaseTest.java
@@ -0,0 +1,148 @@
+/*
+ * 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 com.cloud.hypervisor.xen.resource;
+
+import org.junit.Test;
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.Spy;
+import org.mockito.MockitoAnnotations;
+import static org.mockito.Mockito.*;
+
+import com.cloud.hypervisor.xen.resource.CitrixResourceBase.XsHost;
+import com.cloud.agent.api.ScaleVmCommand;
+import com.cloud.agent.api.to.VirtualMachineTO;
+import com.cloud.agent.api.ScaleVmAnswer;
+import com.xensource.xenapi.*;
+import org.apache.xmlrpc.XmlRpcException;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+import java.util.Iterator;
+
+
+
+
+public class CitrixResourceBaseTest {
+
+ @Spy CitrixResourceBase _resource = new CitrixResourceBase() {
+
+ @Override
+ public ScaleVmAnswer execute(ScaleVmCommand cmd) {
+ return super.execute(cmd);
+ }
+ public String callHostPlugin(Connection conn, String plugin, String cmd, String... params) {
+ return "Success";
+ }
+ @Override
+ protected void scaleVM(Connection conn, VM vm, VirtualMachineTO vmSpec, Host host) throws Types.XenAPIException, XmlRpcException {
+ _host.speed = 500;
+ super.scaleVM(conn, vm, vmSpec, host);
+ }
+
+
+ };
+ @Mock XsHost _host;
+ @Mock Host host;
+ @Mock ScaleVmCommand cmd;
+ @Mock VirtualMachineTO vmSpec;
+ @Mock Connection conn;
+ @Mock VM vm;
+
+ @Before
+ public void setup(){
+
+ MockitoAnnotations.initMocks(this);
+
+ doReturn(vmSpec).when(cmd).getVirtualMachine();
+ doReturn("i-2-3-VM").when(vmSpec).getName();
+
+ }
+
+
+ // Expecting XmlRpcException while trying to get the record of vm using connection
+ @Test(expected = XmlRpcException.class)
+ public void testScaleVMF1() throws
+ Types.BadServerResponse,
+ Types.XenAPIException,
+ XmlRpcException {
+ doReturn(conn).when(_resource).getConnection();
+ Set<VM> vms = (Set<VM> )mock(Set.class);
+
+ Iterator iter = mock(Iterator.class);
+ doReturn(iter).when(vms).iterator();
+ when(iter.hasNext()).thenReturn(true).thenReturn(false);
+ doReturn(vm).when(iter).next();
+ VM.Record vmr = mock(VM.Record.class);
+ when(vm.getRecord(conn)).thenThrow(new XmlRpcException("XmlRpcException"));
+ when(vm.getRecord(conn)).thenReturn(vmr);
+ vmr.powerState = Types.VmPowerState.RUNNING;
+ vmr.residentOn = mock(Host.class);
+ XenAPIObject object = mock(XenAPIObject.class);
+ doReturn(new String("OpaqueRef:NULL")).when(object).toWireString();
+ doNothing().when(_resource).scaleVM(conn, vm, vmSpec, host);
+
+ _resource.execute(cmd);
+ verify(iter, times(2)).hasNext();
+ verify(iter, times(2)).next();
+
+ }
+
+ // Test to scale vm "i-2-3-VM" cpu-cap disabled
+ @Test
+ public void testScaleVMF2() throws Types.XenAPIException, XmlRpcException {
+
+ doReturn(null).when(vm).setMemoryDynamicRangeAsync(conn, 536870912L, 536870912L);
+ doReturn(1).when(vmSpec).getCpus();
+ doNothing().when(vm).setVCPUsNumberLive(conn, 1L);
+ doReturn(500).when(vmSpec).getSpeed();
+ doReturn(false).when(vmSpec).getLimitCpuUse();
+ Map<String, String> args = (Map<String, String>)mock(HashMap.class);
+ when(host.callPlugin(conn, "vmops", "add_to_VCPUs_params_live", args)).thenReturn("Success");
+ doReturn(null).when(_resource).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", "253", "vmname", "i-2-3-VM");
+
+ _resource.scaleVM(conn, vm, vmSpec, host);
+
+ verify(vmSpec, times(1)).getLimitCpuUse();
+ verify(_resource, times(1)).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", "253", "vmname", "i-2-3-VM");
+ }
+
+ // Test to scale vm "i-2-3-VM" cpu-cap enabled
+ @Test
+ public void testScaleVMF3() throws Types.XenAPIException, XmlRpcException {
+
+ doReturn(null).when(vm).setMemoryDynamicRangeAsync(conn, 536870912L, 536870912L);
+ doReturn(1).when(vmSpec).getCpus();
+ doNothing().when(vm).setVCPUsNumberLive(conn, 1L);
+ doReturn(500).when(vmSpec).getSpeed();
+ doReturn(true).when(vmSpec).getLimitCpuUse();
+ doNothing().when(vm).addToVCPUsParamsLive(conn, "cap", "100");
+ Map<String, String> args = (Map<String, String>)mock(HashMap.class);
+ when(host.callPlugin(conn, "vmops", "add_to_VCPUs_params_live", args)).thenReturn("Success");
+ doReturn(null).when(_resource).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", "253", "vmname", "i-2-3-VM");
+
+ _resource.scaleVM(conn, vm, vmSpec, host);
+
+ verify(vmSpec, times(1)).getLimitCpuUse();
+ verify(_resource, times(1)).callHostPlugin(conn, "vmops", "add_to_VCPUs_params_live", "key", "weight", "value", "253", "vmname", "i-2-3-VM");
+ verify(vm, times(1)).addToVCPUsParamsLive(conn, "cap", "100");
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/scripts/vm/hypervisor/xenserver/Add-To-VCPUs-Params-Live.sh
----------------------------------------------------------------------
diff --git a/scripts/vm/hypervisor/xenserver/Add-To-VCPUs-Params-Live.sh b/scripts/vm/hypervisor/xenserver/Add-To-VCPUs-Params-Live.sh
new file mode 100644
index 0000000..0fadcd8
--- /dev/null
+++ b/scripts/vm/hypervisor/xenserver/Add-To-VCPUs-Params-Live.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+# 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.
+
+set -x
+
+vmname=$1
+key=$2
+value=$3
+uuid=`xe vm-list name-label=$vmname | grep uuid | awk '{print $NF}'`
+if [[ $key == "weight" ]]
+then
+ xe vm-param-set VCPUs-params:weight=$value uuid=$uuid
+fi
+if [[ $key == "cap" ]]
+then
+ xe vm-param-set VCPUs-params:cap=$value uuid=$uuid
+fi
+
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/scripts/vm/hypervisor/xenserver/vmops
----------------------------------------------------------------------
diff --git a/scripts/vm/hypervisor/xenserver/vmops b/scripts/vm/hypervisor/xenserver/vmops
index 31c9a59..30b5300 100755
--- a/scripts/vm/hypervisor/xenserver/vmops
+++ b/scripts/vm/hypervisor/xenserver/vmops
@@ -43,6 +43,18 @@ def echo(fn):
return wrapped
@echo
+def add_to_VCPUs_params_live(session, args):
+ key = args['key']
+ value = args['value']
+ vmname = args['vmname']
+ try:
+ cmd = ["bash", "/opt/xensource/bin/Add-To-VCPUs-Params-Live.sh", vmname, key, value]
+ txt = util.pread2(cmd)
+ except:
+ return 'false'
+ return 'true'
+
+@echo
def gethostvmstats(session, args):
collect_host_stats = args['collectHostStats']
consolidation_function = args['consolidationFunction']
@@ -1661,6 +1673,7 @@ if __name__ == "__main__":
"default_network_rules_systemvm":default_network_rules_systemvm,
"network_rules_vmSecondaryIp":network_rules_vmSecondaryIp,
"get_rule_logs_for_vms":get_rule_logs_for_vms,
+ "add_to_VCPUs_params_live":add_to_VCPUs_params_live,
"setLinkLocalIP":setLinkLocalIP,
"cleanup_rules":cleanup_rules,
"bumpUpPriority":bumpUpPriority,
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/src/com/cloud/configuration/Config.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/configuration/Config.java b/server/src/com/cloud/configuration/Config.java
index 1a57a64..ba4ae62 100755
--- a/server/src/com/cloud/configuration/Config.java
+++ b/server/src/com/cloud/configuration/Config.java
@@ -177,7 +177,8 @@ public enum Config {
RouterTemplateId("Advanced", NetworkManager.class, Long.class, "router.template.id", "1", "Default ID for template.", null),
RouterExtraPublicNics("Advanced", NetworkManager.class, Integer.class, "router.extra.public.nics", "2", "specify extra public nics used for virtual router(up to 5)", "0-5"),
StartRetry("Advanced", AgentManager.class, Integer.class, "start.retry", "10", "Number of times to retry create and start commands", null),
- StopRetryInterval("Advanced", HighAvailabilityManager.class, Integer.class, "stop.retry.interval", "600", "Time in seconds between retries to stop or destroy a vm" , null),
+ ScaleRetry("Advanced", AgentManager.class, Integer.class, "scale.retry", "2", "Number of times to retry scaling up the vm", null),
+ StopRetryInterval("Advanced", HighAvailabilityManager.class, Integer.class, "stop.retry.interval", "600", "Time in seconds between retries to stop or destroy a vm" , null),
StorageCleanupInterval("Advanced", StorageManager.class, Integer.class, "storage.cleanup.interval", "86400", "The interval (in seconds) to wait before running the storage cleanup thread.", null),
StorageCleanupEnabled("Advanced", StorageManager.class, Boolean.class, "storage.cleanup.enabled", "true", "Enables/disables the storage cleanup thread.", null),
UpdateWait("Advanced", AgentManager.class, Integer.class, "update.wait", "600", "Time to wait (in seconds) before alerting on a updating agent", null),
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/src/com/cloud/server/ManagementServerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/server/ManagementServerImpl.java b/server/src/com/cloud/server/ManagementServerImpl.java
index f80e0c2..ecd7751 100755
--- a/server/src/com/cloud/server/ManagementServerImpl.java
+++ b/server/src/com/cloud/server/ManagementServerImpl.java
@@ -2115,6 +2115,7 @@ public class ManagementServerImpl extends ManagerBase implements ManagementServe
cmdList.add(DestroyVMCmd.class);
cmdList.add(GetVMPasswordCmd.class);
cmdList.add(ListVMsCmd.class);
+ cmdList.add(ScaleVMCmd.class);
cmdList.add(RebootVMCmd.class);
cmdList.add(RemoveNicFromVMCmd.class);
cmdList.add(ResetVMPasswordCmd.class);
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/src/com/cloud/vm/ItWorkVO.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/ItWorkVO.java b/server/src/com/cloud/vm/ItWorkVO.java
index 6f53495..d218880 100644
--- a/server/src/com/cloud/vm/ItWorkVO.java
+++ b/server/src/com/cloud/vm/ItWorkVO.java
@@ -41,7 +41,8 @@ public class ItWorkVO {
Started,
Release,
Done,
- Migrating
+ Migrating,
+ Reconfiguring
}
@Id
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/src/com/cloud/vm/UserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/UserVmManagerImpl.java b/server/src/com/cloud/vm/UserVmManagerImpl.java
index 82a69bd..6e9cd43 100755
--- a/server/src/com/cloud/vm/UserVmManagerImpl.java
+++ b/server/src/com/cloud/vm/UserVmManagerImpl.java
@@ -32,22 +32,12 @@ import javax.ejb.Local;
import javax.inject.Inject;
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.api.command.admin.vm.AssignVMCmd;
import org.apache.cloudstack.api.command.admin.vm.RecoverVMCmd;
-import org.apache.cloudstack.api.command.user.vm.AddNicToVMCmd;
-import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
-import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
-import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
-import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd;
-import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd;
-import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd;
-import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
-import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
+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.engine.cloud.entity.api.VirtualMachineEntity;
@@ -349,7 +339,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
@Inject
protected SecurityGroupDao _securityGroupDao;
@Inject
- protected CapacityManager _capacityMgr;;
+ protected CapacityManager _capacityMgr;
@Inject
protected VMInstanceDao _vmInstanceDao;
@Inject
@@ -397,6 +387,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
protected String _instance;
protected String _zone;
protected boolean _instanceNameFlag;
+ protected int _scaleRetry;
@Inject ConfigurationDao _configDao;
private int _createprivatetemplatefromvolumewait;
@@ -1048,6 +1039,78 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
}
@Override
+ @ActionEvent(eventType = EventTypes.EVENT_VM_SCALE, eventDescription = "scaling Vm")
+ public UserVm
+ upgradeVirtualMachine(ScaleVMCmd cmd) throws InvalidParameterValueException {
+ Long vmId = cmd.getId();
+ Long newServiceOfferingId = cmd.getServiceOfferingId();
+ Account caller = UserContext.current().getCaller();
+
+ // Verify input parameters
+ VMInstanceVO vmInstance = _vmInstanceDao.findById(vmId);
+ if(vmInstance.getHypervisorType() != HypervisorType.XenServer){
+ throw new InvalidParameterValueException("This operation not permitted for this hypervisor of the vm");
+ }
+
+ _accountMgr.checkAccess(caller, null, true, vmInstance);
+
+ // Check that the specified service offering ID is valid
+ _itMgr.checkIfCanUpgrade(vmInstance, newServiceOfferingId);
+
+ //Check if its a scale "up"
+ ServiceOffering newServiceOffering = _configMgr.getServiceOffering(newServiceOfferingId);
+ ServiceOffering oldServiceOffering = _configMgr.getServiceOffering(vmInstance.getServiceOfferingId());
+ if(newServiceOffering.getSpeed() <= oldServiceOffering.getSpeed()
+ && newServiceOffering.getRamSize() <= oldServiceOffering.getRamSize()){
+ throw new InvalidParameterValueException("Only scaling up the vm is supported, new service offering should have both cpu and memory greater than the old values");
+ }
+
+ // Dynamically upgrade the running vms
+ if(vmInstance.getState().equals(State.Running)){
+ boolean success = false;
+ int retry = _scaleRetry;
+ while (retry-- != 0) { // It's != so that it can match -1.
+ try{
+ // #1 Check existing host has capacity
+ boolean existingHostHasCapacity = _capacityMgr.checkIfHostHasCapacity(vmInstance.getHostId(), newServiceOffering.getSpeed() - oldServiceOffering.getSpeed(),
+ (newServiceOffering.getRamSize() - oldServiceOffering.getRamSize()) * 1024L * 1024L, false, ApiDBUtils.getCpuOverprovisioningFactor(), 1f, false); // TO DO fill it with mem.
+
+ // #2 migrate the vm if host doesn't have capacity
+ if (!existingHostHasCapacity){
+ vmInstance = _itMgr.findHostAndMigrate(vmInstance.getType(), vmInstance, newServiceOfferingId);
+ }
+
+ // #3 scale the vm now
+ _itMgr.upgradeVmDb(vmId, newServiceOfferingId);
+ vmInstance = _vmInstanceDao.findById(vmId);
+ vmInstance = _itMgr.reConfigureVm(vmInstance, oldServiceOffering, existingHostHasCapacity);
+ success = true;
+ return _vmDao.findById(vmInstance.getId());
+ }catch(InsufficientCapacityException e ){
+ s_logger.warn("Received exception while scaling ",e);
+ } catch (ResourceUnavailableException e) {
+ s_logger.warn("Received exception while scaling ",e);
+ } catch (ConcurrentOperationException e) {
+ s_logger.warn("Received exception while scaling ",e);
+ } catch (VirtualMachineMigrationException e) {
+ s_logger.warn("Received exception while scaling ",e);
+ } catch (ManagementServerException e) {
+ s_logger.warn("Received exception while scaling ",e);
+ }finally{
+ if(!success){
+ _itMgr.upgradeVmDb(vmId, oldServiceOffering.getId()); // rollback
+ }
+ }
+ }
+ if (!success)
+ return null;
+ }
+
+ return _vmDao.findById(vmInstance.getId());
+
+ }
+
+ @Override
public HashMap<Long, VmStatsEntry> getVirtualMachineStatistics(long hostId,
String hostName, List<Long> vmIds) throws CloudRuntimeException {
HashMap<Long, VmStatsEntry> vmStatsById = new HashMap<Long, VmStatsEntry>();
@@ -1240,6 +1303,7 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
new UserVmStateListener(_usageEventDao, _networkDao, _nicDao));
value = _configDao.getValue(Config.SetVmInternalNameUsingDisplayName.key());
+
if(value == null) {
_instanceNameFlag = false;
}
@@ -1248,6 +1312,8 @@ public class UserVmManagerImpl extends ManagerBase implements UserVmManager, Use
_instanceNameFlag = Boolean.parseBoolean(value);
}
+ _scaleRetry = NumbersUtil.parseInt(configs.get(Config.ScaleRetry.key()), 2);
+
s_logger.info("User VM Manager is configured.");
return true;
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/src/com/cloud/vm/VirtualMachineManager.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/VirtualMachineManager.java b/server/src/com/cloud/vm/VirtualMachineManager.java
index 7b34f7f0..4a30d97 100644
--- a/server/src/com/cloud/vm/VirtualMachineManager.java
+++ b/server/src/com/cloud/vm/VirtualMachineManager.java
@@ -186,4 +186,16 @@ public interface VirtualMachineManager extends Manager {
*/
VirtualMachineTO toVmTO(VirtualMachineProfile<? extends VMInstanceVO> profile);
+
+ VMInstanceVO reConfigureVm(VMInstanceVO vm, ServiceOffering newServiceOffering, boolean sameHost)
+ throws ResourceUnavailableException, ConcurrentOperationException;
+
+ VMInstanceVO findHostAndMigrate(VirtualMachine.Type vmType, VMInstanceVO vm, Long newSvcOfferingId) throws InsufficientCapacityException,
+ ConcurrentOperationException, ResourceUnavailableException,
+ VirtualMachineMigrationException, ManagementServerException;
+
+ <T extends VMInstanceVO> T migrateForScale(T vm, long srcHostId, DeployDestination dest, Long newSvcOfferingId)
+ throws ResourceUnavailableException, ConcurrentOperationException,
+ ManagementServerException, VirtualMachineMigrationException;
+
}
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
index 0aeef0e..a6d0b1b 100755
--- a/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
+++ b/server/src/com/cloud/vm/VirtualMachineManagerImpl.java
@@ -36,37 +36,18 @@ import javax.ejb.Local;
import javax.inject.Inject;
import javax.naming.ConfigurationException;
+import com.cloud.capacity.CapacityManager;
import org.apache.cloudstack.engine.subsystem.api.storage.DataStoreManager;
import org.apache.cloudstack.storage.datastore.db.PrimaryDataStoreDao;
import com.cloud.dc.*;
+import com.cloud.agent.api.*;
import org.apache.log4j.Logger;
import com.cloud.agent.AgentManager;
import com.cloud.agent.AgentManager.OnError;
import com.cloud.agent.Listener;
-import com.cloud.agent.api.AgentControlAnswer;
-import com.cloud.agent.api.AgentControlCommand;
-import com.cloud.agent.api.Answer;
-import com.cloud.agent.api.CheckVirtualMachineAnswer;
-import com.cloud.agent.api.CheckVirtualMachineCommand;
-import com.cloud.agent.api.ClusterSyncAnswer;
-import com.cloud.agent.api.ClusterSyncCommand;
-import com.cloud.agent.api.Command;
-import com.cloud.agent.api.MigrateAnswer;
-import com.cloud.agent.api.MigrateCommand;
-import com.cloud.agent.api.PingRoutingCommand;
-import com.cloud.agent.api.PrepareForMigrationAnswer;
-import com.cloud.agent.api.PrepareForMigrationCommand;
-import com.cloud.agent.api.RebootAnswer;
-import com.cloud.agent.api.RebootCommand;
-import com.cloud.agent.api.StartAnswer;
-import com.cloud.agent.api.StartCommand;
-import com.cloud.agent.api.StartupCommand;
-import com.cloud.agent.api.StartupRoutingCommand;
import com.cloud.agent.api.StartupRoutingCommand.VmState;
-import com.cloud.agent.api.StopAnswer;
-import com.cloud.agent.api.StopCommand;
import com.cloud.agent.api.to.NicTO;
import com.cloud.agent.api.to.VirtualMachineTO;
import com.cloud.agent.manager.Commands;
@@ -195,6 +176,8 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
@Inject
protected UserVmDao _userVmDao;
@Inject
+ protected CapacityManager _capacityMgr;
+ @Inject
protected NicDao _nicsDao;
@Inject
protected AccountManager _accountMgr;
@@ -2422,13 +2405,12 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
if (newServiceOffering == null) {
throw new InvalidParameterValueException("Unable to find a service offering with id " + newServiceOfferingId);
}
-
- // Check that the VM is stopped
- if (!vmInstance.getState().equals(State.Stopped)) {
+
+ // Check that the VM is stopped / running
+ if (!(vmInstance.getState().equals(State.Stopped) || vmInstance.getState().equals(State.Running) )) {
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.");
+ throw new InvalidParameterValueException("Unable to upgrade virtual machine " + vmInstance.toString() + " " + " in state " + vmInstance.getState()
+ + "; make sure the virtual machine is stopped/running");
}
// Check if the service offering being upgraded to is what the VM is already running with
@@ -2679,4 +2661,277 @@ public class VirtualMachineManagerImpl extends ManagerBase implements VirtualMac
return true;
}
-}
+ @Override
+ public VMInstanceVO findHostAndMigrate(VirtualMachine.Type vmType, VMInstanceVO vm, Long newSvcOfferingId)
+ throws InsufficientCapacityException, ConcurrentOperationException, ResourceUnavailableException, VirtualMachineMigrationException, ManagementServerException {
+
+ VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
+
+ Long srcHostId = vm.getHostId();
+ Long oldSvcOfferingId = vm.getServiceOfferingId();
+ if (srcHostId == null) {
+ throw new CloudRuntimeException("Unable to scale the vm because it doesn't have a host id");
+ }
+ Host host = _hostDao.findById(srcHostId);
+ DataCenterDeployment plan = new DataCenterDeployment(host.getDataCenterId(), host.getPodId(), host.getClusterId(), null, null, null);
+ ExcludeList excludes = new ExcludeList();
+ excludes.addHost(vm.getHostId());
+ vm.setServiceOfferingId(newSvcOfferingId); // Need to find the destination host based on new svc offering
+
+ DeployDestination dest = null;
+
+ for (DeploymentPlanner planner : _planners) {
+ if (planner.canHandle(profile, plan, excludes)) {
+ dest = planner.plan(profile, plan, excludes);
+ } else {
+ continue;
+ }
+
+ if (dest != null) {
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Planner " + planner + " found " + dest + " for scaling the vm to.");
+ }
+ break;
+ }
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Planner " + planner + " was unable to find anything.");
+ }
+ }
+
+ if (dest == null) {
+ throw new InsufficientServerCapacityException("Unable to find a server to scale the vm to.", host.getClusterId());
+ }
+
+ excludes.addHost(dest.getHost().getId());
+ VMInstanceVO vmInstance = null;
+ try {
+ vmInstance = migrateForScale(vm, srcHostId, dest, oldSvcOfferingId);
+ } catch (ResourceUnavailableException e) {
+ s_logger.debug("Unable to migrate to unavailable " + dest);
+ throw e;
+ } catch (ConcurrentOperationException e) {
+ s_logger.debug("Unable to migrate VM due to: " + e.getMessage());
+ throw e;
+ } catch (ManagementServerException e) {
+ s_logger.debug("Unable to migrate VM: " + e.getMessage());
+ throw e;
+ } catch (VirtualMachineMigrationException e) {
+ s_logger.debug("Got VirtualMachineMigrationException, Unable to migrate: " + e.getMessage());
+ if (vm.getState() == State.Starting) {
+ s_logger.debug("VM seems to be still Starting, we should retry migration later");
+ throw e;
+ } else {
+ s_logger.debug("Unable to migrate VM, VM is not in Running or even Starting state, current state: " + vm.getState().toString());
+ }
+ }
+ if (vmInstance != null) {
+ return vmInstance;
+ }else{
+ return null;
+ }
+ }
+
+ @Override
+ public <T extends VMInstanceVO> T migrateForScale(T vm, long srcHostId, DeployDestination dest, Long oldSvcOfferingId) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException,
+ VirtualMachineMigrationException {
+ s_logger.info("Migrating " + vm + " to " + dest);
+
+ Long newSvcOfferingId = vm.getServiceOfferingId();
+ long dstHostId = dest.getHost().getId();
+ Host fromHost = _hostDao.findById(srcHostId);
+ if (fromHost == null) {
+ s_logger.info("Unable to find the host to migrate from: " + srcHostId);
+ throw new CloudRuntimeException("Unable to find the host to migrate from: " + srcHostId);
+ }
+
+ if (fromHost.getClusterId().longValue() != dest.getCluster().getId()) {
+ s_logger.info("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId());
+ throw new CloudRuntimeException("Source and destination host are not in same cluster, unable to migrate to host: " + dest.getHost().getId());
+ }
+
+ VirtualMachineGuru<T> vmGuru = getVmGuru(vm);
+
+ long vmId = vm.getId();
+ vm = vmGuru.findById(vmId);
+ if (vm == null) {
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("Unable to find the vm " + vm);
+ }
+ throw new ManagementServerException("Unable to find a virtual machine with id " + vmId);
+ }
+
+ if (vm.getState() != State.Running) {
+ if (s_logger.isDebugEnabled()) {
+ s_logger.debug("VM is not Running, unable to migrate the vm " + vm);
+ }
+ throw new VirtualMachineMigrationException("VM is not Running, unable to migrate the vm currently " + vm + " , current state: " + vm.getState().toString());
+ }
+
+ short alertType = AlertManager.ALERT_TYPE_USERVM_MIGRATE;
+ if (VirtualMachine.Type.DomainRouter.equals(vm.getType())) {
+ alertType = AlertManager.ALERT_TYPE_DOMAIN_ROUTER_MIGRATE;
+ } else if (VirtualMachine.Type.ConsoleProxy.equals(vm.getType())) {
+ alertType = AlertManager.ALERT_TYPE_CONSOLE_PROXY_MIGRATE;
+ }
+
+ VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
+ _networkMgr.prepareNicForMigration(profile, dest);
+ this.volumeMgr.prepareForMigration(profile, dest);
+
+ VirtualMachineTO to = toVmTO(profile);
+ PrepareForMigrationCommand pfmc = new PrepareForMigrationCommand(to);
+
+ ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Migrating, vm.getType(), vm.getId());
+ work.setStep(Step.Prepare);
+ work.setResourceType(ItWorkVO.ResourceType.Host);
+ work.setResourceId(dstHostId);
+ work = _workDao.persist(work);
+
+ PrepareForMigrationAnswer pfma = null;
+ try {
+ pfma = (PrepareForMigrationAnswer) _agentMgr.send(dstHostId, pfmc);
+ if (!pfma.getResult()) {
+ String msg = "Unable to prepare for migration due to " + pfma.getDetails();
+ pfma = null;
+ throw new AgentUnavailableException(msg, dstHostId);
+ }
+ } catch (OperationTimedoutException e1) {
+ throw new AgentUnavailableException("Operation timed out", dstHostId);
+ } finally {
+ if (pfma == null) {
+ work.setStep(Step.Done);
+ _workDao.update(work.getId(), work);
+ }
+ }
+
+ vm.setLastHostId(srcHostId);
+ try {
+ if (vm == null || vm.getHostId() == null || vm.getHostId() != srcHostId || !changeState(vm, Event.MigrationRequested, dstHostId, work, Step.Migrating)) {
+ s_logger.info("Migration cancelled because state has changed: " + vm);
+ throw new ConcurrentOperationException("Migration cancelled because state has changed: " + vm);
+ }
+ } catch (NoTransitionException e1) {
+ s_logger.info("Migration cancelled because " + e1.getMessage());
+ throw new ConcurrentOperationException("Migration cancelled because " + e1.getMessage());
+ }
+
+ boolean migrated = false;
+ try {
+ boolean isWindows = _guestOsCategoryDao.findById(_guestOsDao.findById(vm.getGuestOSId()).getCategoryId()).getName().equalsIgnoreCase("Windows");
+ MigrateCommand mc = new MigrateCommand(vm.getInstanceName(), dest.getHost().getPrivateIpAddress(), isWindows);
+ mc.setHostGuid(dest.getHost().getGuid());
+
+ try {
+ MigrateAnswer ma = (MigrateAnswer) _agentMgr.send(vm.getLastHostId(), mc);
+ if (!ma.getResult()) {
+ s_logger.error("Unable to migrate due to " + ma.getDetails());
+ return null;
+ }
+ } catch (OperationTimedoutException e) {
+ if (e.isActive()) {
+ s_logger.warn("Active migration command so scheduling a restart for " + vm);
+ _haMgr.scheduleRestart(vm, true);
+ }
+ throw new AgentUnavailableException("Operation timed out on migrating " + vm, dstHostId);
+ }
+
+ try {
+ long newServiceOfferingId = vm.getServiceOfferingId();
+ vm.setServiceOfferingId(oldSvcOfferingId); // release capacity for the old service offering only
+ if (!changeState(vm, VirtualMachine.Event.OperationSucceeded, dstHostId, work, Step.Started)) {
+ throw new ConcurrentOperationException("Unable to change the state for " + vm);
+ }
+ vm.setServiceOfferingId(newServiceOfferingId);
+ } catch (NoTransitionException e1) {
+ throw new ConcurrentOperationException("Unable to change state due to " + e1.getMessage());
+ }
+
+ try {
+ if (!checkVmOnHost(vm, dstHostId)) {
+ s_logger.error("Unable to complete migration for " + vm);
+ try {
+ _agentMgr.send(srcHostId, new Commands(cleanup(vm.getInstanceName())), null);
+ } catch (AgentUnavailableException e) {
+ s_logger.error("AgentUnavailableException while cleanup on source host: " + srcHostId);
+ }
+ cleanup(vmGuru, new VirtualMachineProfileImpl<T>(vm), work, Event.AgentReportStopped, true, _accountMgr.getSystemUser(), _accountMgr.getSystemAccount());
+ return null;
+ }
+ } catch (OperationTimedoutException e) {
+ }
+
+ migrated = true;
+ return vm;
+ } finally {
+ if (!migrated) {
+ s_logger.info("Migration was unsuccessful. Cleaning up: " + vm);
+
+ _alertMgr.sendAlert(alertType, fromHost.getDataCenterId(), fromHost.getPodId(), "Unable to migrate vm " + vm.getInstanceName() + " from host " + fromHost.getName() + " in zone "
+ + dest.getDataCenter().getName() + " and pod " + dest.getPod().getName(), "Migrate Command failed. Please check logs.");
+ try {
+ _agentMgr.send(dstHostId, new Commands(cleanup(vm.getInstanceName())), null);
+ } catch (AgentUnavailableException ae) {
+ s_logger.info("Looks like the destination Host is unavailable for cleanup");
+ }
+
+ try {
+ stateTransitTo(vm, Event.OperationFailed, srcHostId);
+ } catch (NoTransitionException e) {
+ s_logger.warn(e.getMessage());
+ }
+ }
+
+ work.setStep(Step.Done);
+ _workDao.update(work.getId(), work);
+ }
+ }
+ @Override
+ public VMInstanceVO reConfigureVm(VMInstanceVO vm , ServiceOffering oldServiceOffering, boolean reconfiguringOnExistingHost) throws ResourceUnavailableException, ConcurrentOperationException {
+
+ long newServiceofferingId = vm.getServiceOfferingId();
+ ServiceOffering newServiceOffering = _configMgr.getServiceOffering(newServiceofferingId);
+ ScaleVmCommand reconfigureCmd = new ScaleVmCommand(vm.getInstanceName(), newServiceOffering.getCpu(),
+ newServiceOffering.getSpeed(), newServiceOffering.getRamSize(), newServiceOffering.getRamSize(), newServiceOffering.getLimitCpuUse());
+
+ Long dstHostId = vm.getHostId();
+ ItWorkVO work = new ItWorkVO(UUID.randomUUID().toString(), _nodeId, State.Running, vm.getType(), vm.getId());
+ work.setStep(Step.Prepare);
+ work.setResourceType(ItWorkVO.ResourceType.Host);
+ work.setResourceId(vm.getHostId());
+ work = _workDao.persist(work);
+ boolean success = false;
+ try {
+ if(reconfiguringOnExistingHost){
+ vm.setServiceOfferingId(oldServiceOffering.getId());
+ _capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); //release the old capacity
+ vm.setServiceOfferingId(newServiceofferingId);
+ _capacityMgr.allocateVmCapacity(vm, false); // lock the new capacity
+ }
+
+ Answer reconfigureAnswer = _agentMgr.send(vm.getHostId(), reconfigureCmd);
+ if (reconfigureAnswer == null || !reconfigureAnswer.getResult()) {
+ s_logger.error("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
+ throw new CloudRuntimeException("Unable to scale vm due to " + (reconfigureAnswer == null ? "" : reconfigureAnswer.getDetails()));
+ }
+
+ success = true;
+ } catch (OperationTimedoutException e) {
+ throw new AgentUnavailableException("Operation timed out on reconfiguring " + vm, dstHostId);
+ } catch (AgentUnavailableException e) {
+ throw e;
+ } finally{
+ // work.setStep(Step.Done);
+ //_workDao.update(work.getId(), work);
+ if(!success){
+ _capacityMgr.releaseVmCapacity(vm, false, false, vm.getHostId()); // release the new capacity
+ vm.setServiceOfferingId(oldServiceOffering.getId());
+ _capacityMgr.allocateVmCapacity(vm, false); // allocate the old capacity
+ }
+ }
+
+ return vm;
+
+ }
+
+
+ }
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/test/com/cloud/vm/MockUserVmManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/MockUserVmManagerImpl.java b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
index 09825a8..dd8dd83 100644
--- a/server/test/com/cloud/vm/MockUserVmManagerImpl.java
+++ b/server/test/com/cloud/vm/MockUserVmManagerImpl.java
@@ -25,18 +25,7 @@ import javax.naming.ConfigurationException;
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.AddNicToVMCmd;
-import org.apache.cloudstack.api.command.user.vm.DeployVMCmd;
-import org.apache.cloudstack.api.command.user.vm.DestroyVMCmd;
-import org.apache.cloudstack.api.command.user.vm.RebootVMCmd;
-import org.apache.cloudstack.api.command.user.vm.RemoveNicFromVMCmd;
-import org.apache.cloudstack.api.command.user.vm.ResetVMPasswordCmd;
-import org.apache.cloudstack.api.command.user.vm.ResetVMSSHKeyCmd;
-import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
-import org.apache.cloudstack.api.command.user.vm.StartVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpdateDefaultNicForVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpdateVMCmd;
-import org.apache.cloudstack.api.command.user.vm.UpgradeVMCmd;
+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.springframework.stereotype.Component;
@@ -391,6 +380,10 @@ public class MockUserVmManagerImpl extends ManagerBase implements UserVmManager,
return null;
}
+ @Override
+ public UserVm upgradeVirtualMachine(ScaleVMCmd scaleVMCmd) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
@Override
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java b/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java
index 612c70d..4917e77 100755
--- a/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java
+++ b/server/test/com/cloud/vm/MockVirtualMachineManagerImpl.java
@@ -265,6 +265,23 @@ public class MockVirtualMachineManagerImpl extends ManagerBase implements Virtua
return null;
}
+ @Override
+ public VMInstanceVO reConfigureVm(VMInstanceVO vm, ServiceOffering newServiceOffering, boolean sameHost) throws ResourceUnavailableException, ConcurrentOperationException {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public VMInstanceVO findHostAndMigrate(VirtualMachine.Type vmType, VMInstanceVO vm, Long newSvcOfferingId) throws InsufficientCapacityException,
+ ConcurrentOperationException, ResourceUnavailableException,
+ VirtualMachineMigrationException, ManagementServerException{
+ return null;
+ }
+
+ @Override
+ public <T extends VMInstanceVO> T migrateForScale(T vm, long srcHostId, DeployDestination dest, Long newSvcOfferingId) throws ResourceUnavailableException, ConcurrentOperationException, ManagementServerException, VirtualMachineMigrationException {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
/* (non-Javadoc)
* @see com.cloud.vm.VirtualMachineManager#addVmToNetwork(com.cloud.vm.VirtualMachine, com.cloud.network.Network, com.cloud.vm.NicProfile)
*/
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/test/com/cloud/vm/UserVmManagerTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/UserVmManagerTest.java b/server/test/com/cloud/vm/UserVmManagerTest.java
index 0795a35..bb1c07b 100755
--- a/server/test/com/cloud/vm/UserVmManagerTest.java
+++ b/server/test/com/cloud/vm/UserVmManagerTest.java
@@ -17,10 +17,21 @@
package com.cloud.vm;
+import java.lang.reflect.Field;
import java.util.List;
+import com.cloud.api.ApiDBUtils;
+import com.cloud.capacity.CapacityManager;
+import com.cloud.configuration.ConfigurationManager;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.hypervisor.Hypervisor;
+import com.cloud.offering.ServiceOffering;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.user.*;
+import com.cloud.vm.dao.VMInstanceDao;
import org.apache.cloudstack.api.ServerApiException;
import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.Before;
@@ -39,10 +50,6 @@ import com.cloud.storage.VolumeManager;
import com.cloud.storage.VolumeVO;
import com.cloud.storage.dao.VMTemplateDao;
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.UserVO;
import com.cloud.user.dao.AccountDao;
import com.cloud.user.dao.UserDao;
import com.cloud.utils.exception.CloudRuntimeException;
@@ -57,15 +64,21 @@ public class UserVmManagerTest {
@Mock VolumeManager _storageMgr;
@Mock Account _account;
@Mock AccountManager _accountMgr;
+ @Mock ConfigurationManager _configMgr;
+ @Mock CapacityManager _capacityMgr;
@Mock AccountDao _accountDao;
+ @Mock
+ ConfigurationDao _configDao;
@Mock UserDao _userDao;
@Mock UserVmDao _vmDao;
+ @Mock VMInstanceDao _vmInstanceDao;
@Mock VMTemplateDao _templateDao;
@Mock VolumeDao _volsDao;
@Mock RestoreVMCmd _restoreVMCmd;
@Mock AccountVO _accountMock;
@Mock UserVO _userMock;
@Mock UserVmVO _vmMock;
+ @Mock VMInstanceVO _vmInstance;
@Mock VMTemplateVO _templateMock;
@Mock VolumeVO _volumeMock;
@Mock List<VolumeVO> _rootVols;
@@ -74,6 +87,7 @@ public class UserVmManagerTest {
MockitoAnnotations.initMocks(this);
_userVmMgr._vmDao = _vmDao;
+ _userVmMgr._vmInstanceDao = _vmInstanceDao;
_userVmMgr._templateDao = _templateDao;
_userVmMgr._volsDao = _volsDao;
_userVmMgr._itMgr = _itMgr;
@@ -81,6 +95,9 @@ public class UserVmManagerTest {
_userVmMgr._accountDao = _accountDao;
_userVmMgr._userDao = _userDao;
_userVmMgr._accountMgr = _accountMgr;
+ _userVmMgr._configMgr = _configMgr;
+ _userVmMgr._capacityMgr = _capacityMgr;
+ _userVmMgr._scaleRetry = 2;
doReturn(3L).when(_account).getId();
doReturn(8L).when(_vmMock).getAccountId();
@@ -88,6 +105,8 @@ public class UserVmManagerTest {
when(_userDao.findById(anyLong())).thenReturn(_userMock);
doReturn(Account.State.enabled).when(_account).getState();
when(_vmMock.getId()).thenReturn(314L);
+ when(_vmInstance.getId()).thenReturn(1L);
+ when(_vmInstance.getServiceOfferingId()).thenReturn(2L);
}
@@ -179,4 +198,169 @@ public class UserVmManagerTest {
}
+ // Test scaleVm on incompatible HV.
+ @Test(expected=InvalidParameterValueException.class)
+ public void testScaleVMF1() throws Exception {
+
+ ScaleVMCmd cmd = new ScaleVMCmd();
+ Class<?> _class = cmd.getClass();
+
+ Field idField = _class.getDeclaredField("id");
+ idField.setAccessible(true);
+ idField.set(cmd, 1L);
+
+ Field serviceOfferingIdField = _class.getDeclaredField("serviceOfferingId");
+ serviceOfferingIdField.setAccessible(true);
+ serviceOfferingIdField.set(cmd, 1L);
+
+ // UserContext.current().setEventDetails("Vm Id: "+getId());
+ Account account = (Account) new AccountVO("testaccount", 1L, "networkdomain", (short) 0, "uuid");
+ //AccountVO(String accountName, long domainId, String networkDomain, short type, int regionId)
+ UserContext.registerContext(1, account, null, true);
+
+ when(_vmInstanceDao.findById(anyLong())).thenReturn(_vmInstance);
+
+ _userVmMgr.upgradeVirtualMachine(cmd);
+
+ }
+
+ // Test scaleVm on incompatible HV.
+ @Test(expected=InvalidParameterValueException.class)
+ public void testScaleVMF2() throws Exception {
+
+ ScaleVMCmd cmd = new ScaleVMCmd();
+ Class<?> _class = cmd.getClass();
+
+ Field idField = _class.getDeclaredField("id");
+ idField.setAccessible(true);
+ idField.set(cmd, 1L);
+
+ Field serviceOfferingIdField = _class.getDeclaredField("serviceOfferingId");
+ serviceOfferingIdField.setAccessible(true);
+ serviceOfferingIdField.set(cmd, 1L);
+
+ //UserContext.current().setEventDetails("Vm Id: "+getId());
+ // Account account = (Account) new AccountVO("testaccount", 1L, "networkdomain", (short) 0, 1);
+ //AccountVO(String accountName, long domainId, String networkDomain, short type, int regionId)
+ // UserContext.registerContext(1, account, null, true);
+
+ when(_vmInstanceDao.findById(anyLong())).thenReturn(_vmInstance);
+ doReturn(Hypervisor.HypervisorType.XenServer).when(_vmInstance).getHypervisorType();
+
+
+ doNothing().when(_accountMgr).checkAccess(_account, null, true, _templateMock);
+
+ doNothing().when(_itMgr).checkIfCanUpgrade(_vmMock, cmd.getServiceOfferingId());
+
+
+ ServiceOffering so1 = (ServiceOffering) getSvcoffering(512);
+ ServiceOffering so2 = (ServiceOffering) getSvcoffering(256);
+
+ when(_configMgr.getServiceOffering(anyLong())).thenReturn(so1);
+ when(_configMgr.getServiceOffering(1L)).thenReturn(so1);
+
+ _userVmMgr.upgradeVirtualMachine(cmd);
+
+ }
+
+ // Test scaleVm for Stopped vm. Full positive test.
+ @Test
+ public void testScaleVMF3() throws Exception {
+
+ ScaleVMCmd cmd = new ScaleVMCmd();
+ Class<?> _class = cmd.getClass();
+
+ Field idField = _class.getDeclaredField("id");
+ idField.setAccessible(true);
+ idField.set(cmd, 1L);
+
+ Field serviceOfferingIdField = _class.getDeclaredField("serviceOfferingId");
+ serviceOfferingIdField.setAccessible(true);
+ serviceOfferingIdField.set(cmd, 1L);
+
+ //UserContext.current().setEventDetails("Vm Id: "+getId());
+ //Account account = (Account) new AccountVO("testaccount", 1L, "networkdomain", (short) 0, 1);
+ //AccountVO(String accountName, long domainId, String networkDomain, short type, int regionId)
+ //UserContext.registerContext(1, account, null, true);
+
+ when(_vmInstanceDao.findById(anyLong())).thenReturn(_vmInstance);
+ doReturn(Hypervisor.HypervisorType.XenServer).when(_vmInstance).getHypervisorType();
+
+
+ ServiceOffering so1 = (ServiceOffering) getSvcoffering(512);
+ ServiceOffering so2 = (ServiceOffering) getSvcoffering(256);
+
+ when(_configMgr.getServiceOffering(anyLong())).thenReturn(so2);
+ when(_configMgr.getServiceOffering(1L)).thenReturn(so1);
+
+ doReturn(VirtualMachine.State.Stopped).when(_vmInstance).getState();
+
+ doReturn(true).when(_itMgr).upgradeVmDb(anyLong(),anyLong());
+
+ when(_vmDao.findById(anyLong())).thenReturn(_vmMock);
+
+ _userVmMgr.upgradeVirtualMachine(cmd);
+
+ }
+
+ // Test scaleVm for Running vm. Full positive test.
+ public void testScaleVMF4() throws Exception {
+
+ ScaleVMCmd cmd = new ScaleVMCmd();
+ Class<?> _class = cmd.getClass();
+
+ Field idField = _class.getDeclaredField("id");
+ idField.setAccessible(true);
+ idField.set(cmd, 1L);
+
+ Field serviceOfferingIdField = _class.getDeclaredField("serviceOfferingId");
+ serviceOfferingIdField.setAccessible(true);
+ serviceOfferingIdField.set(cmd, 1L);
+
+ //UserContext.current().setEventDetails("Vm Id: "+getId());
+ //Account account = (Account) new AccountVO("testaccount", 1L, "networkdomain", (short) 0, 1);
+ //AccountVO(String accountName, long domainId, String networkDomain, short type, int regionId)
+ //UserContext.registerContext(1, account, null, true);
+
+ when(_vmInstanceDao.findById(anyLong())).thenReturn(_vmInstance);
+ doReturn(Hypervisor.HypervisorType.XenServer).when(_vmInstance).getHypervisorType();
+
+ ServiceOffering so1 = (ServiceOffering) getSvcoffering(512);
+ ServiceOffering so2 = (ServiceOffering) getSvcoffering(256);
+
+ when(_configMgr.getServiceOffering(anyLong())).thenReturn(so2);
+ when(_configMgr.getServiceOffering(1L)).thenReturn(so1);
+
+ doReturn(VirtualMachine.State.Running).when(_vmInstance).getState();
+
+ //when(ApiDBUtils.getCpuOverprovisioningFactor()).thenReturn(3f);
+ when(_capacityMgr.checkIfHostHasCapacity(anyLong(), anyInt(), anyLong(), anyBoolean(), anyFloat(), anyFloat(), anyBoolean())).thenReturn(false);
+ when(_itMgr.reConfigureVm(_vmInstance, so1, false)).thenReturn(_vmInstance);
+
+ doReturn(true).when(_itMgr).upgradeVmDb(anyLong(), anyLong());
+
+ when(_vmDao.findById(anyLong())).thenReturn(_vmMock);
+
+ _userVmMgr.upgradeVirtualMachine(cmd);
+
+ }
+
+ private ServiceOfferingVO getSvcoffering(int ramSize){
+
+ long id = 4L;
+ String name = "name";
+ String displayText = "displayText";
+ int cpu = 1;
+ //int ramSize = 256;
+ int speed = 128;
+
+ boolean ha = false;
+ boolean useLocalStorage = false;
+
+ ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, useLocalStorage, false, null, false, null, false);
+ return serviceOffering;
+ }
+
+
+
}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cloudstack/blob/3e4430d8/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java
----------------------------------------------------------------------
diff --git a/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java b/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java
new file mode 100644
index 0000000..322f051
--- /dev/null
+++ b/server/test/com/cloud/vm/VirtualMachineManagerImplTest.java
@@ -0,0 +1,208 @@
+// 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 com.cloud.vm;
+
+import com.cloud.agent.AgentManager;
+import com.cloud.agent.api.Answer;
+import com.cloud.agent.api.ScaleVmAnswer;
+import com.cloud.agent.api.ScaleVmCommand;
+import com.cloud.capacity.CapacityManager;
+import com.cloud.configuration.ConfigurationManager;
+import com.cloud.configuration.dao.ConfigurationDao;
+import com.cloud.deploy.DeployDestination;
+import com.cloud.host.HostVO;
+import com.cloud.host.dao.HostDao;
+import com.cloud.service.ServiceOfferingVO;
+import com.cloud.storage.VMTemplateVO;
+import com.cloud.storage.VolumeManager;
+import com.cloud.storage.VolumeVO;
+import com.cloud.storage.dao.VMTemplateDao;
+import com.cloud.storage.dao.VolumeDao;
+import com.cloud.user.*;
+import com.cloud.user.dao.AccountDao;
+import com.cloud.user.dao.UserDao;
+import com.cloud.utils.exception.CloudRuntimeException;
+import com.cloud.vm.dao.UserVmDao;
+import com.cloud.vm.dao.VMInstanceDao;
+import org.apache.cloudstack.api.command.user.vm.RestoreVMCmd;
+import org.apache.cloudstack.api.command.user.vm.ScaleVMCmd;
+import org.junit.Test;
+import org.junit.Before;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.Spy;
+import static org.mockito.Mockito.*;
+
+
+import java.lang.reflect.Field;
+import java.util.List;
+
+public class VirtualMachineManagerImplTest {
+
+
+ @Spy VirtualMachineManagerImpl _vmMgr = new VirtualMachineManagerImpl();
+ @Mock
+ VolumeManager _storageMgr;
+ @Mock
+ Account _account;
+ @Mock
+ AccountManager _accountMgr;
+ @Mock
+ ConfigurationManager _configMgr;
+ @Mock
+ CapacityManager _capacityMgr;
+ @Mock
+ AgentManager _agentMgr;
+ @Mock
+ AccountDao _accountDao;
+ @Mock
+ ConfigurationDao _configDao;
+ @Mock
+ HostDao _hostDao;
+ @Mock
+ UserDao _userDao;
+ @Mock
+ UserVmDao _vmDao;
+ @Mock
+ ItWorkDao _workDao;
+ @Mock
+ VMInstanceDao _vmInstanceDao;
+ @Mock
+ VMTemplateDao _templateDao;
+ @Mock
+ VolumeDao _volsDao;
+ @Mock
+ RestoreVMCmd _restoreVMCmd;
+ @Mock
+ AccountVO _accountMock;
+ @Mock
+ UserVO _userMock;
+ @Mock
+ UserVmVO _vmMock;
+ @Mock
+ VMInstanceVO _vmInstance;
+ @Mock
+ HostVO _host;
+ @Mock
+ VMTemplateVO _templateMock;
+ @Mock
+ VolumeVO _volumeMock;
+ @Mock
+ List<VolumeVO> _rootVols;
+ @Mock
+ ItWorkVO _work;
+ @Before
+ public void setup(){
+ MockitoAnnotations.initMocks(this);
+
+ _vmMgr._templateDao = _templateDao;
+ _vmMgr._volsDao = _volsDao;
+ _vmMgr.volumeMgr = _storageMgr;
+ _vmMgr._accountDao = _accountDao;
+ _vmMgr._userDao = _userDao;
+ _vmMgr._accountMgr = _accountMgr;
+ _vmMgr._configMgr = _configMgr;
+ _vmMgr._capacityMgr = _capacityMgr;
+ _vmMgr._hostDao = _hostDao;
+ _vmMgr._nodeId = 1L;
+ _vmMgr._workDao = _workDao;
+ _vmMgr._agentMgr = _agentMgr;
+
+ when(_vmMock.getId()).thenReturn(314l);
+ when(_vmInstance.getId()).thenReturn(1L);
+ when(_vmInstance.getServiceOfferingId()).thenReturn(2L);
+ when(_vmInstance.getInstanceName()).thenReturn("myVm");
+ when(_vmInstance.getHostId()).thenReturn(2L);
+ when(_vmInstance.getType()).thenReturn(VirtualMachine.Type.User);
+ when(_host.getId()).thenReturn(1L);
+ when(_hostDao.findById(anyLong())).thenReturn(null);
+ when(_configMgr.getServiceOffering(anyLong())).thenReturn(getSvcoffering(512));
+ when(_workDao.persist(_work)).thenReturn(_work);
+ when(_workDao.update("1", _work)).thenReturn(true);
+ when(_work.getId()).thenReturn("1");
+ doNothing().when(_work).setStep(ItWorkVO.Step.Done);
+ //doNothing().when(_volsDao).detachVolume(anyLong());
+ //when(_work.setStep(ItWorkVO.Step.Done)).thenReturn("1");
+
+ }
+
+
+ @Test(expected=CloudRuntimeException.class)
+ public void testScaleVM1() throws Exception {
+
+
+ DeployDestination dest = new DeployDestination(null, null, null, _host);
+ long l = 1L;
+
+ when(_vmInstanceDao.findById(anyLong())).thenReturn(_vmInstance);
+ _vmMgr.migrateForScale(_vmInstance, l, dest, l);
+
+ }
+
+ @Test (expected=CloudRuntimeException.class)
+ public void testScaleVM2() throws Exception {
+
+ DeployDestination dest = new DeployDestination(null, null, null, _host);
+ long l = 1L;
+
+ when(_vmInstanceDao.findById(anyLong())).thenReturn(_vmInstance);
+ ServiceOfferingVO newServiceOffering = getSvcoffering(512);
+ ScaleVmCommand reconfigureCmd = new ScaleVmCommand("myVmName", newServiceOffering.getCpu(),
+ newServiceOffering.getSpeed(), newServiceOffering.getRamSize(), newServiceOffering.getRamSize(), newServiceOffering.getLimitCpuUse());
+ Answer answer = new ScaleVmAnswer(reconfigureCmd, true, "details");
+ when(_agentMgr.send(2l, reconfigureCmd)).thenReturn(null);
+ _vmMgr.reConfigureVm(_vmInstance, getSvcoffering(256), false);
+
+ }
+
+ @Test (expected=CloudRuntimeException.class)
+ public void testScaleVM3() throws Exception {
+
+ /*VirtualMachineProfile<VMInstanceVO> profile = new VirtualMachineProfileImpl<VMInstanceVO>(vm);
+
+ Long srcHostId = vm.getHostId();
+ Long oldSvcOfferingId = vm.getServiceOfferingId();
+ if (srcHostId == null) {
+ throw new CloudRuntimeException("Unable to scale the vm because it doesn't have a host id");
+ }*/
+
+ when(_vmInstance.getHostId()).thenReturn(null);
+ when(_vmInstanceDao.findById(anyLong())).thenReturn(_vmInstance);
+ _vmMgr.findHostAndMigrate(VirtualMachine.Type.User, _vmInstance, 2l);
+
+ }
+
+
+ private ServiceOfferingVO getSvcoffering(int ramSize){
+
+ long id = 4L;
+ String name = "name";
+ String displayText = "displayText";
+ int cpu = 1;
+ //int ramSize = 256;
+ int speed = 128;
+
+ boolean ha = false;
+ boolean useLocalStorage = false;
+
+ ServiceOfferingVO serviceOffering = new ServiceOfferingVO(name, cpu, ramSize, speed, null, null, ha, displayText, useLocalStorage, false, null, false, null, false);
+ return serviceOffering;
+ }
+
+
+}