mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2025-01-20 07:19:18 +01:00
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:
parent
0df39a9d83
commit
4abe4ddb30
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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() { }
|
||||
}
|
||||
|
@ -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")
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user