CA-309758: Optimisations to the way we assert if Vms can be migrated to a destination pool in the Cross Pool Migrate wizard

- When asserting if a VM can be migrated to a pool we don't have to check all the hosts in the pool at this point, we can enable the pool when we find the first host where migration is possible; we will check the rest of the hosts only if that pool is selected.
- If the destination pool is older than the source, then we don't need to do any server calls because we know that migration to an older host is not allowed.

Signed-off-by: Mihaela Stoica <mihaela.stoica@citrix.com>
This commit is contained in:
Mihaela Stoica 2019-02-28 15:09:33 +00:00 committed by Konstantina Chremmou
parent 0df39a9d83
commit 4abe4ddb30
7 changed files with 199 additions and 166 deletions

View File

@ -32,7 +32,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using XenAdmin.Commands;
using XenAdmin.Core;
using XenAdmin.Wizards.GenericPages;
@ -69,135 +68,150 @@ namespace XenAdmin.Wizards.CrossPoolMigrateWizard.Filters
canceled = true;
}
public override bool FailureFound
public override bool FailureFoundFor(IXenObject itemToFilterOn)
{
get
Pool targetPool;
List<Host> targets = CollateHosts(itemToFilterOn, out targetPool);
foreach (VM vm in preSelectedVMs)
{
log.InfoFormat("Asserting can migrate to {0}...", ItemToFilterOn);
Pool targetPool;
List<Host> targets = CollateHosts(out targetPool);
var excludedHosts = new List<string>();
log.InfoFormat("Asserting can migrate VM {0} to {1}...", vm.Name(), itemToFilterOn);
bool vmIsMigratable = false;
foreach (Host host in targets)
{
var targetSrs = host.Connection.Cache.SRs.Where(sr => sr.SupportsStorageMigration()).ToList();
var targetNetwork = GetANetwork(host);
if (canceled)
return false;
foreach (VM vm in preSelectedVMs)
// obtain the cache data for a vm
IDictionary<string, string> vmCache;
lock (cacheLock)
{
if (canceled)
return excludedHosts.Count == targets.Count;
if (!cache.ContainsKey(vm.opaque_ref))
{
cache.Add(vm.opaque_ref, new Dictionary<string, string>());
}
vmCache = cache[vm.opaque_ref];
}
// obtain the cache data for a vm
IDictionary<string, string> vmCache;
try
{
//CA-220218: for intra-pool motion of halted VMs we do a move, so no need to assert we can migrate
Pool vmPool = Helpers.GetPoolOfOne(vm.Connection);
if (_wizardMode == WizardMode.Move && vmPool != null && targetPool != null && vmPool.opaque_ref == targetPool.opaque_ref)
{
// vm is migratable, no need to itearate through all the pool members
vmIsMigratable = true;
break;
}
//Skip the resident host as there's a filter for it and
//if not then you could exclude intrapool migration
//CA-205799: do not offer the host the VM is currently on
Host homeHost = vm.Home();
if (homeHost != null && homeHost.opaque_ref == host.opaque_ref)
continue;
if (vmCache.ContainsKey(host.opaque_ref))
{
disableReason = vmCache[host.opaque_ref];
if (string.IsNullOrEmpty(disableReason))
{
// vm is migratable to at least one host in the pool, no need to itearate through all the pool members
vmIsMigratable = true;
break;
}
continue;
}
//if pool_migrate can be done, then we will allow it in the wizard, even if storage migration is not allowed (i.e. users can use the wizard to live-migrate a VM inside the pool)
if (_wizardMode == WizardMode.Migrate && vmPool != null && targetPool != null && vmPool.opaque_ref == targetPool.opaque_ref)
{
var reason = VMOperationHostCommand.GetVmCannotBootOnHostReason(vm, host, vm.Connection.Session, vm_operations.pool_migrate);
if (string.IsNullOrEmpty(reason))
{
lock (cacheLock)
{
vmCache.Add(host.opaque_ref, reason);
}
// vm is migratable to at least one host in the pool, no need to itearate through all the pool members
vmIsMigratable = true;
break;
}
}
//check if the destination host is older than the source host
var destinationVersion = Helpers.HostPlatformVersion(host);
var sourceVersion = Helpers.HostPlatformVersion(vm.Home() ?? Helpers.GetMaster(vmPool));
if (Helpers.productVersionCompare(destinationVersion, sourceVersion) < 0)
{
throw new Failure(Messages.OLDER_THAN_CURRENT_SERVER);
}
PIF managementPif = host.Connection.Cache.PIFs.First(p => p.management);
XenAPI.Network managementNetwork = host.Connection.Cache.Resolve(managementPif.network);
Session session = host.Connection.DuplicateSession();
Dictionary<string, string> receiveMapping = Host.migrate_receive(session, host.opaque_ref, managementNetwork.opaque_ref, new Dictionary<string, string>());
var targetSrs = host.Connection.Cache.SRs.Where(sr => sr.SupportsStorageMigration()).ToList();
var targetNetwork = GetANetwork(host);
VM.assert_can_migrate(vm.Connection.Session,
vm.opaque_ref,
receiveMapping,
true,
GetVdiMap(vm, targetSrs),
vm.Connection == host.Connection ? new Dictionary<XenRef<VIF>, XenRef<XenAPI.Network>>() : GetVifMap(vm, targetNetwork),
new Dictionary<string, string>());
lock (cacheLock)
{
if (!cache.ContainsKey(vm.opaque_ref))
{
cache.Add(vm.opaque_ref, new Dictionary<string, string>());
}
vmCache = cache[vm.opaque_ref];
vmCache.Add(host.opaque_ref, string.Empty);
}
// vm is migratable to at least one host in the pool, no need to itearate through all the pool members
vmIsMigratable = true;
break;
}
catch (Failure failure)
{
if (failure.ErrorDescription.Count > 0 && failure.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
disableReason = failure.Message.Split('\n')[0].TrimEnd('\r'); // we want the first line only
else
disableReason = failure.Message;
try
lock (cacheLock)
{
//CA-220218: for intra-pool motion of halted VMs we do a move, so no need to assert we can migrate
Pool vmPool = Helpers.GetPoolOfOne(vm.Connection);
if (_wizardMode == WizardMode.Move && vmPool != null && targetPool != null && vmPool.opaque_ref == targetPool.opaque_ref)
continue;
//Skip the resident host as there's a filter for it and
//if not then you could exclude intrapool migration
//CA-205799: do not offer the host the VM is currently on
Host homeHost = vm.Home();
if (homeHost != null && homeHost.opaque_ref == host.opaque_ref)
{
if (!excludedHosts.Contains(host.opaque_ref))
excludedHosts.Add(host.opaque_ref);
continue;
}
if (vmCache.ContainsKey(host.opaque_ref))
{
disableReason = vmCache[host.opaque_ref];
if (!string.IsNullOrEmpty(disableReason) && !excludedHosts.Contains(host.opaque_ref))
{
excludedHosts.Add(host.opaque_ref);
}
continue;
}
//if pool_migrate can be done, then we will allow it in the wizard, even if storage migration is not allowed (i.e. users can use the wizard to live-migrate a VM inside the pool)
if (_wizardMode == WizardMode.Migrate && vmPool != null && targetPool != null && vmPool.opaque_ref == targetPool.opaque_ref)
{
var reason = VMOperationHostCommand.GetVmCannotBootOnHostReason(vm, host, vm.Connection.Session, vm_operations.pool_migrate);
if (string.IsNullOrEmpty(reason))
{
lock (cacheLock)
{
vmCache.Add(host.opaque_ref, reason);
}
continue;
}
}
PIF managementPif = host.Connection.Cache.PIFs.First(p => p.management);
XenAPI.Network network = host.Connection.Cache.Resolve(managementPif.network);
Session session = host.Connection.DuplicateSession();
Dictionary<string, string> receiveMapping = Host.migrate_receive(session, host.opaque_ref, network.opaque_ref, new Dictionary<string, string>());
VM.assert_can_migrate(vm.Connection.Session,
vm.opaque_ref,
receiveMapping,
true,
GetVdiMap(vm, targetSrs),
vm.Connection == host.Connection ? new Dictionary<XenRef<VIF>, XenRef<XenAPI.Network>>() : GetVifMap(vm, targetNetwork),
new Dictionary<string, string>());
lock (cacheLock)
{
vmCache.Add(host.opaque_ref, string.Empty);
}
vmCache.Add(host.opaque_ref, disableReason.Clone().ToString());
}
catch (Failure failure)
{
if (failure.ErrorDescription.Count > 0 && failure.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED)
disableReason = failure.Message.Split('\n')[0].TrimEnd('\r'); // we want the first line only
else
disableReason = failure.Message;
lock (cacheLock)
{
vmCache.Add(host.opaque_ref, disableReason.Clone().ToString());
}
log.ErrorFormat("VM: {0}, Host: {1} - Reason: {2};", vm.Name(), host.Name(), failure.Message);
log.ErrorFormat("VM: {0}, Host: {1} - Reason: {2};", vm.opaque_ref, host.opaque_ref, failure.Message);
if (!excludedHosts.Contains(host.opaque_ref))
excludedHosts.Add(host.opaque_ref);
}
vmIsMigratable = false;
}
}
return excludedHosts.Count == targets.Count;
// if at least one VM is not migratable to the target pool, then there is no point checking the remaining VMs
if (!vmIsMigratable)
return true;
}
return false;
}
private List<Host> CollateHosts(out Pool thePool)
private List<Host> CollateHosts(IXenObject itemToFilterOn, out Pool thePool)
{
thePool = null;
List<Host> target = new List<Host>();
if (ItemToFilterOn is Host)
if (itemToFilterOn is Host)
{
target.Add(ItemToFilterOn as Host);
thePool = Helpers.GetPoolOfOne(ItemToFilterOn.Connection);
target.Add(itemToFilterOn as Host);
thePool = Helpers.GetPoolOfOne(itemToFilterOn.Connection);
}
if (ItemToFilterOn is Pool)
if (itemToFilterOn is Pool)
{
Pool pool = ItemToFilterOn as Pool;
Pool pool = itemToFilterOn as Pool;
target.AddRange(pool.Connection.Cache.Hosts);
target.Sort();
thePool = pool;
}
return target;

View File

@ -49,31 +49,29 @@ namespace XenAdmin.Wizards.CrossPoolMigrateWizard.Filters
this.preSelectedVMs = preSelectedVMs;
}
public override bool FailureFound
public override bool FailureFoundFor(IXenObject itemToFilterOn)
{
get
var residentHosts = from VM vm in preSelectedVMs
let home = vm.Home()
where home != null
select home;
if (itemToFilterOn is Host)
return residentHosts.Any(h => h == itemToFilterOn);
Pool tempPool = itemToFilterOn as Pool;
if (tempPool != null)
{
var residentHosts = from VM vm in preSelectedVMs
let home = vm.Home()
where home != null
select home;
if (tempPool.Connection.Cache.Hosts.Length > 1)
return false;
if (ItemToFilterOn is Host)
return residentHosts.Any(h => h == ItemToFilterOn);
Pool tempPool = ItemToFilterOn as Pool;
if (tempPool != null)
{
if (tempPool.Connection.Cache.Hosts.Length > 1)
return false;
//Pool with one host (or less)
Host master = tempPool.Connection.Resolve(tempPool.master);
return residentHosts.Any(h => h == master);
}
return false;
//Pool with one host (or less)
Host master = tempPool.Connection.Resolve(tempPool.master);
return residentHosts.Any(h => h == master);
}
return false;
}
public override string Reason

View File

@ -50,21 +50,18 @@ namespace XenAdmin.Wizards.CrossPoolMigrateWizard.Filters
this.preSelectedVMs = preSelectedVMs;
}
public override bool FailureFound
public override bool FailureFoundFor(IXenObject itemToFilterOn)
{
get
{
bool targetWlb = false;
bool targetWlb = false;
if(ItemToFilterOn != null)
targetWlb = Helpers.CrossPoolMigrationRestrictedWithWlb(ItemToFilterOn.Connection);
if(itemToFilterOn != null)
targetWlb = Helpers.CrossPoolMigrationRestrictedWithWlb(itemToFilterOn.Connection);
bool sourceWlb = preSelectedVMs.Any(vm => Helpers.CrossPoolMigrationRestrictedWithWlb(vm.Connection));
bool sourceWlb = preSelectedVMs.Any(vm => Helpers.CrossPoolMigrationRestrictedWithWlb(vm.Connection));
reason = targetWlb ? Messages.CPM_WLB_ENABLED_ON_HOST_FAILURE_REASON : Messages.CPM_WLB_ENABLED_ON_VM_FAILURE_REASON;
reason = targetWlb ? Messages.CPM_WLB_ENABLED_ON_HOST_FAILURE_REASON : Messages.CPM_WLB_ENABLED_ON_VM_FAILURE_REASON;
return targetWlb || sourceWlb;
}
return targetWlb || sourceWlb;
}
private string reason = Messages.UNKNOWN;

