2017-01-16 20:59:50 +01:00
|
|
|
|
/* Copyright (c) Citrix Systems, Inc.
|
2013-06-24 13:41:48 +02:00
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
* Redistribution and use in source and binary forms,
|
|
|
|
|
* with or without modification, are permitted provided
|
|
|
|
|
* that the following conditions are met:
|
|
|
|
|
*
|
|
|
|
|
* * Redistributions of source code must retain the above
|
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
|
* following disclaimer.
|
|
|
|
|
* * Redistributions in binary form must reproduce the above
|
|
|
|
|
* copyright notice, this list of conditions and the
|
|
|
|
|
* following disclaimer in the documentation and/or other
|
|
|
|
|
* materials provided with the distribution.
|
|
|
|
|
*
|
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
|
|
|
|
|
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
|
|
|
|
|
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
|
|
|
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
|
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
|
|
|
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
using System;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Linq;
|
|
|
|
|
using NUnit.Framework;
|
|
|
|
|
using XenAdmin;
|
|
|
|
|
using XenAdmin.Commands;
|
|
|
|
|
using XenAPI;
|
|
|
|
|
|
|
|
|
|
namespace XenAdminTests.CommandTests
|
|
|
|
|
{
|
|
|
|
|
[TestFixture, Category(TestCategories.UICategoryA)]
|
|
|
|
|
public class MigrateVirtualDiskCommandTest : DatabaseTester_TestFixture
|
|
|
|
|
{
|
|
|
|
|
private readonly IMainWindow mw = new MockMainWindow();
|
|
|
|
|
private const string dbName = "tampa_livevdimove_xapi-db.xml";
|
|
|
|
|
|
|
|
|
|
public MigrateVirtualDiskCommandTest() : base(dbName){}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void CheckContextMenuText()
|
|
|
|
|
{
|
2016-10-06 16:49:36 +02:00
|
|
|
|
var selectedVdi1 = new SelectedItem(new VDI());
|
|
|
|
|
var selectedVdi2 = new SelectedItem(new VDI());
|
|
|
|
|
|
|
|
|
|
var cmd = new MigrateVirtualDiskCommand(mw, new[] { selectedVdi1 });
|
2013-06-24 13:41:48 +02:00
|
|
|
|
Assert.AreEqual(Messages.MOVE_VDI_CONTEXT_MENU, cmd.ContextMenuText);
|
2016-10-06 16:49:36 +02:00
|
|
|
|
|
|
|
|
|
cmd = new MigrateVirtualDiskCommand(mw, new[] { selectedVdi1, selectedVdi2 });
|
|
|
|
|
Assert.AreEqual(Messages.MAINWINDOW_MOVE_OBJECTS, cmd.ContextMenuText);
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void CommandCanExecuteWithSelectionOfSrAndVdiOnRunningVms()
|
|
|
|
|
{
|
|
|
|
|
foreach (SrTuple srTuple in runningVmCanMigrateVdiData)
|
|
|
|
|
{
|
|
|
|
|
SR sr; VDI vdi;
|
|
|
|
|
ResolveSrAndVdiNames(srTuple.SrName, srTuple.VdiName, out sr, out vdi);
|
2016-09-27 22:14:07 +02:00
|
|
|
|
var cmd = new MigrateVirtualDiskCommand(mw, new[] {new SelectedItem(vdi)});
|
2013-06-24 13:41:48 +02:00
|
|
|
|
Assert.AreEqual(srTuple.ExpectedCanMigrate, cmd.CanExecute(),
|
|
|
|
|
String.Format("VDI '{0}' on SR '{1}' can migrate", srTuple.VdiName, srTuple.SrName));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void CommandCannotExecuteNullVdi()
|
|
|
|
|
{
|
2016-10-06 16:49:36 +02:00
|
|
|
|
Assert.Throws<ArgumentNullException>(() => new MigrateVirtualDiskCommand(mw, null), "Null vdi selection");
|
2013-06-24 13:41:48 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void CommandCanExecuteWithSelectionOfSrAndVdiOnShutdownVms()
|
|
|
|
|
{
|
|
|
|
|
ShutdownAllVMs();
|
|
|
|
|
|
|
|
|
|
foreach (SrTuple srTuple in runningVmCanMigrateVdiData)
|
|
|
|
|
{
|
|
|
|
|
SR sr; VDI vdi;
|
|
|
|
|
ResolveSrAndVdiNames(srTuple.SrName, srTuple.VdiName, out sr, out vdi);
|
2016-09-27 22:14:07 +02:00
|
|
|
|
var cmd = new MigrateVirtualDiskCommand(mw, new[] { new SelectedItem(vdi) });
|
2013-06-24 13:41:48 +02:00
|
|
|
|
Assert.IsFalse(cmd.CanExecute(),
|
|
|
|
|
String.Format("VDI '{0}' on SR '{1}' can migrate", srTuple.VdiName, srTuple.SrName));
|
|
|
|
|
}
|
|
|
|
|
StartAllVMs();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void TestMultipleSelectionsAllValid()
|
|
|
|
|
{
|
|
|
|
|
List<SelectedItem> selection = CreateVdiSelection(runningVmCanMigrateVdiData, t=>t.ExpectedCanMigrate);
|
|
|
|
|
Assert.IsNotEmpty(selection);
|
|
|
|
|
MigrateVirtualDiskCommand cmd = new MigrateVirtualDiskCommand(mw, selection);
|
|
|
|
|
Assert.IsTrue(cmd.CanExecute(), "All valid selection can migrate");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void TestMultipleSelectionsMixed()
|
|
|
|
|
{
|
|
|
|
|
List<SelectedItem> selection = CreateVdiSelection(runningVmCanMigrateVdiData, t=>true);
|
|
|
|
|
Assert.IsNotEmpty(selection);
|
|
|
|
|
MigrateVirtualDiskCommand cmd = new MigrateVirtualDiskCommand(mw, selection);
|
|
|
|
|
Assert.IsFalse(cmd.CanExecute(), "Mixture in/valid selection can migrate");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[Test]
|
|
|
|
|
public void CommandCannotExecuteAndReasonMessage()
|
|
|
|
|
{
|
|
|
|
|
//Note: Order of logic implicitly checked here
|
|
|
|
|
SR sr; VDI vdi;
|
|
|
|
|
ResolveSrAndVdiNames("iSCSIa", "vdi1", out sr, out vdi);
|
|
|
|
|
ExecuteCommandCheckingReason("Unknown", vdi);
|
|
|
|
|
vdi.type = vdi_type.metadata;
|
|
|
|
|
ExecuteCommandCheckingReason(Messages.CANNOT_MOVE_DR_VD, vdi);
|
|
|
|
|
vdi.type = vdi_type.ha_statefile;
|
|
|
|
|
ExecuteCommandCheckingReason(Messages.CANNOT_MOVE_HA_VD, vdi);
|
|
|
|
|
vdi.Locked = true;
|
|
|
|
|
ExecuteCommandCheckingReason(Messages.CANNOT_MOVE_VDI_IN_USE, vdi);
|
|
|
|
|
vdi.is_a_snapshot = true;
|
|
|
|
|
ExecuteCommandCheckingReason(Messages.CANNOT_MOVE_VDI_IS_SNAPSHOT, vdi);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#region Private Helper Methods
|
|
|
|
|
private void StartAllVMs()
|
|
|
|
|
{
|
|
|
|
|
DatabaseManager.ConnectionFor(dbName).Cache.VMs.ToList().ForEach(vm => VM.start(DatabaseManager.ConnectionFor(dbName).Session, vm.opaque_ref, false, false));
|
|
|
|
|
DatabaseManager.ConnectionFor(dbName).LoadCache(DatabaseManager.ConnectionFor(dbName).Session);
|
|
|
|
|
Assert.IsTrue(DatabaseManager.ConnectionFor(dbName).Cache.VMs.All(vm => vm.IsRunning), "VMs are in a running state");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ShutdownAllVMs()
|
|
|
|
|
{
|
|
|
|
|
DatabaseManager.ConnectionFor(dbName).Cache.VMs.ToList().ForEach(vm => VM.hard_shutdown(DatabaseManager.ConnectionFor(dbName).Session, vm.opaque_ref));
|
|
|
|
|
DatabaseManager.ConnectionFor(dbName).LoadCache(DatabaseManager.ConnectionFor(dbName).Session);
|
|
|
|
|
Assert.IsFalse(DatabaseManager.ConnectionFor(dbName).Cache.VMs.ToList().All(vm => vm.IsRunning), "VMs are in a shutdown state");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ExecuteCommandCheckingReason(string expectedReason, VDI vdi)
|
|
|
|
|
{
|
|
|
|
|
MigrateVirtualDiskCommand cmd = new MigrateVirtualDiskCommand(mw, new List<SelectedItem> { new SelectedItem(vdi) });
|
|
|
|
|
Dictionary<SelectedItem, string> reasons = cmd.GetCantExecuteReasons();
|
|
|
|
|
Assert.IsNotEmpty(reasons, "Reasons found for " + vdi.name_label);
|
|
|
|
|
Assert.IsFalse(cmd.CanExecute(), "Command can execute for " + vdi.name_label);
|
|
|
|
|
Assert.AreEqual(expectedReason, reasons.First().Value, "Reason as expected for " + vdi.name_label);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ResolveSrAndVdiNames(string srName, string vdiName, out SR sr, out VDI vdi)
|
|
|
|
|
{
|
|
|
|
|
sr = DatabaseManager.ConnectionFor(dbName).Cache.SRs.First(s => s.name_label == srName);
|
|
|
|
|
Assert.NotNull(sr, "Couldn't resolve SR: " + srName);
|
|
|
|
|
List<VDI> vdis = DatabaseManager.ConnectionFor(dbName).ResolveAll(sr.VDIs);
|
|
|
|
|
vdi = vdis.First(v => v.name_label == vdiName);
|
|
|
|
|
Assert.NotNull(vdi, "Couldn't resolve VDI: " + vdiName);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private List<SelectedItem> CreateVdiSelection(List<SrTuple> sourceList, Predicate<SrTuple> condition)
|
|
|
|
|
{
|
|
|
|
|
List<SelectedItem> selection = new List<SelectedItem>();
|
|
|
|
|
foreach (SrTuple srTuple in sourceList)
|
|
|
|
|
{
|
|
|
|
|
if(condition(srTuple))
|
|
|
|
|
{
|
|
|
|
|
SR sr; VDI vdi;
|
|
|
|
|
ResolveSrAndVdiNames(srTuple.SrName, srTuple.VdiName, out sr, out vdi);
|
|
|
|
|
selection.Add(new SelectedItem(vdi));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return selection;
|
|
|
|
|
}
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
#region Test Data
|
|
|
|
|
private struct SrTuple
|
|
|
|
|
{
|
|
|
|
|
public SrTuple(string SrName, string VdiName, bool ExpectedCanMigrate)
|
|
|
|
|
{
|
|
|
|
|
this.SrName = SrName;
|
|
|
|
|
this.VdiName = VdiName;
|
|
|
|
|
this.ExpectedCanMigrate = ExpectedCanMigrate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public readonly string SrName;
|
|
|
|
|
public readonly string VdiName;
|
|
|
|
|
public readonly bool ExpectedCanMigrate;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private readonly List<SrTuple> runningVmCanMigrateVdiData = new List<SrTuple>
|
|
|
|
|
{
|
|
|
|
|
new SrTuple("iSCSIa", "shared 1", true),
|
|
|
|
|
new SrTuple("iSCSIa", "shared 0", true),
|
|
|
|
|
new SrTuple("iSCSIa", "vdi1", false), //Not connected to a VM
|
|
|
|
|
new SrTuple("Local storage", "local 0", true),
|
|
|
|
|
new SrTuple("Local storage", "local 1", true)
|
|
|
|
|
};
|
|
|
|
|
#endregion
|
|
|
|
|
}
|
|
|
|
|
}
|