Merge pull request #722 from MihaelaStoica/CP-14641

CP-14641: Add warnings on pool join if the pool and the joining host have different CPU features
This commit is contained in:
Stephen Turner 2015-11-04 17:02:12 +00:00
commit 9380134c9e
15 changed files with 487 additions and 12 deletions

View File

@ -126,7 +126,7 @@ namespace XenAdmin.Commands
return;
}
// Get permission for any fix-ups: 1) Licensing free hosts; 2) CPU masking 3) Ad configuration
// Get permission for any fix-ups: 1) Licensing free hosts; 2) CPU masking 3) Ad configuration 4) CPU feature levelling (Dundee or higher only)
// (We already know that these things are fixable because we have been through CanJoinPool() above).
if (!HelpersGUI.GetPermissionFor(_hosts, host => PoolJoinRules.FreeHostPaidMaster(host, master, false),
Messages.ADD_HOST_TO_POOL_LICENSE_MESSAGE, Messages.ADD_HOST_TO_POOL_LICENSE_MESSAGE_MULTIPLE, true, "PoolJoinRelicensing")
@ -136,8 +136,8 @@ namespace XenAdmin.Commands
||
!HelpersGUI.GetPermissionFor(_hosts, host => !PoolJoinRules.CompatibleAdConfig(host, master, false),
Messages.ADD_HOST_TO_POOL_AD_MESSAGE, Messages.ADD_HOST_TO_POOL_AD_MESSAGE_MULTIPLE, true, "PoolJoinAdConfiguring")
)
||
!HelpersGUI.GetPermissionForCpuFeatureLevelling(_hosts, _pool))
{
return;
}

View File

@ -81,6 +81,12 @@ namespace XenAdmin.Commands
return Messages.MIGRATION_NOT_ALLOWED_NO_SHARED_STORAGE;
}
}
if (targetHost != draggedVMHome && VMOperationHostCommand.VmCpuFeaturesIncompatibleWithHost(targetHost, draggedVM))
{
// target host does not offer some of the CPU features that the VM currently sees
return Messages.MIGRATION_NOT_ALLOWED_CPU_FEATURES;
}
}
}
}
@ -125,6 +131,12 @@ namespace XenAdmin.Commands
if (draggedVM.allowed_operations == null || !draggedVM.allowed_operations.Contains(vm_operations.migrate_send))
return false;
if (VMOperationHostCommand.VmCpuFeaturesIncompatibleWithHost(targetHost, draggedVM))
{
// target host does not offer some of the CPU features that the VM currently sees
return false;
}
}
return true;

View File

@ -95,6 +95,12 @@ namespace XenAdmin.Commands
return Messages.MIGRATION_NOT_ALLOWED_NO_SHARED_STORAGE;
}
}
if (targetHost != draggedVMHome && VMOperationHostCommand.VmCpuFeaturesIncompatibleWithHost(targetHost, draggedVM))
{
// target host does not offer some of the CPU features that the VM currently sees
return Messages.MIGRATION_NOT_ALLOWED_CPU_FEATURES;
}
}
}
}
@ -174,6 +180,12 @@ namespace XenAdmin.Commands
// dragged VM must currently be shown below a host
return false;
}
if (VMOperationHostCommand.VmCpuFeaturesIncompatibleWithHost(targetHost, draggedVM))
{
// target host does not offer some of the CPU features that the VM currently sees
return false;
}
}
return true;

View File