View File

@ -32,6 +32,7 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Windows.Forms;
using XenAdmin.Controls;
using XenAPI;
@ -48,6 +49,7 @@ namespace XenAdmin.Wizards.GenericPages
/// Event raised when the reason is updated
/// </summary>
public event Action<DelayLoadingOptionComboBoxItem> ReasonUpdated;
public Object ParentComboBox;
private string failureReason;
private IXenObject xenObject;
private const int DEFAULT_RETRIES = 10;

View File

@ -41,21 +41,19 @@ namespace XenAdmin.Wizards.GenericPages
if (!(itemToFilterOn is Host) && !(itemToFilterOn is Pool))
throw new ArgumentException("Target should be host or pool");
ItemToFilterOn = itemToFilterOn;
baseItemToFilterOn = itemToFilterOn;
}
/// <summary>
/// Base item that should be used to filter on
/// </summary>
protected IXenObject ItemToFilterOn { get; set; }
public abstract bool FailureFound { get; }
private IXenObject baseItemToFilterOn { get; set; }
//public abstract bool FailureFound { get; }
public abstract string Reason { get; }
public bool FailureFoundFor(IXenObject xenObject)
{
ItemToFilterOn = xenObject;
return FailureFound;
}
public abstract bool FailureFoundFor(IXenObject xenObject);
public bool FailureFound => FailureFoundFor(baseItemToFilterOn);
public virtual void Cancel() { }
}

