xenadmin/XenModel/XenAPI-Extensions/VM.cs

1854 lines
66 KiB
C#
Raw Normal View History

/* 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.Diagnostics;
using System.Linq;
using System.Timers;
using System.Xml;
using XenAdmin;
using XenAdmin.Core;
using XenAdmin.Network;
using System.Net;
using System.Text.RegularExpressions;
using Newtonsoft.Json;
namespace XenAPI
{
public partial class VM : IComparable<VM>
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
// The following variables are only used when the corresponding variable is missing from
// the recommendations field of the VM (which is inherited from the recommendations field
// of the template it was created from). This should not normally happen, so we just use
// the maximum that any VM can have as a backstop, without worrying about different OS's
// or different XenServer versions.
private const int DEFAULT_NUM_VCPUS_ALLOWED = 16;
private const int DEFAULT_NUM_VIFS_ALLOWED = 7;
private const int DEFAULT_NUM_VBDS_ALLOWED = 255;
public const long DEFAULT_MEM_ALLOWED = 1 * Util.BINARY_TERA;
public const long DEFAULT_MEM_MIN_IMG_IMPORT = 256 * Util.BINARY_MEGA;
public const int DEFAULT_CORES_PER_SOCKET = 1;
public const long MAX_SOCKETS = 16; // current hard limit in Xen: CA-198276
private XmlDocument xdRecommendations = null;
public const int MAX_ALLOWED_VTPMS = 1;
public int MaxVCPUsAllowed()
{
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@field='vcpus-max']");
if (int.TryParse(xn?.Attributes?["max"]?.Value, out var result))
return result;
return DEFAULT_NUM_VCPUS_ALLOWED;
}
public int MinVCPUs()
{
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@field='vcpus-min']");
if (int.TryParse(xn?.Attributes?["min"]?.Value, out var result))
return result;
return 1;
}
public bool IsRunning()
{
return power_state == vm_power_state.Running;
}
/// <summary>
/// Returns true if the VM's pool has HA enabled and the VM has a saved restart priority other than DoNotRestart.
/// Does not take account of ha-always-run.
/// </summary>
/// <param name="session"></param>
/// <param name="vm"></param>
/// <returns></returns>
public bool HasSavedRestartPriority()
{
Pool pool = Helpers.GetPoolOfOne(Connection);
return pool != null && pool.ha_enabled && !String.IsNullOrEmpty(ha_restart_priority);
}
/// <summary>
/// Get the given VM's home, i.e. the host under which we are going to display it. May return null, if this VM should live
/// at the pool level. For a normal VM, we look at (1) where it's running; (2) where its storage forces it to run;
/// (3) what its affinity is (its requested but not guaranteed host).
/// </summary>
public virtual Host Home()
{
if (is_a_snapshot) // Snapshots have the same "home" as their VM. This is necessary to make a pool-server-VM-snapshot tree (CA-76273).
{
VM from = Connection.Resolve(snapshot_of);
return (from == null ? null : from.Home()); // "from" can be null if VM has been deleted
}
if (is_a_template) // Templates (apart from snapshots) don't have a "home", even if their affinity is set CA-36286
return null;
if (power_state == vm_power_state.Running)
return Connection.Resolve(resident_on);
Host storage_host = GetStorageHost(false);
if (storage_host != null)
return storage_host;
Host affinityHost = Connection.Resolve(affinity);
if (affinityHost != null && affinityHost.IsLive())
return affinityHost;
return null;
}
public VBD FindVMCDROM()
{
if (Connection == null)
return null;
List<VBD> vbds = Connection.ResolveAll(VBDs).FindAll(vbd => vbd.IsCDROM());
if (vbds.Count > 0)
{
vbds.Sort();
return vbds[0];
}
else
{
return null;
}
}
public SR FindVMCDROMSR()
{
VBD vbd = FindVMCDROM();
if (vbd != null)
{
VDI vdi = vbd.Connection.Resolve(vbd.VDI);
if (vdi != null)
{
return vdi.Connection.Resolve(vdi.SR);
}
}
return null;
}
public override string Name()
{
const string CONTROL_DOMAIN = "Control domain on host: ";
if (name_label != null && name_label.StartsWith(CONTROL_DOMAIN))
{
var hostName = name_label.Substring(CONTROL_DOMAIN.Length);
return string.Format(Messages.CONTROL_DOM_ON_HOST, hostName);
}
return name_label;
}
public long MaxMemAllowed()
{
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@field='memory-static-max']");
if (long.TryParse(xn?.Attributes?["max"]?.Value, out var result))
return result;
return DEFAULT_MEM_ALLOWED;
}
public int MaxVIFsAllowed()
{
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@property='number-of-vifs']");
if (int.TryParse(xn?.Attributes?["max"]?.Value, out var result))
return result;
return DEFAULT_NUM_VIFS_ALLOWED;
}
public int MaxVBDsAllowed()
{
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@property='number-of-vbds']");
if (int.TryParse(xn?.Attributes?["max"]?.Value, out var result))
return result;
return DEFAULT_NUM_VBDS_ALLOWED;
}
private XmlDocument GetRecommendations()
{
if (xdRecommendations != null)
return xdRecommendations;
if (string.IsNullOrEmpty(this.recommendations))
return null;
xdRecommendations = new XmlDocument();
try
{
xdRecommendations.LoadXml(this.recommendations);
}
catch
{
xdRecommendations = null;
}
return xdRecommendations;
}
public Host GetStorageHost(bool ignoreCDs)
{
foreach (VBD TheVBD in Connection.ResolveAll(VBDs))
{
if (ignoreCDs && TheVBD.type == vbd_type.CD)
continue;
VDI TheVDI = Connection.Resolve(TheVBD.VDI);
if (TheVDI == null || !TheVDI.Show(true))
continue;
SR TheSR = Connection.Resolve(TheVDI.SR);
if (TheSR == null)
continue;
Host host = TheSR.GetStorageHost();
if (host != null)
{
return host;
}
}
return null;
}
/// <remarks>
/// Default on server is CD - disk then optical
/// </remarks>
public string GetBootOrder()
{
if (this.HVM_boot_params.ContainsKey("order"))
return this.HVM_boot_params["order"].ToUpper();
return "CD";
}
public void SetBootOrder(string value)
{
HVM_boot_params = SetDictionaryKey(HVM_boot_params, "order", value.ToLower());
}
public bool IsPvVm()
{
return IsRealVm() && !IsHVM() && !other_config.ContainsKey("pvcheckpass");
}
public bool IsUEFIEnabled()
{
if (!IsHVM())
return false;
var firmware = Get(HVM_boot_params, "firmware");
return !string.IsNullOrEmpty(firmware) && firmware.Trim().ToLower() == "uefi";
}
public bool IsSecureBootEnabled()
{
if (!IsUEFIEnabled())
return false;
var secureboot = Get(platform, "secureboot");
return !string.IsNullOrEmpty(secureboot) && secureboot.Trim().ToLower() == "true";
}
public int GetVcpuWeight()
{
if (VCPUs_params != null && VCPUs_params.ContainsKey("weight"))
{
int weight;
if (int.TryParse(VCPUs_params["weight"], out weight)) // if we cant parse it we assume its because it is too large, obviously if it isnt a number (ie a string) then we will still go to the else
return weight > 0 ? weight : 1; // because we perform a log on what is returned from this the weight must always be greater than 0
else
return 65536; // could not parse number, assume max
}
else
return 256;
}
public void SetVcpuWeight(int value)
{
VCPUs_params = SetDictionaryKey(VCPUs_params, "weight", value.ToString());
}
public bool DefaultTemplate()
{
return Get(other_config, "default_template") != null;
}
public bool InternalTemplate()
{
return Get(other_config, "xensource_internal") != null;
}
public string InstallRepository()
{
return Get(other_config, "install-repository");
}
public string InstallDistro()
{
return Get(other_config, "install-distro");
}
public string InstallMethods()
{
return Get(other_config, "install-methods");
}
public bool IsHVM()
{
return HVM_boot_policy != "";
}
public bool HasRDP()
{
var metrics = Connection.Resolve(this.guest_metrics);
if (metrics == null)
return false;
// feature-ts is the feature flag indicating the toolstack
// can enable RDP remotely (by writing to control/ts)
return 0 != IntKey(metrics.other, "feature-ts", 0);
}
public bool RDPEnabled()
{
var vmMetrics = Connection.Resolve(this.guest_metrics);
if (vmMetrics == null)
return false;
// data/ts indicates the VM has RDP enabled
return 0 != IntKey(vmMetrics.other, "data-ts", 0);
}
public bool RDPControlEnabled()
{
var metrics = Connection.Resolve(this.guest_metrics);
if (metrics == null)
return false;
// feature-ts2 is the feature flag indicating that data/ts is valid
return 0 != IntKey(metrics.other, "feature-ts2", 0);
}
public bool CanUseRDP()
{
var guestMetrics = Connection.Resolve(guest_metrics);
if (guestMetrics == null)
return false;
return IntKey(guestMetrics.other, "feature-ts2", 0) != 0 &&
IntKey(guestMetrics.other, "feature-ts", 0) != 0 &&
IntKey(guestMetrics.other, "data-ts", 0) != 0 &&
// CA-322672: Can't connect using RDP if there are no networks.
// The network object contains the IP info written by the xenvif
// driver (which needs a 1st reboot to swap out the emulated network adapter)
guestMetrics.networks.Count > 0;
}
/// <summary>Returns true if
/// 1) the guest is HVM and
/// 2a) the allow-gpu-passthrough restriction is absent or
/// 2b) the allow-gpu-passthrough restriction is non-zero
///</summary>
public bool CanHaveGpu()
{
if (!IsHVM())
return false;
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@field='allow-gpu-passthrough']");
if (int.TryParse(xn?.Attributes?["value"]?.Value, out var result))
return result != 0;
return true;
}
public bool HasSriovRecommendation()
{
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@field='allow-network-sriov']");
if (int.TryParse(xn?.Attributes?["value"]?.Value, out var result))
return result != 0;
return false;
}
public bool HasVendorDeviceRecommendation()
{
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@field='has-vendor-device']");
if (bool.TryParse(xn?.Attributes?["value"]?.Value, out var result))
return result;
log.Error("Error parsing has-vendor-device on the template.");
return false;
}
/// <summary>Returns true if
/// 1) the guest is HVM and
/// 2a) the allow-vgpu restriction is absent or
/// 2b) the allow-vgpu restriction is non-zero
///</summary>
public bool CanHaveVGpu()
{
if (!IsHVM() || !CanHaveGpu())
return false;
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@field='allow-vgpu']");
if (int.TryParse(xn?.Attributes?["value"]?.Value, out var result))
return result != 0;
return true;
}
#region Supported Boot Mode Recommendations
public bool CanSupportUEFIBoot()
{
return GetRecommendationByField("supports-uefi") == "yes";
}
public bool CanSupportUEFISecureBoot()
{
return GetRecommendationByField("supports-secure-boot") == "yes";
}
private string GetRecommendationByField(string fieldName)
{
XmlDocument xd = GetRecommendations();
XmlNode xn = xd?.SelectSingleNode(@"restrictions/restriction[@field='" + fieldName + "']");
return xn?.Attributes?["value"]?.Value ?? string.Empty;
}
#endregion
2015-10-26 17:01:55 +01:00
// AutoPowerOn is supposed to be unsupported. However, we advise customers how to
// enable it (http://support.citrix.com/article/CTX133910), so XenCenter has to be
// able to recognise it, and turn it off during Rolling Pool Upgrade.
public bool GetAutoPowerOn()
{
return BoolKey(other_config, "auto_poweron");
}
public void SetAutoPowerOn(bool value)
{
other_config = SetDictionaryKey(other_config, "auto_poweron", value.ToString().ToLower());
}
public bool GetIgnoreExcessiveVcpus()
{
return BoolKey(other_config, "ignore_excessive_vcpus");
}
public void SetIgnoreExcessiveVcpus(bool value)
{
other_config = SetDictionaryKey(other_config, "ignore_excessive_vcpus", value.ToString().ToLower());
}
public string IsOnSharedStorage()
{
foreach (XenRef<VBD> vbdRef in VBDs)
{
VBD vbd = Connection.Resolve<VBD>(vbdRef);
if (vbd != null)
{
VDI vdi = Connection.Resolve<VDI>(vbd.VDI);
if (vdi != null)
{
SR sr = Connection.Resolve<SR>(vdi.SR);
if (sr != null && !sr.shared)
{
if (sr.content_type == SR.Content_Type_ISO)
{
return Messages.EJECT_YOUR_CD;
}
else
{
return Messages.VM_USES_LOCAL_STORAGE;
}
}
}
}
}
return "";
}
public decimal GetRecommendedExportSpace(bool showHiddenVMs)
{
decimal totalSpace = 0;
foreach (VBD vbd in Connection.ResolveAll(VBDs))
{
if (!vbd.IsCDROM())
{
VDI VDI = Connection.Resolve<VDI>(vbd.VDI);
if (VDI != null && VDI.Show(showHiddenVMs))
{
SR TheSR = Connection.Resolve(VDI.SR);
if (TheSR != null && !TheSR.IsToolsSR())
{
totalSpace += VDI.virtual_size;
}
}
}
}
return totalSpace;
}
public override int CompareTo(VM other)
{
// Sort in the following order:
// 1) Control domain
// 2) Normal VMs
// 3) Snapshots
// 4) User templates
// 5) Default templates
// Within each category, using CompareNames()
int myClass, otherClass;
if (is_control_domain)
myClass = 1;
else if (is_a_snapshot)
myClass = 3;
else if (is_a_template)
myClass = DefaultTemplate() ? 5 : 4;
else
myClass = 2;
if (other.is_control_domain)
otherClass = 1;
else if (other.is_a_snapshot)
otherClass = 3;
else if (other.is_a_template)
otherClass = other.DefaultTemplate() ? 5 : 4;
else
otherClass = 2;
if (myClass != otherClass)
return (myClass - otherClass);
else
return base.CompareTo(other);
}
/// <summary>
/// These are the operations that make us show the orange icon for the VM in the tree
/// and on the Memory tab. It's shorter to add the ones that cause problems.
/// </summary>
public static bool is_lifecycle_operation(vm_operations op)
{
return op != vm_operations.changing_dynamic_range && op != vm_operations.changing_static_range && op != vm_operations.changing_memory_limits;
}
private DateTime startuptime;
public DateTime GetBodgeStartupTime()
{
return startuptime;
}
public void SetBodgeStartupTime(DateTime value)
{
startuptime = value;
// This has an impact on the virt state of the VM as we allow a set amount of time for tools to show up before assuming unvirt
NotifyPropertyChanged("virtualisation_status");
if (VirtualizationTimer != null)
VirtualizationTimer.Stop();
// 2 minutes before we give up plus some breathing space
VirtualizationTimer = new Timer(182000) {AutoReset = false};
VirtualizationTimer.Elapsed += VirtualizationTimer_Elapsed;
VirtualizationTimer.Start();
}
void VirtualizationTimer_Elapsed(object sender, ElapsedEventArgs e)
{
NotifyPropertyChanged("virtualisation_status");
}
private Timer VirtualizationTimer = null;
[Flags]
public enum VirtualisationStatus
{
NOT_INSTALLED = 0,
UNKNOWN = 1,
PV_DRIVERS_OUT_OF_DATE = 2,
IO_DRIVERS_INSTALLED = 4,
MANAGEMENT_INSTALLED = 8,
};
public string GetVirtualisationWarningMessages()
{
VirtualisationStatus status = GetVirtualisationStatus(out _);
if (status.HasFlag(VirtualisationStatus.IO_DRIVERS_INSTALLED) && status.HasFlag(VirtualisationStatus.MANAGEMENT_INSTALLED)
|| status.HasFlag(VM.VirtualisationStatus.UNKNOWN))
// calling function shouldn't send us here if tools are, or might be, present: used to assert here but it can sometimes happen (CA-51460)
return "";
if (status.HasFlag(VM.VirtualisationStatus.PV_DRIVERS_OUT_OF_DATE))
{
VM_guest_metrics guestMetrics = Connection.Resolve(guest_metrics);
if (guestMetrics != null
&& guestMetrics.PV_drivers_version.ContainsKey("major")
&& guestMetrics.PV_drivers_version.ContainsKey("minor"))
{
return string.Format(Messages.PV_DRIVERS_OUT_OF_DATE, BrandManager.VmTools,
guestMetrics.PV_drivers_version["major"], guestMetrics.PV_drivers_version["minor"]);
}
return string.Format(Messages.PV_DRIVERS_OUT_OF_DATE_UNKNOWN_VERSION, BrandManager.VmTools);
}
return HasNewVirtualisationStates()
? Messages.VIRTUALIZATION_STATE_VM_MANAGEMENT_AGENT_NOT_INSTALLED
: string.Format(Messages.PV_DRIVERS_NOT_INSTALLED, BrandManager.VmTools);
}
/// <summary>
/// Virtualization Status of the VM
/// </summary>
///
/// <remarks>
/// Following states are expected:
///
/// For Non-Windows VMs and for Windows VMs pre-Dundee:
/// 0 = Not installed
/// 1 = Unknown
/// 2 = Out of date
/// 12 = Tools installed (Optimized)
///
/// For Windows VMs on Dundee or higher:
/// 0 = Not installed
/// 1 = Unknown
/// 4 = I/O Optimized
/// 12 = I/O and Management installed
/// </remarks>
public VirtualisationStatus GetVirtualisationStatus(out string friendlyStatus)
{
friendlyStatus = Messages.VIRTUALIZATION_UNKNOWN;
if (Connection == null || power_state != vm_power_state.Running || Connection.Resolve(metrics) == null)
return VirtualisationStatus.UNKNOWN;
var vmGuestMetrics = Connection.Resolve(guest_metrics);
var lessThanTwoMin = (DateTime.UtcNow - GetBodgeStartupTime()).TotalMinutes < 2;
if (HasNewVirtualisationStates())
{
var flags = VirtualisationStatus.NOT_INSTALLED;
if (vmGuestMetrics != null && vmGuestMetrics.PV_drivers_detected)
flags |= VirtualisationStatus.IO_DRIVERS_INSTALLED;
if (vmGuestMetrics != null && IntKey(vmGuestMetrics.other, "feature-static-ip-setting", 0) != 0)
flags |= VirtualisationStatus.MANAGEMENT_INSTALLED;
if (flags.HasFlag(VirtualisationStatus.IO_DRIVERS_INSTALLED | VirtualisationStatus.MANAGEMENT_INSTALLED))
friendlyStatus = Messages.VIRTUALIZATION_STATE_VM_IO_DRIVERS_AND_MANAGEMENT_AGENT_INSTALLED;
else if (flags.HasFlag(VirtualisationStatus.IO_DRIVERS_INSTALLED))
friendlyStatus = Messages.VIRTUALIZATION_STATE_VM_MANAGEMENT_AGENT_NOT_INSTALLED;
else if (lessThanTwoMin)
flags = VirtualisationStatus.UNKNOWN;
else
friendlyStatus = string.Format(Messages.PV_DRIVERS_NOT_INSTALLED, BrandManager.VmTools);
return flags;
}
if (vmGuestMetrics == null || !vmGuestMetrics.PV_drivers_installed())
if (lessThanTwoMin)
return VirtualisationStatus.UNKNOWN;
else
{
friendlyStatus = string.Format(Messages.PV_DRIVERS_NOT_INSTALLED, BrandManager.VmTools);
return VirtualisationStatus.NOT_INSTALLED;
}
if (!vmGuestMetrics.PV_drivers_version.TryGetValue("major", out var major))
major = "0";
if (!vmGuestMetrics.PV_drivers_version.TryGetValue("minor", out var minor))
minor = "0";
if (!vmGuestMetrics.PV_drivers_up_to_date)
{
friendlyStatus = string.Format(Messages.VIRTUALIZATION_OUT_OF_DATE, $"{major}.{minor}");
return VirtualisationStatus.PV_DRIVERS_OUT_OF_DATE;
}
friendlyStatus = string.Format(Messages.VIRTUALIZATION_OPTIMIZED, $"{major}.{minor}");
return VirtualisationStatus.IO_DRIVERS_INSTALLED | VirtualisationStatus.MANAGEMENT_INSTALLED;
}
/// <summary>
/// Is this a Windows VM on Dundee or higher host?
/// We need to know this, because for those VMs virtualization status is defined differently.
/// This does not mean new(ly created) VM
/// </summary>
public bool HasNewVirtualisationStates()
{
return IsWindows() && Helpers.DundeeOrGreater(Connection);
}
/// <summary>
/// Returns whether this VM support ballooning.
/// Real VMs support ballooning if tools are installed on a balloonable OS.
/// For templates we cannot tell whether tools are installed, so ballooning is
/// supported if and only if dynamic min != static_max (CA-34258/CA-34260).
/// </summary>
public bool SupportsBallooning()
{
if (Connection == null)
return false;
if (is_a_template)
return memory_dynamic_min != memory_static_max;
var otherKey = Connection.Resolve(guest_metrics)?.other;
return otherKey != null && otherKey.ContainsKey("feature-balloon");
}
/// <summary>
/// Whether the VM uses ballooning (has different setting of dynamic_max and static_max)
/// </summary>
public bool UsesBallooning()
{
return memory_dynamic_max != memory_static_max && SupportsBallooning();
}
/// <summary>
/// Whether the VM should be shown to the user in the GUI.
/// </summary>
public override bool Show(bool showHiddenVMs)
{
if (InternalTemplate())
return false;
if (name_label.StartsWith(Helpers.GuiTempObjectPrefix))
return false;
if (showHiddenVMs)
return true;
return !IsHidden();
}
/// <summary>
/// Returns whether the other_config.HideFromXenCenter flag is set to true.
/// </summary>
public override bool IsHidden()
{
return BoolKey(other_config, HIDE_FROM_XENCENTER);
}
public bool HasNoDisksAndNoLocalCD()
{
if (Connection == null)
return false;
foreach (VBD vbd in Connection.ResolveAll<VBD>(VBDs))
{
if (vbd.type == vbd_type.Disk)
{
return false; // we have a disk :(
}
else
{
VDI vdi = Connection.Resolve<VDI>(vbd.VDI);
if (vdi == null)
continue;
SR sr = Connection.Resolve<SR>(vdi.SR);
if (sr == null || sr.shared)
continue;
return false; // we have a shared cd
}
}
return true; // we have no disks hooray!!
}
private const string P2V_SOURCE_MACHINE = "p2v_source_machine";
private const string P2V_IMPORT_DATE = "p2v_import_date";
public bool IsP2V()
{
return other_config != null && other_config.ContainsKey(P2V_SOURCE_MACHINE) && other_config.ContainsKey(P2V_IMPORT_DATE);
}
/// <summary>
/// List of distros that we treat as Linux/Non-Windows (written in the VM.guest_metrics
/// by the Linux Guest Agent after evaluating xe-linux-distribution)
/// </summary>
private static string[] linuxDistros =
{
"debian", "rhel", "fedora", "centos", "scientific", "oracle", "sles",
2021-08-23 08:54:57 +02:00
"lsb", "boot2docker", "freebsd", "ubuntu", "neokylin", "gooroom", "rocky"
};
/// <summary>
/// Sort in the following order:
/// 1) User Templates
/// 2) Windows VMs
/// 3) Other VMs (e.g. Linux . Names in alphabetical order)
/// 4) Citrix VMs (e.g. XenApp templates)
/// 5) Misc VMs
/// 6) Regular snapshots
/// 7) Snapshots from VMPP (CA-46206)
/// Last: Hidden VMs (only visible if "Show Hidden Objects" is on: see CA-39036).
/// </summary>
public enum VmTemplateType
{
NoTemplate = 0,//it's not a template
Custom = 1,
Windows = 2,
LegacyWindows = 3,
Asianux = 4,
Centos = 5,
CoreOS = 6,
Debian = 7,
Gooroom = 8,
Linx = 9,
NeoKylin = 10,
Oracle = 11,
RedHat = 12,
2021-08-23 08:54:57 +02:00
Rocky = 13,
SciLinux = 14,
Suse = 15,
Turbo = 16,
Ubuntu = 17,
YinheKylin = 18,
Citrix = 19,
Solaris = 20,
Misc = 21,
Snapshot = 22,
SnapshotFromVmpp = 23,
Count = 24 //bump this if values are added
}
public VmTemplateType TemplateType()
{
if (!is_a_template)
return VmTemplateType.NoTemplate;
if (is_snapshot_from_vmpp)
return VmTemplateType.SnapshotFromVmpp;
if (is_a_snapshot)
return VmTemplateType.Snapshot;
if (!DefaultTemplate())
return VmTemplateType.Custom;
string os = name_label.ToLowerInvariant();
if (os.Contains("citrix"))
return VmTemplateType.Citrix;
if (os.Contains("debian"))
return VmTemplateType.Debian;
if (os.Contains("gooroom"))
return VmTemplateType.Gooroom;
2021-08-23 08:54:57 +02:00
if (os.Contains("rocky"))
return VmTemplateType.Rocky;
if (os.Contains("centos"))
return VmTemplateType.Centos;
if (os.Contains("linx"))
return VmTemplateType.Linx;
if (os.Contains("red hat"))
return VmTemplateType.RedHat;
if (os.Contains("oracle"))
return VmTemplateType.Oracle;
if (os.Contains("suse"))
return VmTemplateType.Suse;
if (os.Contains("scientific"))
return VmTemplateType.SciLinux;
if (os.Contains("legacy windows"))
return VmTemplateType.LegacyWindows;
if (os.Contains("windows"))
return VmTemplateType.Windows;
if (os.Contains("ubuntu"))
return VmTemplateType.Ubuntu;
if (os.Contains("yinhe"))
return VmTemplateType.YinheKylin;
if (os.Contains("kylin"))
return VmTemplateType.NeoKylin;
if (os.Contains("asianux"))
return VmTemplateType.Asianux;
if (os.Contains("turbo"))
return VmTemplateType.Turbo;
if (os.Contains("solaris"))
return VmTemplateType.Solaris;
if (os.Contains("coreos"))
return VmTemplateType.CoreOS;
return VmTemplateType.Misc;
}
public VmDescriptionType DescriptionType()
{
var templateType = TemplateType();
switch (templateType)
{
case VmTemplateType.NoTemplate:
case VmTemplateType.Custom:
case VmTemplateType.Snapshot:
case VmTemplateType.SnapshotFromVmpp:
return VmDescriptionType.ReadWrite;
case VmTemplateType.Misc:
return VmDescriptionType.ReadOnly;
default:
return VmDescriptionType.None;
}
}
public enum VmDescriptionType { None, ReadOnly, ReadWrite }
public override string Description()
{
// Don't i18n this
if (IsP2V() && name_description.StartsWith("VM imported from physical machine"))
return "";
if (DescriptionType() == VmDescriptionType.ReadOnly)
return FriendlyNameManager.GetFriendlyName("VM.TemplateDescription-" + name_label) ?? name_description;
//if this assertion fails it means the code calling this property
//should be checking beforehand what the DescriptionType is
Debug.Assert(DescriptionType() != VmDescriptionType.None);
return name_description;
}
public string P2V_SourceMachine()
{
return other_config != null && other_config.ContainsKey(P2V_SOURCE_MACHINE) ? other_config[P2V_SOURCE_MACHINE] : "";
}
public DateTime P2V_ImportDate()
{
if (other_config == null || !other_config.ContainsKey(P2V_IMPORT_DATE))
return DateTime.MinValue;
string importDate = other_config[P2V_IMPORT_DATE];
return Util.TryParseIso8601DateTime(importDate, out var result) ? result : DateTime.MinValue;
}
public static XenRef<Task> async_live_migrate(Session session, string _vm, string _host)
{
Dictionary<string, string> options = new Dictionary<string, string>();
options["live"] = "true";
return XenAPI.VM.async_pool_migrate(session, _vm, _host, options);
}
public String GetOSName()
{
VM_guest_metrics guestMetrics = Connection.Resolve(guest_metrics);
if (guestMetrics == null)
return Messages.UNKNOWN;
if (guestMetrics.os_version == null)
return Messages.UNKNOWN;
if (!guestMetrics.os_version.ContainsKey("name"))
return Messages.UNKNOWN;
String os_name = guestMetrics.os_version["name"];
// This hack is to make the windows names look nicer
int index = os_name.IndexOf("|");
if (index >= 1)
os_name = os_name.Substring(0, index);
// CA-9631: conform to MS trademark guidelines
if (os_name.StartsWith("Microsoft<66>"))
{
if (os_name != "Microsoft<66>")
os_name = os_name.Substring(10).Trim();
}
else if (os_name.StartsWith("Microsoft"))
{
if (os_name != "Microsoft")
os_name = os_name.Substring(9).Trim();
}
if (os_name == "")
return Messages.UNKNOWN;
else
return os_name;
}
/// <summary>
/// Gets the time this VM started, in server time, UTC. Returns DateTime.MinValue if there are no VM_metrics
/// to read.
/// </summary>
public DateTime GetStartTime()
{
VM_metrics metrics = Connection.Resolve(this.metrics);
if (metrics == null)
return DateTime.MinValue;
return metrics.start_time;
}
private static readonly DateTime Epoch = new DateTime(1970, 1, 1);
public PrettyTimeSpan RunningTime()
{
if (power_state != vm_power_state.Running &&
power_state != vm_power_state.Paused &&
power_state != vm_power_state.Suspended)
{
return null;
}
DateTime startTime = GetStartTime();
if (startTime == Epoch || startTime == DateTime.MinValue)
return null;
return new PrettyTimeSpan(DateTime.UtcNow - startTime - Connection.ServerTimeOffset);
}
/// <summary>
/// Returns DateTime.MinValue if the date is not present in other_config.
/// </summary>
public DateTime LastShutdownTime()
{
return other_config.ContainsKey("last_shutdown_time") &&
Util.TryParseIso8601DateTime(other_config["last_shutdown_time"], out var result)
? result
: DateTime.MinValue;
}
/// <remarks>
/// AlwaysRestartHighPriority and AlwaysRestart are replaced by Restart in Boston; we still keep them for backward compatibility
/// </remarks>
public enum HA_Restart_Priority { AlwaysRestartHighPriority, AlwaysRestart, Restart, BestEffort, DoNotRestart };
/// <summary>
/// An enum-ified version of ha_restart_priority: use this one instead.
/// NB setting this property does not change ha-always-run.
/// </summary>
public HA_Restart_Priority HARestartPriority()
{
return StringToPriority(this.ha_restart_priority);
}
public override string NameWithLocation()
{
if (this.Connection != null)
{
if (this.IsRealVm())
{
return base.NameWithLocation();
}
else if (this.is_a_snapshot)
{
var snapshotOf = this.Connection.Resolve(this.snapshot_of);
if (snapshotOf == null)
return base.NameWithLocation();
return string.Format(Messages.SNAPSHOT_OF_TITLE, Name(), snapshotOf.Name(), LocationString());
}
else if (this.is_a_template)
{
if (Helpers.IsPool(Connection))
return string.Format(Messages.OBJECT_IN_POOL, Name(), Connection.Name);
return string.Format(Messages.OBJECT_ON_SERVER, Name(), Connection.Name);
}
}
return base.NameWithLocation();
}
internal override string LocationString()
{
Host server = this.Home();
if (server != null)
return string.Format(Messages.ON_SERVER, server);
Pool pool = Helpers.GetPool(this.Connection);
if (pool != null)
return string.Format(Messages.IN_POOL, pool);
return string.Empty;
}
public static List<HA_Restart_Priority> GetAvailableRestartPriorities(IXenConnection connection)
{
var restartPriorities = new List<HA_Restart_Priority>();
2015-10-26 17:01:55 +01:00
restartPriorities.Add(HA_Restart_Priority.Restart);
restartPriorities.Add(HA_Restart_Priority.BestEffort);
restartPriorities.Add(HA_Restart_Priority.DoNotRestart);
return restartPriorities;
}
/// <summary>
/// Returns true if VM's restart priority is AlwaysRestart or AlwaysRestartHighPriority.
/// </summary>
public bool HaPriorityIsRestart()
{
return HaPriorityIsRestart(Connection, HARestartPriority());
}
public static bool HaPriorityIsRestart(IXenConnection connection, HA_Restart_Priority haRestartPriority)
{
2015-10-26 17:01:55 +01:00
return haRestartPriority == HA_Restart_Priority.Restart;
}
public static HA_Restart_Priority HaHighestProtectionAvailable(IXenConnection connection)
{
2015-10-26 17:01:55 +01:00
return HA_Restart_Priority.Restart;
}
public const string RESTART_PRIORITY_ALWAYS_RESTART_HIGH_PRIORITY = "0"; //only used for Pre-Boston pools
public const string RESTART_PRIORITY_ALWAYS_RESTART = "1"; //only used for Pre-Boston pools
/// <summary>
/// This is the new "Restart" priority in Boston, and will replace RESTART_PRIORITY_ALWAYS_RESTART_HIGH_PRIORITY and RESTART_PRIORITY_ALWAYS_RESTART
/// </summary>
public const string RESTART_PRIORITY_RESTART = "restart";
public const string RESTART_PRIORITY_BEST_EFFORT = "best-effort";
public const string RESTART_PRIORITY_DO_NOT_RESTART = "";
/// <summary>
/// Parses a HA_Restart_Priority into a string the server understands.
/// </summary>
/// <param name="priority"></param>
/// <returns></returns>
internal static string PriorityToString(HA_Restart_Priority priority)
{
switch (priority)
{
case HA_Restart_Priority.AlwaysRestartHighPriority:
return RESTART_PRIORITY_ALWAYS_RESTART_HIGH_PRIORITY;
case HA_Restart_Priority.AlwaysRestart:
return RESTART_PRIORITY_ALWAYS_RESTART;
case HA_Restart_Priority.Restart:
return RESTART_PRIORITY_RESTART;
case HA_Restart_Priority.BestEffort:
return RESTART_PRIORITY_BEST_EFFORT;
default:
return RESTART_PRIORITY_DO_NOT_RESTART;
}
}
internal static HA_Restart_Priority StringToPriority(string priority)
{
switch (priority)
{
case RESTART_PRIORITY_ALWAYS_RESTART_HIGH_PRIORITY:
return HA_Restart_Priority.AlwaysRestartHighPriority;
case RESTART_PRIORITY_RESTART:
return HA_Restart_Priority.Restart;
case RESTART_PRIORITY_DO_NOT_RESTART:
return HA_Restart_Priority.DoNotRestart;
case RESTART_PRIORITY_BEST_EFFORT:
return HA_Restart_Priority.BestEffort;
default:
return HA_Restart_Priority.AlwaysRestart;
}
}
/// <summary>
/// Whether HA is capable of restarting this VM (i.e. the VM is not a template or control domain).
/// </summary>
public bool HaCanProtect(bool showHiddenVMs)
{
return IsRealVm() && Show(showHiddenVMs);
}
/// <summary>
/// True if this VM's ha_restart_priority is not "Do not restart" and its pool has ha_enabled true.
/// </summary>
public bool HAIsProtected()
{
if (Connection == null)
return false;
Pool myPool = Helpers.GetPoolOfOne(Connection);
if (myPool == null)
return false;
return myPool.ha_enabled && HARestartPriority() != HA_Restart_Priority.DoNotRestart;
}
/// <summary>
2015-10-26 17:01:55 +01:00
/// Calls set_ha_restart_priority
/// </summary>
/// <param name="priority"></param>
public static void SetHaRestartPriority(Session session, VM vm, HA_Restart_Priority priority)
{
2015-10-26 17:01:55 +01:00
VM.set_ha_restart_priority(session, vm.opaque_ref, PriorityToString(priority));
}
public bool AnyDiskFastClonable()
{
if (Connection == null)
return false;
foreach (VBD vbd in Connection.ResolveAll(VBDs))
{
if (vbd.type != vbd_type.Disk)
continue;
VDI vdi = Connection.Resolve(vbd.VDI);
if (vdi == null)
continue;
SR sr = Connection.Resolve(vdi.SR);
if (sr == null)
continue;
SM sm = SM.GetByType(Connection, sr.type);
if (sm == null)
continue;
if (Array.IndexOf(sm.capabilities, "VDI_CLONE") != -1)
return true;
}
return false;
}
public bool HasAtLeastOneDisk()
{
if (Connection == null)
return false;
foreach (VBD vbd in Connection.ResolveAll(VBDs))
{
if (vbd.type != vbd_type.Disk)
continue;
return true;
}
return false;
}
/// <summary>
/// Checks whether the VM is the dom0 (the flag is_control_domain may also apply to other control domains)
/// </summary>
public bool IsControlDomainZero(out Host host)
{
host = null;
if (!is_control_domain)
return false;
host = Connection.Resolve(resident_on);
if (host == null)
return false;
if (!Helper.IsNullOrEmptyOpaqueRef(host.control_domain))
return host.control_domain == opaque_ref;
var vms = Connection.ResolveAll(host.resident_VMs);
var first = vms.FirstOrDefault(vm => vm.is_control_domain && vm.domid == 0);
return first != null && first.opaque_ref == opaque_ref;
}
public bool IsSrDriverDomain(out SR sr)
{
sr = null;
if (!is_control_domain || IsControlDomainZero(out _))
return false;
foreach (var pbd in Connection.Cache.PBDs)
{
if (pbd != null &&
pbd.other_config.TryGetValue("storage_driver_domain", out string vmRef) &&
vmRef == opaque_ref)
{
sr = Connection.Resolve(pbd.SR);
if (sr != null)
return true;
}
}
return false;
}
public bool IsRealVm()
{
return !is_a_snapshot && !is_a_template && !is_control_domain;
}
[CA-233454] PVS tab doesn't show a new VM when it's created (#1314) * [CA-233454] PVS tab doesn't show a new VM when it's created Refined the rules for not adding a VM to the table, if it is a template (thus not_a_real_vm), and it has the __gui__ prefix (thus hidden), we still add it, but hide it. When its name is changed (to remove the __gui__ prefix), we update its name and re-calculate whether it should be visible (in the case of a new VM this will be true once the __gui__ prefix is gone). Also resort the table if a node changes from hidden to visible, because it appears as an addition to the table. Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * [CA-233454] Update criteria for VM visibility to include is_a_real_vm is_a_template is changed before the name_label removes the __gui__ prefix, so this works with no other changes to the vm property changed event. Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * [CA-233454] Update when the VMs are shown in the PVS list New observable property IsBeingCreated for VMs, set to true when they're made a hidden object, and false when they're removed from hidden objects (both in CreateVMAction). In the PVS Page when this is set to false, we re-evaluate whether a VM can be shown. This means that new VMs show here at the same time they're added to the tree (only different is tree refresh time), instead of far earlier (and before their networks were added). Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * [CA-233454] Set IsbeingCreated in the CreateVMFastAction * [CA-233454] Properly support the VM Fast Create action Further changes to CreateVMFastAction, to ensure it works with PVS tab - use the __gui__ prefix when the VM is created and then change it back just before showing. * [CA-233454] Small logic adjustments/tidying up
2016-11-30 13:24:02 +01:00
private bool _isBeingCreated;
[JsonIgnore]
[CA-233454] PVS tab doesn't show a new VM when it's created (#1314) * [CA-233454] PVS tab doesn't show a new VM when it's created Refined the rules for not adding a VM to the table, if it is a template (thus not_a_real_vm), and it has the __gui__ prefix (thus hidden), we still add it, but hide it. When its name is changed (to remove the __gui__ prefix), we update its name and re-calculate whether it should be visible (in the case of a new VM this will be true once the __gui__ prefix is gone). Also resort the table if a node changes from hidden to visible, because it appears as an addition to the table. Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * [CA-233454] Update criteria for VM visibility to include is_a_real_vm is_a_template is changed before the name_label removes the __gui__ prefix, so this works with no other changes to the vm property changed event. Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * [CA-233454] Update when the VMs are shown in the PVS list New observable property IsBeingCreated for VMs, set to true when they're made a hidden object, and false when they're removed from hidden objects (both in CreateVMAction). In the PVS Page when this is set to false, we re-evaluate whether a VM can be shown. This means that new VMs show here at the same time they're added to the tree (only different is tree refresh time), instead of far earlier (and before their networks were added). Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * [CA-233454] Set IsbeingCreated in the CreateVMFastAction * [CA-233454] Properly support the VM Fast Create action Further changes to CreateVMFastAction, to ensure it works with PVS tab - use the __gui__ prefix when the VM is created and then change it back just before showing. * [CA-233454] Small logic adjustments/tidying up
2016-11-30 13:24:02 +01:00
public bool IsBeingCreated
{
get { return _isBeingCreated; }
set
{
_isBeingCreated = value;
NotifyPropertyChanged("IsBeingCreated");
}
}
public XmlNode ProvisionXml()
{
try
{
string xml = Get(other_config, "disks");
if (string.IsNullOrEmpty(xml))
return null;
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
return doc.FirstChild;
}
catch (Exception)
{
return null;
}
}
public bool InstantTemplate()
{
return BoolKey(other_config, "instant");
}
public bool IsConversionVM()
{
return IsRealVm() && BoolKey(other_config, "conversionvm");
}
public override string ToString()
{
return name_label;
}
/// <summary>
/// The name label of the VM's affinity server, or None if it is not set
/// (This is what the UI calls the "home server", but is not the same as VM.Home).
/// </summary>
public string AffinityServerString()
{
Host host = Connection.Resolve(affinity);
if (host == null)
return Messages.NONE;
return host.Name();
}
public bool HasProvisionXML()
{
return other_config != null && other_config.ContainsKey("disks");
}
public bool BiosStringsCopied()
{
if (DefaultTemplate())
{
return false;
}
if (bios_strings.Count == 0)
{
return false;
}
bool value = bios_strings.ContainsKey("bios-vendor") && bios_strings["bios-vendor"] == "Xen"
&& bios_strings.ContainsKey("system-manufacturer") && bios_strings["system-manufacturer"] == "Xen";
return !value;
}
public bool HasCD()
{
foreach (var vbd in this.Connection.ResolveAll<VBD>(this.VBDs))
{
if (vbd.IsCDROM())
{
return true;
}
}
return false;
}
public bool HasVGPUs()
{
return VGPUs != null && VGPUs.Count > 0;
}
public bool HasGPUPassthrough()
{
if (VGPUs != null && VGPUs.Count > 0)
{
var vGPUs = Connection.ResolveAll(VGPUs);
return vGPUs.Any(vGPU => vGPU != null && vGPU.IsPassthrough());
}
return false;
}
public virtual IEnumerable<SR> SRs()
{
List<VBD> vbds = Connection.ResolveAll(VBDs);
foreach (var vbd in vbds)
{
if (vbd != null)
{
VDI vdi = vbd.Connection.Resolve(vbd.VDI);
if (vdi != null)
{
yield return vdi.Connection.Resolve(vdi.SR);
}
}
}
}
public bool IsAssignedToVapp()
{
//on pre-boston servers appliance is null
return appliance != null && appliance.opaque_ref != null &&
appliance.opaque_ref.StartsWith("OpaqueRef:") &&
appliance.opaque_ref != "OpaqueRef:NULL";
}
public long GetCoresPerSocket()
{
if (platform != null && platform.ContainsKey("cores-per-socket"))
{
long coresPerSocket;
return long.TryParse(platform["cores-per-socket"], out coresPerSocket) ? coresPerSocket : DEFAULT_CORES_PER_SOCKET;
}
return DEFAULT_CORES_PER_SOCKET;
}
public void SetCoresPerSocket(long value)
{
platform = SetDictionaryKey(platform, "cores-per-socket", value.ToString());
}
public long MaxCoresPerSocket()
{
var homeServer = Home();
if (homeServer != null)
return homeServer.CoresPerSocket();
var maxCoresPerSocket = 0;
foreach (var host in this.Connection.Cache.Hosts)
{
var coresPerSocket = host.CoresPerSocket();
if (coresPerSocket > maxCoresPerSocket)
maxCoresPerSocket = coresPerSocket;
}
return maxCoresPerSocket;
}
public bool HasValidVCPUConfiguration()
{
return ValidVCPUConfiguration(VCPUs_max, GetCoresPerSocket()) == "";
}
public static string ValidVCPUConfiguration(long noOfVCPUs, long coresPerSocket)
{
if (coresPerSocket > 0)
{
if (noOfVCPUs % coresPerSocket != 0)
return Messages.CPU_TOPOLOGY_INVALID_REASON_MULTIPLE;
if (noOfVCPUs / coresPerSocket > MAX_SOCKETS)
return Messages.CPU_TOPOLOGY_INVALID_REASON_SOCKETS;
}
return "";
}
public string Topology()
{
var cores = GetCoresPerSocket();
var sockets = ValidVCPUConfiguration(VCPUs_max, cores) == "" ? VCPUs_max/cores : 0;
return GetTopology(sockets, cores);
}
public static string GetTopology(long sockets, long cores)
{
if (sockets == 0) // invalid cores value
return cores == 1 ? string.Format(Messages.CPU_TOPOLOGY_STRING_INVALID_VALUE_1) : string.Format(Messages.CPU_TOPOLOGY_STRING_INVALID_VALUE, cores);
if (sockets == 1 && cores == 1)
return Messages.CPU_TOPOLOGY_STRING_1_SOCKET_1_CORE;
if (sockets == 1)
return string.Format(Messages.CPU_TOPOLOGY_STRING_1_SOCKET_N_CORE, cores);
if (cores == 1)
return string.Format(Messages.CPU_TOPOLOGY_STRING_N_SOCKET_1_CORE, sockets);
return string.Format(Messages.CPU_TOPOLOGY_STRING_N_SOCKET_N_CORE, sockets, cores);
}
public bool CanBeEnlightened()
{
return other_config.ContainsKey("xscontainer-monitor");
}
public bool IsEnlightened()
{
var v = Get(other_config, "xscontainer-monitor");
return v != null && v.ToLower() == "true";
}
public VDI CloudConfigDrive()
{
var vbds = Connection.ResolveAll(VBDs);
return vbds.Select(vbd => Connection.Resolve(vbd.VDI)).FirstOrDefault(vdi => vdi != null && vdi.IsCloudConfigDrive());
}
public bool CanHaveCloudConfigDrive()
{
if (is_a_template && TemplateType() == VmTemplateType.CoreOS)
return true;
//otherwise check if it has a config drive
return CloudConfigDrive() != null;
}
public VM_Docker_Info DockerInfo()
{
string xml = Get(other_config, "docker_info");
if (string.IsNullOrEmpty(xml))
return null;
VM_Docker_Info info = new VM_Docker_Info(xml);
return info;
}
public VM_Docker_Version DockerVersion()
{
string xml = Get(other_config, "docker_version");
if (string.IsNullOrEmpty(xml))
return null;
VM_Docker_Version info = new VM_Docker_Version(xml);
return info;
}
public bool ReadCachingEnabled()
{
return ReadCachingVDIs().Count > 0;
}
/// <summary>
/// Return the list of VDIs that have Read Caching enabled
/// </summary>
public List<VDI> ReadCachingVDIs()
{
var readCachingVdis = new List<VDI>();
foreach (var vbd in Connection.ResolveAll(VBDs).Where(vbd => vbd != null && vbd.currently_attached))
{
var vdi = Connection.Resolve(vbd.VDI);
var resident_host = Connection.Resolve(resident_on);
if (vdi != null && resident_host != null && vdi.ReadCachingEnabled(resident_host))
readCachingVdis.Add(vdi);
}
return readCachingVdis;
}
public string ReadCachingDisabledReason()
{
// The code in VDI.ReadCachingDisabledReason returns the first matching reason from the list
// (LICENSE_RESTRICTION, SR_NOT_SUPPORTED, NO_RO_IMAGE, SR_OVERRIDE). In the case that there
// are several VDIs with different reasons, this function returns the last matching reason,
// because that is the VDI that is nearest to being read-cachable in some sense. As the reasons
// are stored in an enum, we can just use greater-than to find the last reason
var ans = VDI.ReadCachingDisabledReasonCode.UNKNOWN;
foreach (var vbd in Connection.ResolveAll(VBDs).Where(vbd => vbd != null && vbd.currently_attached))
{
var vdi = Connection.Resolve(vbd.VDI);
var resident_host = Connection.Resolve(resident_on);
if (vdi != null && resident_host != null && !vdi.ReadCachingEnabled(resident_host))
{
var reason = vdi.ReadCachingDisabledReason(resident_host);
if (reason > ans)
ans = reason;
}
}
switch (ans)
{
case VDI.ReadCachingDisabledReasonCode.LICENSE_RESTRICTION:
if (Helpers.FeatureForbidden(Connection, Host.RestrictReadCaching))
return Messages.VM_READ_CACHING_DISABLED_REASON_LICENSE;
else
return Messages.VM_READ_CACHING_DISABLED_REASON_PREV_LICENSE;
case VDI.ReadCachingDisabledReasonCode.SR_NOT_SUPPORTED:
return Messages.VM_READ_CACHING_DISABLED_REASON_SR_TYPE;
case VDI.ReadCachingDisabledReasonCode.NO_RO_IMAGE:
return Messages.VM_READ_CACHING_DISABLED_REASON_NO_RO_IMAGE;
case VDI.ReadCachingDisabledReasonCode.SR_OVERRIDE:
return Messages.VM_READ_CACHING_DISABLED_REASON_TURNED_OFF;
default:
// should only happen transiently
return null;
}
}
/// <summary>
/// Whether the VM can be moved inside the pool (vdi copy + destroy)
/// </summary>
public bool CanBeMoved()
{
if (SRs().Any(sr => sr != null && sr.HBALunPerVDI()))
return false;
if (!is_a_template && !Locked && allowed_operations != null && allowed_operations.Contains(vm_operations.export) && power_state != vm_power_state.Suspended)
{
return Connection.ResolveAll(VBDs).Find(v => v.GetIsOwner()) != null;
}
return false;
}
/// <summary>
/// Returns whether this is a Windows VM by checking the distro value in the
/// guest_metrics before falling back to the viridian flag. The result may not be
/// correct at all times (a Linux distro can be detected if the guest agent is
/// running on the VM). It is more reliable if the VM has already booted once, and
/// also works for the "Other Install Media" template and unbooted VMs made from it.
/// </summary>
public bool IsWindows()
{
var gm = Connection.Resolve(guest_metrics);
if (CanTreatAsNonWindows(gm))
return false;
var gmFromLastBootedRecord = GuestMetricsFromLastBootedRecord();
if (CanTreatAsNonWindows(gmFromLastBootedRecord))
return false;
return
IsHVM() && BoolKey(platform, "viridian");
}
private static bool CanTreatAsNonWindows(VM_guest_metrics guestMetrics)
{
if (guestMetrics?.os_version == null)
return false;
if (guestMetrics.os_version.TryGetValue("distro", out var distro) &&
!string.IsNullOrEmpty(distro) && linuxDistros.Contains(distro.ToLowerInvariant()))
return true;
if (guestMetrics.os_version.TryGetValue("uname", out var uname) &&
!string.IsNullOrEmpty(uname) && uname.ToLowerInvariant().Contains("netscaler"))
return true;
return false;
}
private VM_guest_metrics GuestMetricsFromLastBootedRecord()
{
if (!string.IsNullOrEmpty(last_booted_record))
{
Regex regex = new Regex("'guest_metrics' +'(OpaqueRef:[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})'");
var v = regex.Match(last_booted_record);
if (v.Success)
{
string s = v.Groups[1].ToString();
return Connection.Resolve(new XenRef<VM_guest_metrics>(s));
}
}
return null;
}
public bool WindowsUpdateCapable()
{
return this.has_vendor_device && this.IsWindows();
}
/// <summary>
/// Returns the VM IP address for SSH login.
/// </summary>
public string IPAddressForSSH()
{
List<string> ipAddresses = new List<string>();
if (!this.is_control_domain) //vm
{
List<VIF> vifs = this.Connection.ResolveAll(this.VIFs);
vifs.Sort();
foreach (var vif in vifs)
{
if (!vif.currently_attached)
continue;
var network = vif.Connection.Resolve(vif.network);
if (network != null && network.IsGuestInstallerNetwork())
continue;
ipAddresses.AddRange(vif.IPAddresses());
}
}
else //control domain
{
List<PIF> pifList = new List<PIF>(this.Connection.Cache.PIFs);
pifList.Sort(); // This sort ensures that the primary PIF comes before other management PIFs
foreach (PIF pif in pifList)
{
if (pif.host.opaque_ref != this.resident_on.opaque_ref || !pif.currently_attached)
continue;
if (pif.IsManagementInterface(false))
{
ipAddresses.Add(pif.IP);
}
}
}
//find first IPv4 address and return it - we would use it if there is one
IPAddress addr;
foreach (string addrString in ipAddresses)
if (IPAddress.TryParse(addrString, out addr) && addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
return addrString;
//return the first address (this will not be IPv4)
return ipAddresses.FirstOrDefault() ?? string.Empty;
}
public bool HciWarnBeforeShutdown()
{
return other_config != null && other_config.ContainsKey("hci-warn-before-shutdown");
}
public bool SupportsVcpuHotplug()
{
return !IsWindows() && !Helpers.FeatureForbidden(Connection, Host.RestrictVcpuHotplug);
}
CP-16922: Implement GUI for enabling PVS read caching (#1179) * CP-16922: UI changes to PvsPage to replace the single configure button with a separate enable and disable one, per the design doc * CP-16922: Intermediate checkin - command + dialog implemented, but PVS page has some layout problems. * CP-16922: Implement GUI for enabling PVS read caching and marking/unmarking VMs for prepopulation Fix the broken layout by reverting MainWindow.resx (which shouldn't have changed) back to the previous commit. Also removed duplicate properties button code. * CP-16922: PVS Page changes, to properly display all VMs (not just ones with proxy), only populating proxy columns if there is a proxy set. * CP-16922: Use a CommandButton for the enable button, connected to the same command as the VM main menu button, and using the table selection as its selection manager. * CP-16922: Delete the old enable button click behaviour (because it now has a proper command), was causing the dialog to show up twice * CP-16922: Properly list VMs by hiding those that should be hidden (in addition to non-VMs, also hide the __gui__ prefix and others covered by Show function). Also fixes a bug in NewVMGroupVMsPage discovered when fixing this - it would show VMs that should be hidden if you used the search bar. * CP-16922: Use ButtonText on the Command to set the text on the PVS enable button * CP-16922: Add the disable PVS read-caching command, and its associated proxy destroy action. Both based off enable equivalents (main difference is no dialog for now). Not used yet but will be in main window menu + disable button * CP-16922: Add disable PVS read-caching to the main window menu (under enable), with the disable command attached to it * CP-16922: Add support for disabling PVS read-caching for selected hosts via the PVS tab page by converting the disable button into a CommandButton in the same way as the enable button, using its own command + selection manager (because Selection Managers only bind to one control) * CP-16922: Design - adding the "read caching enabled" column from the design, so that we display both whether caching is enabled and whether the VM is currently cached. If it's disabled all other values are missing. * CP-16922: Add the PVS site column per the design to show the site a VM's PVS Proxy is on if it has a proxy * CP-16922: Add an event handler for VMs changing so that newly provisioned VMs immediately show up in the PVS tab page * CP-16922: Support for properly remembering the selection when the VM list is refreshed. A new method returns the list of VMs for the selected rows, and we can then check if the VM for the row being added is in this list. If there was no previous selection then we still select the first row by default. * CP-16922: Remove prepopulate, which is being removed from the design. The next XAPI version will remove it, so just pass False as prepopulate for now, and remove the option from the dialog and PVS tab page. * CP-16922: Improve UI appearance for a missing Storage Repository. Per the design, if there is no SR show the message None (memory only) instead of an empty cell * CP-16922: Improved sorting behaviour. All columns (except VM name, which always uses it) use the VM tag as a sorting fallback (since they all have duplicates). New default sort which sorts by caching enabled first (yes before no), and then VM tag as a tiebreaker. Support for remembering the current sort on refresh, ie when a value changes. * CP-16922: Refactoring to use a VM extension method to get the PVSProxy (or null if none exists) for a VM. This was used in the PVS Page + commands which are now much simpler. * CP-16922: Move where the SelectionChanged event handler is set when refreshing the PVS table, so that the command buttons are updated correctly. * CP-16922: Improvements to PVS read-caching dialog. Set accept/cancel buttons properly + adjust tab values, so focus acts as expected * CP-16922: Remove the images from the menu for Enable/Disable PVS read-caching, most commands in the menu don't have one * CP-16922: Sorting out the strings for the actions + ParallelAction calls, and moving them into the resx. Also reverting wrong MainWindow.resx changes. * CAR-2184: XAPI changes. I've made the minimal possible changes to PvsPage to ensure it compiles, but it's had a lot of feature work (CP-16922) that supersedes this, these small changes should keep that merge painless. * CAR-2184 XAPI: Reverse the unrelated changes, only keep the PVS ones. * CP-16922: Merge latest XAPI changes, notably changes to Pvs_site.create * CP-16922: Change tab show/hide logic for PVS tab, show it even if there are no PVS sites. * CP-16922: Implement GUI for enabling PVS read caching Design changes - remove the storage repo column and replace it with status, which is populated with a friendly version of the pvs_proxy_status enum. Also populate site list using site.name_label Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * CP-16922: Implement GUI for enabling PVS read caching Adjusting the CanExecute of Enable PVS read-caching command to require at least one PVS site to be created. Since the PVS tab can show up without any sites, we need this because we can't create a proxy without a site. * CP-16922: Code review changes. Tidied up messages, and ensured actions run async properly, and without exiting before their task. * CP-16922: Code review changes. Update dialog resx, wider combobox on dialog, update action descriptions before setting RelatedTask, add friendly message body for PVS error notifications.
2016-09-22 16:08:15 +02:00
public PVS_proxy PvsProxy()
CP-16922: Implement GUI for enabling PVS read caching (#1179) * CP-16922: UI changes to PvsPage to replace the single configure button with a separate enable and disable one, per the design doc * CP-16922: Intermediate checkin - command + dialog implemented, but PVS page has some layout problems. * CP-16922: Implement GUI for enabling PVS read caching and marking/unmarking VMs for prepopulation Fix the broken layout by reverting MainWindow.resx (which shouldn't have changed) back to the previous commit. Also removed duplicate properties button code. * CP-16922: PVS Page changes, to properly display all VMs (not just ones with proxy), only populating proxy columns if there is a proxy set. * CP-16922: Use a CommandButton for the enable button, connected to the same command as the VM main menu button, and using the table selection as its selection manager. * CP-16922: Delete the old enable button click behaviour (because it now has a proper command), was causing the dialog to show up twice * CP-16922: Properly list VMs by hiding those that should be hidden (in addition to non-VMs, also hide the __gui__ prefix and others covered by Show function). Also fixes a bug in NewVMGroupVMsPage discovered when fixing this - it would show VMs that should be hidden if you used the search bar. * CP-16922: Use ButtonText on the Command to set the text on the PVS enable button * CP-16922: Add the disable PVS read-caching command, and its associated proxy destroy action. Both based off enable equivalents (main difference is no dialog for now). Not used yet but will be in main window menu + disable button * CP-16922: Add disable PVS read-caching to the main window menu (under enable), with the disable command attached to it * CP-16922: Add support for disabling PVS read-caching for selected hosts via the PVS tab page by converting the disable button into a CommandButton in the same way as the enable button, using its own command + selection manager (because Selection Managers only bind to one control) * CP-16922: Design - adding the "read caching enabled" column from the design, so that we display both whether caching is enabled and whether the VM is currently cached. If it's disabled all other values are missing. * CP-16922: Add the PVS site column per the design to show the site a VM's PVS Proxy is on if it has a proxy * CP-16922: Add an event handler for VMs changing so that newly provisioned VMs immediately show up in the PVS tab page * CP-16922: Support for properly remembering the selection when the VM list is refreshed. A new method returns the list of VMs for the selected rows, and we can then check if the VM for the row being added is in this list. If there was no previous selection then we still select the first row by default. * CP-16922: Remove prepopulate, which is being removed from the design. The next XAPI version will remove it, so just pass False as prepopulate for now, and remove the option from the dialog and PVS tab page. * CP-16922: Improve UI appearance for a missing Storage Repository. Per the design, if there is no SR show the message None (memory only) instead of an empty cell * CP-16922: Improved sorting behaviour. All columns (except VM name, which always uses it) use the VM tag as a sorting fallback (since they all have duplicates). New default sort which sorts by caching enabled first (yes before no), and then VM tag as a tiebreaker. Support for remembering the current sort on refresh, ie when a value changes. * CP-16922: Refactoring to use a VM extension method to get the PVSProxy (or null if none exists) for a VM. This was used in the PVS Page + commands which are now much simpler. * CP-16922: Move where the SelectionChanged event handler is set when refreshing the PVS table, so that the command buttons are updated correctly. * CP-16922: Improvements to PVS read-caching dialog. Set accept/cancel buttons properly + adjust tab values, so focus acts as expected * CP-16922: Remove the images from the menu for Enable/Disable PVS read-caching, most commands in the menu don't have one * CP-16922: Sorting out the strings for the actions + ParallelAction calls, and moving them into the resx. Also reverting wrong MainWindow.resx changes. * CAR-2184: XAPI changes. I've made the minimal possible changes to PvsPage to ensure it compiles, but it's had a lot of feature work (CP-16922) that supersedes this, these small changes should keep that merge painless. * CAR-2184 XAPI: Reverse the unrelated changes, only keep the PVS ones. * CP-16922: Merge latest XAPI changes, notably changes to Pvs_site.create * CP-16922: Change tab show/hide logic for PVS tab, show it even if there are no PVS sites. * CP-16922: Implement GUI for enabling PVS read caching Design changes - remove the storage repo column and replace it with status, which is populated with a friendly version of the pvs_proxy_status enum. Also populate site list using site.name_label Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * CP-16922: Implement GUI for enabling PVS read caching Adjusting the CanExecute of Enable PVS read-caching command to require at least one PVS site to be created. Since the PVS tab can show up without any sites, we need this because we can't create a proxy without a site. * CP-16922: Code review changes. Tidied up messages, and ensured actions run async properly, and without exiting before their task. * CP-16922: Code review changes. Update dialog resx, wider combobox on dialog, update action descriptions before setting RelatedTask, add friendly message body for PVS error notifications.
2016-09-22 16:08:15 +02:00
{
return Connection.Cache.PVS_proxies.FirstOrDefault(p =>
CP-16922: Implement GUI for enabling PVS read caching (#1179) * CP-16922: UI changes to PvsPage to replace the single configure button with a separate enable and disable one, per the design doc * CP-16922: Intermediate checkin - command + dialog implemented, but PVS page has some layout problems. * CP-16922: Implement GUI for enabling PVS read caching and marking/unmarking VMs for prepopulation Fix the broken layout by reverting MainWindow.resx (which shouldn't have changed) back to the previous commit. Also removed duplicate properties button code. * CP-16922: PVS Page changes, to properly display all VMs (not just ones with proxy), only populating proxy columns if there is a proxy set. * CP-16922: Use a CommandButton for the enable button, connected to the same command as the VM main menu button, and using the table selection as its selection manager. * CP-16922: Delete the old enable button click behaviour (because it now has a proper command), was causing the dialog to show up twice * CP-16922: Properly list VMs by hiding those that should be hidden (in addition to non-VMs, also hide the __gui__ prefix and others covered by Show function). Also fixes a bug in NewVMGroupVMsPage discovered when fixing this - it would show VMs that should be hidden if you used the search bar. * CP-16922: Use ButtonText on the Command to set the text on the PVS enable button * CP-16922: Add the disable PVS read-caching command, and its associated proxy destroy action. Both based off enable equivalents (main difference is no dialog for now). Not used yet but will be in main window menu + disable button * CP-16922: Add disable PVS read-caching to the main window menu (under enable), with the disable command attached to it * CP-16922: Add support for disabling PVS read-caching for selected hosts via the PVS tab page by converting the disable button into a CommandButton in the same way as the enable button, using its own command + selection manager (because Selection Managers only bind to one control) * CP-16922: Design - adding the "read caching enabled" column from the design, so that we display both whether caching is enabled and whether the VM is currently cached. If it's disabled all other values are missing. * CP-16922: Add the PVS site column per the design to show the site a VM's PVS Proxy is on if it has a proxy * CP-16922: Add an event handler for VMs changing so that newly provisioned VMs immediately show up in the PVS tab page * CP-16922: Support for properly remembering the selection when the VM list is refreshed. A new method returns the list of VMs for the selected rows, and we can then check if the VM for the row being added is in this list. If there was no previous selection then we still select the first row by default. * CP-16922: Remove prepopulate, which is being removed from the design. The next XAPI version will remove it, so just pass False as prepopulate for now, and remove the option from the dialog and PVS tab page. * CP-16922: Improve UI appearance for a missing Storage Repository. Per the design, if there is no SR show the message None (memory only) instead of an empty cell * CP-16922: Improved sorting behaviour. All columns (except VM name, which always uses it) use the VM tag as a sorting fallback (since they all have duplicates). New default sort which sorts by caching enabled first (yes before no), and then VM tag as a tiebreaker. Support for remembering the current sort on refresh, ie when a value changes. * CP-16922: Refactoring to use a VM extension method to get the PVSProxy (or null if none exists) for a VM. This was used in the PVS Page + commands which are now much simpler. * CP-16922: Move where the SelectionChanged event handler is set when refreshing the PVS table, so that the command buttons are updated correctly. * CP-16922: Improvements to PVS read-caching dialog. Set accept/cancel buttons properly + adjust tab values, so focus acts as expected * CP-16922: Remove the images from the menu for Enable/Disable PVS read-caching, most commands in the menu don't have one * CP-16922: Sorting out the strings for the actions + ParallelAction calls, and moving them into the resx. Also reverting wrong MainWindow.resx changes. * CAR-2184: XAPI changes. I've made the minimal possible changes to PvsPage to ensure it compiles, but it's had a lot of feature work (CP-16922) that supersedes this, these small changes should keep that merge painless. * CAR-2184 XAPI: Reverse the unrelated changes, only keep the PVS ones. * CP-16922: Merge latest XAPI changes, notably changes to Pvs_site.create * CP-16922: Change tab show/hide logic for PVS tab, show it even if there are no PVS sites. * CP-16922: Implement GUI for enabling PVS read caching Design changes - remove the storage repo column and replace it with status, which is populated with a friendly version of the pvs_proxy_status enum. Also populate site list using site.name_label Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * CP-16922: Implement GUI for enabling PVS read caching Adjusting the CanExecute of Enable PVS read-caching command to require at least one PVS site to be created. Since the PVS tab can show up without any sites, we need this because we can't create a proxy without a site. * CP-16922: Code review changes. Tidied up messages, and ensured actions run async properly, and without exiting before their task. * CP-16922: Code review changes. Update dialog resx, wider combobox on dialog, update action descriptions before setting RelatedTask, add friendly message body for PVS error notifications.
2016-09-22 16:08:15 +02:00
{
var vm = p.VM();
return vm != null && vm.Equals(this);
}); // null if none
CP-16922: Implement GUI for enabling PVS read caching (#1179) * CP-16922: UI changes to PvsPage to replace the single configure button with a separate enable and disable one, per the design doc * CP-16922: Intermediate checkin - command + dialog implemented, but PVS page has some layout problems. * CP-16922: Implement GUI for enabling PVS read caching and marking/unmarking VMs for prepopulation Fix the broken layout by reverting MainWindow.resx (which shouldn't have changed) back to the previous commit. Also removed duplicate properties button code. * CP-16922: PVS Page changes, to properly display all VMs (not just ones with proxy), only populating proxy columns if there is a proxy set. * CP-16922: Use a CommandButton for the enable button, connected to the same command as the VM main menu button, and using the table selection as its selection manager. * CP-16922: Delete the old enable button click behaviour (because it now has a proper command), was causing the dialog to show up twice * CP-16922: Properly list VMs by hiding those that should be hidden (in addition to non-VMs, also hide the __gui__ prefix and others covered by Show function). Also fixes a bug in NewVMGroupVMsPage discovered when fixing this - it would show VMs that should be hidden if you used the search bar. * CP-16922: Use ButtonText on the Command to set the text on the PVS enable button * CP-16922: Add the disable PVS read-caching command, and its associated proxy destroy action. Both based off enable equivalents (main difference is no dialog for now). Not used yet but will be in main window menu + disable button * CP-16922: Add disable PVS read-caching to the main window menu (under enable), with the disable command attached to it * CP-16922: Add support for disabling PVS read-caching for selected hosts via the PVS tab page by converting the disable button into a CommandButton in the same way as the enable button, using its own command + selection manager (because Selection Managers only bind to one control) * CP-16922: Design - adding the "read caching enabled" column from the design, so that we display both whether caching is enabled and whether the VM is currently cached. If it's disabled all other values are missing. * CP-16922: Add the PVS site column per the design to show the site a VM's PVS Proxy is on if it has a proxy * CP-16922: Add an event handler for VMs changing so that newly provisioned VMs immediately show up in the PVS tab page * CP-16922: Support for properly remembering the selection when the VM list is refreshed. A new method returns the list of VMs for the selected rows, and we can then check if the VM for the row being added is in this list. If there was no previous selection then we still select the first row by default. * CP-16922: Remove prepopulate, which is being removed from the design. The next XAPI version will remove it, so just pass False as prepopulate for now, and remove the option from the dialog and PVS tab page. * CP-16922: Improve UI appearance for a missing Storage Repository. Per the design, if there is no SR show the message None (memory only) instead of an empty cell * CP-16922: Improved sorting behaviour. All columns (except VM name, which always uses it) use the VM tag as a sorting fallback (since they all have duplicates). New default sort which sorts by caching enabled first (yes before no), and then VM tag as a tiebreaker. Support for remembering the current sort on refresh, ie when a value changes. * CP-16922: Refactoring to use a VM extension method to get the PVSProxy (or null if none exists) for a VM. This was used in the PVS Page + commands which are now much simpler. * CP-16922: Move where the SelectionChanged event handler is set when refreshing the PVS table, so that the command buttons are updated correctly. * CP-16922: Improvements to PVS read-caching dialog. Set accept/cancel buttons properly + adjust tab values, so focus acts as expected * CP-16922: Remove the images from the menu for Enable/Disable PVS read-caching, most commands in the menu don't have one * CP-16922: Sorting out the strings for the actions + ParallelAction calls, and moving them into the resx. Also reverting wrong MainWindow.resx changes. * CAR-2184: XAPI changes. I've made the minimal possible changes to PvsPage to ensure it compiles, but it's had a lot of feature work (CP-16922) that supersedes this, these small changes should keep that merge painless. * CAR-2184 XAPI: Reverse the unrelated changes, only keep the PVS ones. * CP-16922: Merge latest XAPI changes, notably changes to Pvs_site.create * CP-16922: Change tab show/hide logic for PVS tab, show it even if there are no PVS sites. * CP-16922: Implement GUI for enabling PVS read caching Design changes - remove the storage repo column and replace it with status, which is populated with a friendly version of the pvs_proxy_status enum. Also populate site list using site.name_label Signed-off-by: Callum McIntyre <callumiandavid.mcintyre@citrix.com> * CP-16922: Implement GUI for enabling PVS read caching Adjusting the CanExecute of Enable PVS read-caching command to require at least one PVS site to be created. Since the PVS tab can show up without any sites, we need this because we can't create a proxy without a site. * CP-16922: Code review changes. Tidied up messages, and ensured actions run async properly, and without exiting before their task. * CP-16922: Code review changes. Update dialog resx, wider combobox on dialog, update action descriptions before setting RelatedTask, add friendly message body for PVS error notifications.
2016-09-22 16:08:15 +02:00
}
public bool UsingUpstreamQemu()
{
return (platform != null) &&
platform.ContainsKey("device-model") &&
platform["device-model"] == "qemu-upstream-compat";
}
/// <summary>
/// Whether the VM's boot mode can be changed. A VM's boot mode cannot be changed once the VM has been started.
/// </summary>
public bool CanChangeBootMode()
{
return last_boot_CPU_flags == null || last_boot_CPU_flags.Count == 0;
}
public bool CanAddVtpm(out string cannotReason)
{
cannotReason = null;
if (VTPMs.Count >= VM.MAX_ALLOWED_VTPMS)
{
cannotReason = string.Format(Messages.VTPM_MAX_REACHED, VM.MAX_ALLOWED_VTPMS);
return false;
}
if (power_state != vm_power_state.Halted)
{
cannotReason = Messages.VTPM_POWER_STATE_WRONG_ATTACH;
return false;
}
return true;
}
public bool CanRemoveVtpm(out string cannotReason)
{
cannotReason = null;
if (power_state != vm_power_state.Halted)
{
cannotReason = Messages.VTPM_POWER_STATE_WRONG_REMOVE;
return false;
}
return true;
}
}
public struct VMStartupOptions
{
public long Order, StartDelay;
public VM.HA_Restart_Priority? HaRestartPriority;
public VMStartupOptions(long order, long startDelay)
{
Order = order;
StartDelay = startDelay;
HaRestartPriority = null;
}
public VMStartupOptions(long order, long startDelay, VM.HA_Restart_Priority haRestartPriority)
{
Order = order;
StartDelay = startDelay;
HaRestartPriority = haRestartPriority;
}
}
}