@ -66,7 +66,7 @@ namespace XenAdmin.Commands
{
VM vm = (VM)item.XenObject;
string reason = GetVmCannotBootOnHostReason(vm, GetHost(vm), session);
string reason = GetVmCannotBootOnHostReason(vm, GetHost(vm), session, operation);
_cantBootReasons[vm] = reason;
if (reason == null)
@ -117,7 +117,7 @@ namespace XenAdmin.Commands
return vm != null && _cantBootReasons.ContainsKey(vm) && _cantBootReasons[vm] == null;
}
private static string GetVmCannotBootOnHostReason(VM vm, Host host, Session session)
private static string GetVmCannotBootOnHostReason(VM vm, Host host, Session session, vm_operations operation)
{
Host residentHost = vm.Connection.Resolve(vm.resident_on);
@ -134,6 +134,11 @@ namespace XenAdmin.Commands
if (vm.power_state == vm_power_state.Running && residentHost != null && host.opaque_ref == residentHost.opaque_ref)
return Messages.HOST_MENU_CURRENT_SERVER;
if ((operation == vm_operations.pool_migrate || operation == vm_operations.resume_on) && VmCpuFeaturesIncompatibleWithHost(host, vm))
{
return FriendlyErrorNames.VM_INCOMPATIBLE_WITH_THIS_HOST;
}
try
{
VM.assert_can_boot_here(session, vm.opaque_ref, host.opaque_ref);
@ -177,5 +182,31 @@ namespace XenAdmin.Commands
return base.GetCantExecuteReasonCore(item);
}
public static bool VmCpuFeaturesIncompatibleWithHost(Host targetHost, VM vm)
{
// check the CPU feature compatibility for Dundee and higher hosts
if (!Helpers.DundeeOrGreater(targetHost))
return false;
// only for running or suspended VMs
if (vm.power_state != vm_power_state.Running && vm.power_state != vm_power_state.Suspended)
return false;
if (vm.last_boot_CPU_flags == null || !vm.last_boot_CPU_flags.ContainsKey("vendor") || !vm.last_boot_CPU_flags.ContainsKey("features")
|| targetHost.cpu_info == null || !targetHost.cpu_info.ContainsKey("vendor"))
return false;
if (vm.last_boot_CPU_flags["vendor"] != targetHost.cpu_info["vendor"])
return true;
if (vm.IsHVM && targetHost.cpu_info.ContainsKey("features_hvm"))
return PoolJoinRules.FewerFeatures(targetHost.cpu_info["features_hvm"], vm.last_boot_CPU_flags["features"]);
if (!vm.IsHVM && targetHost.cpu_info.ContainsKey("features_pv"))
return PoolJoinRules.FewerFeatures(targetHost.cpu_info["features_pv"], vm.last_boot_CPU_flags["features"]);
return false;
}
}
}

View File

@ -42,6 +42,7 @@ using XenAdmin.Dialogs;
using XenAdmin.Network;
using XenAdmin.XenSearch;
using XenAPI;
using System.Linq;
namespace XenAdmin.Core
{
@ -95,8 +96,9 @@ namespace XenAdmin.Core
/// <param name="msg_multiple">Dialog message when more than one item needs action. {0} will be substituted for a list of the items.</param>
/// <param name="defaultYes">Whether the default button should be Proceed (as opposed to Cancel)</param>
/// <param name="helpName">Help ID for the dialog</param>
/// <param name="icon">Severity icon for the dialog</param>
/// <returns>Whether permission was obtained (also true if no items needed action)</returns>
public static bool GetPermissionFor<T>(List<T> items, Predicate<T> pred, string msg_single, string msg_multiple, bool defaultYes, string helpName)
public static bool GetPermissionFor<T>(List<T> items, Predicate<T> pred, string msg_single, string msg_multiple, bool defaultYes, string helpName, Icon icon = null)
{
if (Program.RunInAutomatedTestMode)
return true;
@ -111,7 +113,7 @@ namespace XenAdmin.Core
msg = string.Format(msg_multiple, string.Join("\n", itemsToFixup.ConvertAll(item => item.ToString()).ToArray()));
ThreeButtonDialog dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Exclamation, msg),
new ThreeButtonDialog.Details(icon ?? SystemIcons.Exclamation, msg),
helpName,
new ThreeButtonDialog.TBDButton(Messages.PROCEED, DialogResult.Yes),
new ThreeButtonDialog.TBDButton(Messages.CANCEL, DialogResult.No));
@ -570,5 +572,27 @@ namespace XenAdmin.Core
Messages.DATEFORMAT_DMY_HMS, true)
: search.Name;
}
public static bool GetPermissionForCpuFeatureLevelling(List<Host> hosts, Pool pool)
{
if (hosts == null || pool == null || !Helpers.DundeeOrGreater(pool.Connection))
return true;
List<Host> hostsWithFewerFeatures = hosts.Where(host => PoolJoinRules.HostHasFewerFeatures(host, pool)).ToList();
List<Host> hostsWithMoreFeatures = hosts.Where(host => PoolJoinRules.HostHasMoreFeatures(host, pool)).ToList();
if (hostsWithFewerFeatures.Count > 0 && hostsWithMoreFeatures.Count > 0)
{
return GetPermissionFor(hostsWithFewerFeatures.Union(hostsWithMoreFeatures).ToList(), host => true,
Messages.ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_AND_HOST_MESSAGE, Messages.ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_AND_HOST_MESSAGE_MULTIPLE, true, "PoolJoinCpuMasking");
}
if (hostsWithFewerFeatures.Count > 0)
return GetPermissionFor(hostsWithFewerFeatures, host => true,
Messages.ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_MESSAGE, Messages.ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_MESSAGE_MULTIPLE, true, "PoolJoinCpuMasking");
return GetPermissionFor(hostsWithMoreFeatures, host => true,
Messages.ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_HOST_MESSAGE, Messages.ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_HOST_MESSAGE_MULTIPLE, true, "PoolJoinCpuMasking", SystemIcons.Information);
}
}
}