View File

@ -409,13 +409,13 @@ namespace XenAdmin.Wizards.GenericPages
foreach (var host in sortedHosts)
{
var item = new DelayLoadingOptionComboBoxItem(host, homeserverFilters);
item.LoadSync();
cb.Items.Add(item);
if (item.Enabled && ((m_selectedObject != null && m_selectedObject.opaque_ref == host.opaque_ref) ||
(target.Item.opaque_ref == host.opaque_ref)))
cb.Value = item;
item.ParentComboBox = cb;
item.PreferAsSelectedItem = m_selectedObject != null && m_selectedObject.opaque_ref == host.opaque_ref ||
target.Item.opaque_ref == host.opaque_ref;
item.ReasonUpdated += DelayLoadedGridComboBoxItem_ReasonChanged;
item.LoadAsync();
}
}
SetComboBoxPreSelection(cb);
@ -524,7 +524,34 @@ namespace XenAdmin.Wizards.GenericPages
});
}
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
private void DelayLoadedGridComboBoxItem_ReasonChanged(DelayLoadingOptionComboBoxItem item)
{
if (item == null)
throw new NullReferenceException("Trying to update delay loaded reason but failed to extract reason");
var cb = item.ParentComboBox as DataGridViewEnableableComboBoxCell;
if (cb == null)
return;
Program.Invoke(this, () =>
{
try
{
var selectedValue = cb.Value;
cb.DataGridView.RefreshEdit();
if (item.Enabled && item.PreferAsSelectedItem)
cb.Value = item;
else
cb.Value = selectedValue;
}
finally
{
item.ReasonUpdated -= DelayLoadedGridComboBoxItem_ReasonChanged;
}
});
}
private void PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "name_label" || e.PropertyName == "metrics" ||
e.PropertyName == "enabled" || e.PropertyName == "live" || e.PropertyName == "patches")