View File

@ -173,7 +173,9 @@ namespace XenAdmin.Dialogs
Messages.NEW_POOL_CPU_MASKING_MESSAGE, Messages.NEW_POOL_CPU_MASKING_MESSAGE_MULTIPLE, true, "PoolJoinCpuMasking")
||
!HelpersGUI.GetPermissionFor(slaves, host => !PoolJoinRules.CompatibleAdConfig(host, master, false),
Messages.NEW_POOL_AD_MESSAGE, Messages.NEW_POOL_AD_MESSAGE_MULTIPLE, true, "PoolJoinAdConfiguring"))
Messages.NEW_POOL_AD_MESSAGE, Messages.NEW_POOL_AD_MESSAGE_MULTIPLE, true, "PoolJoinAdConfiguring")
||
!HelpersGUI.GetPermissionForCpuFeatureLevelling(slaves, Helpers.GetPoolOfOne(master.Connection)))
{
return;
}

View File

@ -0,0 +1,127 @@
/* Copyright (c) Citrix Systems Inc.
* 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.Text;
using NUnit.Framework;
using XenAdmin.Core;
namespace XenAdminTests.UnitTests
{
[TestFixture, Category(TestCategories.Unit)]
public class CPUFeaturesTest
{
const string cpu1 = "000ce3bd-bfebfbff-00000001-20100800";
const string cpu2 = "040ce3bd-bfebfbff-00000001-20100800";
const string cpu3 = "040ce33d-bfebfbff-00000001-20100801";
const string cpu4 = "000ce3bd-bfebfbff-00000001";
[TestFixtureSetUp]
public void SetupResults()
{
// A cpu does not have less features than itself
Expect(cpu1, cpu1, false);
Expect(cpu2, cpu2, false);
Expect(cpu3, cpu3, false);
Expect(cpu4, cpu4, false);
// cpu1 has less features than cpu2 and cpu3
Expect(cpu1, cpu2, true);
Expect(cpu1, cpu3, true);
Expect(cpu1, cpu4, false);
// cpu2 has less features than cpu3 (and some extra ones)
Expect(cpu2, cpu1, false);
Expect(cpu2, cpu3, true);
Expect(cpu2, cpu4, false);
// cpu3 is missing one feature (and has some extra ones)
Expect(cpu3, cpu1, true);
Expect(cpu3, cpu2, true);
Expect(cpu3, cpu4, true);
// cpu4 has less features than all because is missing the last set
Expect(cpu4, cpu1, true);
Expect(cpu4, cpu2, true);
Expect(cpu4, cpu3, true);
}
Dictionary<Config, bool> expectedResults = new Dictionary<Config, bool>();
private class Config
{
string featureSet1, featureSet2;
public Config(string featureSet1, string featureSet2)
{
this.featureSet1 = featureSet1;
this.featureSet2 = featureSet2;
}
public override bool Equals(object obj)
{
Config other = obj as Config;
if (other == null)
return false;
return
this.featureSet1 == other.featureSet1 &&
this.featureSet2 == other.featureSet2;
}
public override int GetHashCode()
{
return featureSet1.GetHashCode() * featureSet2.GetHashCode();
}
}
private void Expect(string featureSet1, string featureSet2, bool expected_result)
{
expectedResults[new Config(featureSet1, featureSet2)] = expected_result;
}
private bool Expected(string featureSet1, string featureSet2)
{
return expectedResults[new Config(featureSet1, featureSet2)];
}
[Test]
public void Run(
[Values(cpu1, cpu2, cpu3, cpu4)] string featureSet1,
[Values(cpu1, cpu2, cpu3, cpu4)] string featureSet2
)
{
System.Console.WriteLine("Asserting {0} < {1}", featureSet1, featureSet2);
Assert.AreEqual(
Expected(featureSet1, featureSet2),
PoolJoinRules.FewerFeatures(featureSet1, featureSet2), string.Format("Assertion failed on {0} < {1}", featureSet1, featureSet2));
}
}
}

View File

@ -67,6 +67,7 @@
<ItemGroup>
<Compile Include="HealthCheckTests\CredentialTests.cs" />
<Compile Include="HealthCheckTests\RequestUploadTaskTests.cs" />
<Compile Include="UnitTests\CPUFeaturesTest.cs" />
<Compile Include="UnitTests\SubnetworkMaskValidatorTest.cs" />
<Compile Include="UnitTests\ExceptionSerializationTest.cs" />
<Compile Include="XenModelTests\ActionTests\ActionTest.cs" />

View File

@ -3802,6 +3802,90 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to You are attempting to add the server &apos;{0}&apos; to a pool that is using older CPUs.
///
///VMs running in the pool will only use the CPU features common to all the servers in the pool.
///
///Do you want to do this?.
/// </summary>
public static string ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_HOST_MESSAGE {
get {
return ResourceManager.GetString("ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_HOST_MESSAGE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You are attempting to add the following servers to a pool that is using older CPUs.
///
///{0}
///
///VMs running in the pool will only use the CPU features common to all the servers in the pool.
///
///Do you want to do this?.
/// </summary>
public static string ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_HOST_MESSAGE_MULTIPLE {
get {
return ResourceManager.GetString("ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_HOST_MESSAGE_MULTIPLE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You are attempting to add the server &apos;{0}&apos; to a pool that is using different CPUs.
///
///VMs starting on the pool in future will only use the reduced set of CPU features common to all the servers in the pool. VMs already running in the pool will not be able to migrate to the new server until they are restarted.
///
///Do you want to do this?.
/// </summary>
public static string ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_AND_HOST_MESSAGE {
get {
return ResourceManager.GetString("ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_AND_HOST_MESSAGE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You are attempting to add the following servers to a pool that is using different CPUs.
///
///{0}
///
///VMs starting on the pool in future will only use the reduced set of CPU features common to all the servers in the pool. VMs already running in the pool will not be able to migrate to the new servers until they are restarted.
///
///Do you want to do this?.
/// </summary>
public static string ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_AND_HOST_MESSAGE_MULTIPLE {
get {
return ResourceManager.GetString("ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_AND_HOST_MESSAGE_MULTIPLE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You are attempting to add the server &apos;{0}&apos; to a pool that is using newer CPUs.
///
///VMs starting on the pool in future will only use the reduced set of CPU features common to all the servers in the pool. VMs already running in the pool will not be able to migrate to the new server until they are restarted.
///
///Do you want to do this?.
/// </summary>
public static string ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_MESSAGE {
get {
return ResourceManager.GetString("ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_MESSAGE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You are attempting to add the following servers to a pool that is using newer CPUs.
///
///{0}
///
///VMs starting on the pool in future will only use the reduced set of CPU features common to all the servers in the pool. VMs already running in the pool will not be able to migrate to the new servers until they are restarted.
///
///Do you want to do this?.
/// </summary>
public static string ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_MESSAGE_MULTIPLE {
get {
return ResourceManager.GetString("ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_MESSAGE_MULTIPLE", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to You are attempting to add the server &apos;{0}&apos; to a pool that is using older CPUs.
///
@ -21625,6 +21709,15 @@ namespace XenAdmin {
}
}
/// <summary>
/// Looks up a localized string similar to This VM may not migrate because the destination host does not have some of the CPU features that the VM is currently using.
/// </summary>
public static string MIGRATION_NOT_ALLOWED_CPU_FEATURES {
get {
return ResourceManager.GetString("MIGRATION_NOT_ALLOWED_CPU_FEATURES", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to This VM may not migrate because it has a dedicated GPU.
/// </summary>

View File

@ -1255,6 +1255,54 @@ Do you want to enable AD authentication on your server and join it to the same d
{0}
All pool members must use the same authentication method. Do you want to enable AD authentication on these servers and join them to the same domain as the pool?</value>
</data>
<data name="ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_HOST_MESSAGE" xml:space="preserve">
<value>You are attempting to add the server '{0}' to a pool that is using older CPUs.
VMs running in the pool will only use the CPU features common to all the servers in the pool.
Do you want to do this?</value>
</data>
<data name="ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_HOST_MESSAGE_MULTIPLE" xml:space="preserve">
<value>You are attempting to add the following servers to a pool that is using older CPUs.
{0}
VMs running in the pool will only use the CPU features common to all the servers in the pool.
Do you want to do this?</value>
</data>
<data name="ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_AND_HOST_MESSAGE" xml:space="preserve">
<value>You are attempting to add the server '{0}' to a pool that is using different CPUs.
VMs starting on the pool in future will only use the reduced set of CPU features common to all the servers in the pool. VMs already running in the pool will not be able to migrate to the new server until they are restarted.
Do you want to do this?</value>
</data>
<data name="ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_AND_HOST_MESSAGE_MULTIPLE" xml:space="preserve">
<value>You are attempting to add the following servers to a pool that is using different CPUs.
{0}
VMs starting on the pool in future will only use the reduced set of CPU features common to all the servers in the pool. VMs already running in the pool will not be able to migrate to the new servers until they are restarted.
Do you want to do this?</value>
</data>
<data name="ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_MESSAGE" xml:space="preserve">
<value>You are attempting to add the server '{0}' to a pool that is using newer CPUs.
VMs starting on the pool in future will only use the reduced set of CPU features common to all the servers in the pool. VMs already running in the pool will not be able to migrate to the new server until they are restarted.
Do you want to do this?</value>
</data>
<data name="ADD_HOST_TO_POOL_CPU_DOWN_LEVEL_POOL_MESSAGE_MULTIPLE" xml:space="preserve">
<value>You are attempting to add the following servers to a pool that is using newer CPUs.
{0}
VMs starting on the pool in future will only use the reduced set of CPU features common to all the servers in the pool. VMs already running in the pool will not be able to migrate to the new servers until they are restarted.
Do you want to do this?</value>
</data>
<data name="ADD_HOST_TO_POOL_CPU_MASKING_MESSAGE" xml:space="preserve">
<value>You are attempting to add the server '{0}' to a pool that is using older CPUs.
@ -7520,6 +7568,9 @@ To learn more about the XenServer Live VDI Migration feature or to start a XenSe
<data name="MIGRATION_NOT_ALLOWED" xml:space="preserve">
<value>This VM may not migrate at the moment</value>
</data>
<data name="MIGRATION_NOT_ALLOWED_CPU_FEATURES" xml:space="preserve">
<value>This VM may not migrate because the destination host does not have some of the CPU features that the VM is currently using</value>
</data>
<data name="MIGRATION_NOT_ALLOWED_GPU" xml:space="preserve">
<value>This VM may not migrate because it has a dedicated GPU</value>
</data>

View File

@ -533,5 +533,85 @@ namespace XenAdmin.Core
return badPacks;
}
/// <summary>
/// Check whether the host that joins the pool has a more extensive feature set than the pool (as long as the CPU vendor is common)
/// In this case the host will transparently be down-levelled to the pool level (without needing reboots)
/// </summary>
public static bool HostHasMoreFeatures(Host slave, Pool pool)
{
if (slave == null || pool == null)
return false;
Dictionary<string, string> slave_cpu_info = slave.cpu_info;
Dictionary<string, string> pool_cpu_info = pool.cpu_info;
if (!Helper.AreEqual2(slave_cpu_info, null) && !Helper.AreEqual2(pool_cpu_info, null))
{
// if pool has less features than slave, then slave will be down-levelled
return FewerFeatures(pool_cpu_info, slave_cpu_info);
}
return false;
}
/// <summary>
/// Check whether the host that joins the pool has a less extensive feature set than the pool (as long as the CPU vendor is common)
/// In this case the pool is transparently down-levelled to the new host's level (without needing reboots)
/// </summary>
public static bool HostHasFewerFeatures(Host slave, Pool pool)
{
if (slave == null || pool == null)
return false;
Dictionary<string, string> slave_cpu_info = slave.cpu_info;
Dictionary<string, string> pool_cpu_info = pool.cpu_info;
if (!Helper.AreEqual2(slave_cpu_info, null) && !Helper.AreEqual2(pool_cpu_info, null))
{
// if slave has less features than pool, then pool will be down-levelled
return FewerFeatures(slave_cpu_info, pool_cpu_info);
}
return false;
}
/// <summary>
/// Check whether first CPU has fewer features than the second.
/// It returns true if the first feature set is less than the second one in at least one bit
/// </summary>
public static bool FewerFeatures(Dictionary<string, string> cpu_infoA, Dictionary<string, string> cpu_infoB)
{
if (cpu_infoA.ContainsKey("features_hvm") && cpu_infoB.ContainsKey("features_hvm") &&
FewerFeatures(cpu_infoA["features_hvm"], cpu_infoB["features_hvm"]))
return true;
if (cpu_infoA.ContainsKey("features_pv") && cpu_infoB.ContainsKey("features_pv") &&
FewerFeatures(cpu_infoA["features_pv"], cpu_infoB["features_pv"]))
return true;
return false;
}
public static bool FewerFeatures(string featureSetA, string featureSetB)
{
if (string.IsNullOrEmpty(featureSetA) || string.IsNullOrEmpty(featureSetB))
return false;
string stringA = featureSetA.Replace(" ", "");
stringA = stringA.Replace("-", "");
string stringB = featureSetB.Replace(" ", "");
stringB = stringB.Replace("-", "");
if (stringA.Length < stringB.Length)
stringA = stringA.PadRight(stringB.Length, '0');
if (stringB.Length < stringA.Length)
stringB = stringB.PadRight(stringA.Length, '0');
for (int i = 0; i < stringA.Length / 8; ++i)
{
uint intA = Convert.ToUInt32(stringA.Substring(8 * i, 8), 16);
uint intB = Convert.ToUInt32(stringB.Substring(8 * i, 8), 16);
if ((intA & intB) != intB)
return true;
}
return false;
}
}
}

View File

@ -4917,7 +4917,7 @@ namespace XenAPI {
}
/// <summary>
/// Looks up a localized string similar to The VM is incompatible with the CPU features of this host..
/// Looks up a localized string similar to The host does not have some of the CPU features that the VM is currently using.
/// </summary>
public static string VM_INCOMPATIBLE_WITH_THIS_HOST {
get {

View File

@ -1749,7 +1749,7 @@ Authorized Roles: {1}</value>
<value>HVM not supported</value>
</data>
<data name="VM_INCOMPATIBLE_WITH_THIS_HOST" xml:space="preserve">
<value>The VM is incompatible with the CPU features of this host.</value>
<value>The host does not have some of the CPU features that the VM is currently using</value>
</data>
<data name="VM_IS_PART_OF_AN_APPLIANCE" xml:space="preserve">
<value>The VM cannot be recovered on its own as it is part of a VM appliance.</value>

View File

@ -78,7 +78,8 @@ namespace XenAPI
List<XenRef<VDI>> metadata_VDIs,
string ha_cluster_stack,
List<pool_allowed_operations> allowed_operations,
Dictionary<string, pool_allowed_operations> current_operations)
Dictionary<string, pool_allowed_operations> current_operations,
Dictionary<string, string> cpu_info)
{
this.uuid = uuid;
this.name_label = name_label;
@ -111,6 +112,7 @@ namespace XenAPI
this.ha_cluster_stack = ha_cluster_stack;
this.allowed_operations = allowed_operations;
this.current_operations = current_operations;
this.cpu_info = cpu_info;
}
/// <summary>
@ -155,6 +157,7 @@ namespace XenAPI
ha_cluster_stack = update.ha_cluster_stack;
allowed_operations = update.allowed_operations;
current_operations = update.current_operations;
cpu_info = update.cpu_info;
}
internal void UpdateFromProxy(Proxy_Pool proxy)
@ -190,6 +193,7 @@ namespace XenAPI
ha_cluster_stack = proxy.ha_cluster_stack == null ? null : (string)proxy.ha_cluster_stack;
allowed_operations = proxy.allowed_operations == null ? null : Helper.StringArrayToEnumList<pool_allowed_operations>(proxy.allowed_operations);
current_operations = proxy.current_operations == null ? null : Maps.convert_from_proxy_string_pool_allowed_operations(proxy.current_operations);
cpu_info = proxy.cpu_info == null ? null : Maps.convert_from_proxy_string_string(proxy.cpu_info);
}
public Proxy_Pool ToProxy()
@ -226,6 +230,7 @@ namespace XenAPI
result_.ha_cluster_stack = (ha_cluster_stack != null) ? ha_cluster_stack : "";
result_.allowed_operations = (allowed_operations != null) ? Helper.ObjectListToStringArray(allowed_operations) : new string[] {};
result_.current_operations = Maps.convert_to_proxy_string_pool_allowed_operations(current_operations);
result_.cpu_info = Maps.convert_to_proxy_string_string(cpu_info);
return result_;
}
@ -266,6 +271,7 @@ namespace XenAPI
ha_cluster_stack = Marshalling.ParseString(table, "ha_cluster_stack");
allowed_operations = Helper.StringArrayToEnumList<pool_allowed_operations>(Marshalling.ParseStringArray(table, "allowed_operations"));
current_operations = Maps.convert_from_proxy_string_pool_allowed_operations(Marshalling.ParseHashTable(table, "current_operations"));
cpu_info = Maps.convert_from_proxy_string_string(Marshalling.ParseHashTable(table, "cpu_info"));
}
public bool DeepEquals(Pool other, bool ignoreCurrentOperations)
@ -307,7 +313,8 @@ namespace XenAPI
Helper.AreEqual2(this._restrictions, other._restrictions) &&
Helper.AreEqual2(this._metadata_VDIs, other._metadata_VDIs) &&
Helper.AreEqual2(this._ha_cluster_stack, other._ha_cluster_stack) &&
Helper.AreEqual2(this._allowed_operations, other._allowed_operations);
Helper.AreEqual2(this._allowed_operations, other._allowed_operations) &&
Helper.AreEqual2(this._cpu_info, other._cpu_info);
}
public override string SaveChanges(Session session, string opaqueRef, Pool server)
@ -734,6 +741,17 @@ namespace XenAPI
return Maps.convert_from_proxy_string_pool_allowed_operations(session.proxy.pool_get_current_operations(session.uuid, (_pool != null) ? _pool : "").parse());
}
/// <summary>
/// Get the cpu_info field of the given pool.
/// First published in XenServer Dundee.
/// </summary>
/// <param name="session">The session</param>
/// <param name="_pool">The opaque_ref of the given pool</param>
public static Dictionary<string, string> get_cpu_info(Session session, string _pool)
{
return Maps.convert_from_proxy_string_string(session.proxy.pool_get_cpu_info(session.uuid, (_pool != null) ? _pool : "").parse());
}
/// <summary>
/// Set the name_label field of the given pool.
/// First published in XenServer 4.0.
@ -2524,5 +2542,24 @@ namespace XenAPI
}
}
private Dictionary<string, pool_allowed_operations> _current_operations;
/// <summary>
/// Details about the physical CPUs on the pool
/// First published in XenServer Dundee.
/// </summary>
public virtual Dictionary<string, string> cpu_info
{
get { return _cpu_info; }
set
{
if (!Helper.AreEqual(value, _cpu_info))
{
_cpu_info = value;
Changed = true;
NotifyPropertyChanged("cpu_info");
}
}
}
private Dictionary<string, string> _cpu_info;
}
}

View File

@ -576,6 +576,10 @@ namespace XenAPI
Response<Object>
pool_get_current_operations(string session, string _pool);
[XmlRpcMethod("pool.get_cpu_info")]
Response<Object>
pool_get_cpu_info(string session, string _pool);
[XmlRpcMethod("pool.set_name_label")]
Response<string>
pool_set_name_label(string session, string _pool, string _name_label);
@ -6836,6 +6840,7 @@ namespace XenAPI
public string ha_cluster_stack;
public string [] allowed_operations;
public Object current_operations;
public Object cpu_info;
}
[XmlRpcMissingMapping(MappingAction.Ignore)]