View File

@ -47,37 +47,34 @@ namespace XenAdmin.Wizards.ImportWizard.Filters
{
_hardwarePlatformSettings = hardwarePlatformSettings;
if (ItemToFilterOn is Host host)
if (itemAddedToComboBox is Host host)
_hosts.Add(host);
if (ItemToFilterOn is Pool pool)
if (itemAddedToComboBox is Pool pool)
_hosts.AddRange(pool.Connection.Cache.Hosts);
}
public override bool FailureFound
public override bool FailureFoundFor(IXenObject itemToFilterOn)
{
get
foreach (var setting in _hardwarePlatformSettings)
{
foreach (var setting in _hardwarePlatformSettings)
long hardwarePlatformVersion;
if (!long.TryParse(setting.Value.Value, out hardwarePlatformVersion))
continue;
if (_hosts.Count > 0)
{
long hardwarePlatformVersion;
if (!long.TryParse(setting.Value.Value, out hardwarePlatformVersion))
continue;
if (_hosts.Count > 0)
{
if (_hosts.Any(h => !h.virtual_hardware_platform_versions.Contains(hardwarePlatformVersion)))
return true;
}
else
{
if (hardwarePlatformVersion > 0)
return true;
}
if (_hosts.Any(h => !h.virtual_hardware_platform_versions.Contains(hardwarePlatformVersion)))
return true;
}
else
{
if (hardwarePlatformVersion > 0)
return true;
}
return false;
}
return false;
}
public override string Reason => Messages.CPM_FAILURE_REASON_HARDWARE_PLATFORM;