2017-01-16 20:59:50 +01:00
/ * Copyright ( c ) Citrix Systems , Inc .
2013-06-24 13:41:48 +02:00
* All rights reserved .
*
* Redistribution and use in source and binary forms ,
* with or without modification , are permitted provided
* that the following conditions are met :
*
* * Redistributions of source code must retain the above
* copyright notice , this list of conditions and the
* following disclaimer .
* * Redistributions in binary form must reproduce the above
* copyright notice , this list of conditions and the
* following disclaimer in the documentation and / or other
* materials provided with the distribution .
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES ,
* INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED . IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT , INCIDENTAL ,
* SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING ,
* BUT NOT LIMITED TO , PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR BUSINESS
* INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY ,
* WHETHER IN CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING
* NEGLIGENCE OR OTHERWISE ) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE .
* /
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.Linq ;
using System.Timers ;
using System.Xml ;
using Citrix.XenCenter ;
using XenAdmin ;
using XenAdmin.Core ;
2014-12-05 11:11:17 +01:00
using XenAdmin.Model ;
2013-06-24 13:41:48 +02:00
using XenAdmin.Network ;
2016-01-07 13:55:25 +01:00
using System.Net ;
using System.Text.RegularExpressions ;
2013-06-24 13:41:48 +02:00
namespace XenAPI
{
public enum SnapshotsView
{
ListView , TreeView , None
}
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 ;
2014-09-26 20:01:35 +02:00
private const int DEFAULT_NUM_VBDS_ALLOWED = 16 ;
2015-09-25 18:05:02 +02:00
public const long DEFAULT_MEM_ALLOWED = 1 * Util . BINARY_TERA ;
2014-06-05 14:00:14 +02:00
public const int DEFAULT_CORES_PER_SOCKET = 1 ;
2016-07-04 14:21:52 +02:00
public const long MAX_SOCKETS = 16 ; // current hard limit in Xen: CA-198276
2016-02-12 18:01:05 +01:00
2013-06-24 13:41:48 +02:00
private SnapshotsView _snapshotView = SnapshotsView . None ;
private XmlDocument xdRecommendations = null ;
public int MaxVCPUsAllowed
{
get
{
XmlDocument xd = GetRecommendations ( ) ;
if ( xd = = null )
return DEFAULT_NUM_VCPUS_ALLOWED ;
XmlNode xn = xd . SelectSingleNode ( @"restrictions/restriction[@field='vcpus-max']" ) ;
try
{
return Convert . ToInt32 ( xn . Attributes [ "max" ] . Value ) ;
}
catch
{
return DEFAULT_NUM_VCPUS_ALLOWED ;
}
}
}
public bool IsRunning
{
get
{
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
{
get
{
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 long TotalVMSize
{
get
{
long size = 0 ;
foreach ( VBD vbd in Connection . ResolveAll < VBD > ( VBDs ) )
{
if ( vbd . type = = vbd_type . CD )
continue ;
VDI vdi = Connection . Resolve < VDI > ( vbd . VDI ) ;
if ( vdi = = null )
continue ;
size + = vdi . physical_utilisation ;
}
return size ;
}
}
public VBD FindVMCDROM ( )
{
if ( Connection = = null )
return null ;
List < VBD > vbds = Connection . ResolveAll ( VBDs ) . FindAll ( delegate ( VBD vbd ) { return 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
{
get
{
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
{
get
{
XmlDocument xd = GetRecommendations ( ) ;
if ( xd = = null )
return DEFAULT_MEM_ALLOWED ;
XmlNode xn = xd . SelectSingleNode ( @"restrictions/restriction[@field='memory-static-max']" ) ;
try
{
return Convert . ToInt64 ( xn . Attributes [ "max" ] . Value ) ;
}
catch
{
return DEFAULT_MEM_ALLOWED ;
}
}
}
public int MaxVIFsAllowed
{
get
{
XmlDocument xd = GetRecommendations ( ) ;
if ( xd = = null )
return DEFAULT_NUM_VIFS_ALLOWED ;
XmlNode xn = xd . SelectSingleNode ( @"restrictions/restriction[@property='number-of-vifs']" ) ;
try
{
return Convert . ToInt32 ( xn . Attributes [ "max" ] . Value ) ;
}
catch
{
return DEFAULT_NUM_VIFS_ALLOWED ;
}
}
}
public int MaxVBDsAllowed
{
get
{
XmlDocument xd = GetRecommendations ( ) ;
if ( xd = = null )
return DEFAULT_NUM_VBDS_ALLOWED ;
XmlNode xn = xd . SelectSingleNode ( @"restrictions/restriction[@property='number-of-vbds']" ) ;
try
{
return Convert . ToInt32 ( xn . Attributes [ "max" ] . Value ) ;
}
catch
{
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 BootOrder
{
get
{
if ( this . HVM_boot_params . ContainsKey ( "order" ) )
return this . HVM_boot_params [ "order" ] . ToUpper ( ) ;
return "CD" ;
}
set
{
if ( value = = BootOrder )
return ;
Dictionary < string , string > new_HVM_boot_params =
HVM_boot_params = = null ?
new Dictionary < string , string > ( ) :
new Dictionary < string , string > ( HVM_boot_params ) ;
new_HVM_boot_params [ "order" ] = value . ToLower ( ) ;
HVM_boot_params = new_HVM_boot_params ;
}
}
public long Memory
{
set
{
memory_dynamic_min = value ;
memory_dynamic_max = value ;
memory_static_max = value ;
}
}
public int VCPUWeight
{
get
{
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 ;
}
set
{
if ( value ! = VCPUWeight )
{
Dictionary < string , string > new_VCPUs_params =
VCPUs_params = = null ?
new Dictionary < string , string > ( ) :
new Dictionary < string , string > ( VCPUs_params ) ;
new_VCPUs_params [ "weight" ] = value . ToString ( ) ;
VCPUs_params = new_VCPUs_params ;
}
}
}
public bool DefaultTemplate
{
get { return Get ( other_config , "default_template" ) ! = null ; }
}
public bool InternalTemplate
{
get { return Get ( other_config , "xensource_internal" ) ! = null ; }
}
public string InstallRepository
{
get { return Get ( other_config , "install-repository" ) ; }
set { if ( InstallRepository ! = value ) { set_other_config ( "install-repository" , value ) ; } }
}
public string InstallDistro
{
get { return Get ( other_config , "install-distro" ) ; }
}
public string InstallMethods
{
get { return Get ( other_config , "install-methods" ) ; }
}
public bool IsHVM
{
get { return HVM_boot_policy ! = "" ; }
}
2016-04-04 11:15:57 +02:00
public bool HasStaticIP
{
get
{
var metrics = Connection . Resolve ( this . guest_metrics ) ;
if ( metrics = = null )
return false ;
return 0 ! = IntKey ( metrics . other , "feature-static-ip-setting" , 0 ) ;
}
}
2014-08-22 18:36:41 +02:00
public bool HasRDP
{
get
{
var metrics = Connection . Resolve ( this . guest_metrics ) ;
if ( metrics = = null )
return false ;
return 0 ! = IntKey ( metrics . other , "feature-ts" , 0 ) ;
}
}
2014-12-04 09:35:36 +01:00
public bool RDPEnabled
{
get
{
var metrics = Connection . Resolve ( this . guest_metrics ) ;
if ( metrics = = null )
return false ;
return 0 ! = IntKey ( metrics . other , "data-ts" , 0 ) ;
}
}
2015-01-27 07:13:50 +01:00
public bool RDPControlEnabled
{
get
{
var metrics = Connection . Resolve ( this . guest_metrics ) ;
if ( metrics = = null )
return false ;
return 0 ! = IntKey ( metrics . other , "feature-ts2" , 0 ) ;
}
}
2014-08-22 18:36:41 +02:00
/// <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>
2015-01-19 17:49:47 +01:00
public bool CanHaveGpu
2014-08-22 18:36:41 +02:00
{
get
{
if ( ! IsHVM )
return false ;
XmlDocument xd = GetRecommendations ( ) ;
if ( xd = = null )
return true ;
try
{
2014-09-15 19:24:17 +02:00
XmlNode xn = xd . SelectSingleNode ( @"restrictions/restriction[@field='allow-gpu-passthrough']" ) ;
2014-08-22 18:36:41 +02:00
if ( xn = = null )
return true ;
return
Convert . ToInt32 ( xn . Attributes [ "value" ] . Value ) ! = 0 ;
}
catch
{
return true ;
}
}
}
2015-12-17 16:31:41 +01:00
public bool HasVendorDeviceRecommendation
{
get
{
bool result = false ;
XmlDocument xd = GetRecommendations ( ) ;
if ( xd = = null )
return result ;
try
{
XmlNode xn = xd . SelectSingleNode ( @"restrictions/restriction[@field='has-vendor-device']" ) ;
if ( xn = = null | | xn . Attributes = = null )
return result ;
2015-12-17 16:45:54 +01:00
result = bool . Parse ( xn . Attributes [ "value" ] . Value ) ;
2015-12-17 16:31:41 +01:00
}
catch ( Exception ex )
{
log . Error ( "Error parsing has-vendor-device on the template." , ex ) ;
}
return result ;
}
}
2015-02-12 12:47:47 +01:00
/// <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>
2015-01-21 11:04:47 +01:00
public bool CanHaveVGpu
{
2015-02-12 12:47:47 +01:00
get
{
if ( ! IsHVM | | ! CanHaveGpu )
return false ;
XmlDocument xd = GetRecommendations ( ) ;
if ( xd = = null )
return true ;
try
{
XmlNode xn = xd . SelectSingleNode ( @"restrictions/restriction[@field='allow-vgpu']" ) ;
if ( xn = = null | | xn . Attributes = = null )
return true ;
return
Convert . ToInt32 ( xn . Attributes [ "value" ] . Value ) ! = 0 ;
}
catch
{
return true ;
}
}
2015-01-21 11:04:47 +01:00
}
2013-06-24 13:41:48 +02:00
void set_other_config ( string key , string value )
{
Dictionary < string , string > new_other_config =
other_config = = null ?
new Dictionary < string , string > ( ) :
new Dictionary < string , string > ( other_config ) ;
new_other_config [ key ] = value ;
other_config = new_other_config ;
}
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.
2013-06-24 13:41:48 +02:00
public bool AutoPowerOn
{
get
{
return BoolKey ( other_config , "auto_poweron" ) ;
}
set
{
if ( value ! = AutoPowerOn )
set_other_config ( "auto_poweron" , value . ToString ( ) . ToLower ( ) ) ;
}
}
public bool IgnoreExcessiveVcpus
{
get
{
return BoolKey ( other_config , "ignore_excessive_vcpus" ) ;
}
set
{
if ( value ! = IgnoreExcessiveVcpus )
set_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 BodgeStartupTime
{
get
{
return startuptime ;
}
set
{
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]
2015-09-09 14:56:11 +02:00
public enum VirtualisationStatus
{
UNKNOWN = 1 ,
2015-09-16 19:22:16 +02:00
PV_DRIVERS_OUT_OF_DATE = 2 ,
IO_DRIVERS_INSTALLED = 4 ,
MANAGEMENT_INSTALLED = 8 ,
2015-09-09 14:56:11 +02:00
} ;
2013-06-24 13:41:48 +02:00
public VirtualisationStatus virtualisation_status
{
get
{
return GetVirtualisationStatus ;
}
}
public string VirtualisationVersion
{
get
{
if ( Connection = = null )
return "0.0" ;
VM_guest_metrics metrics = Connection . Resolve < VM_guest_metrics > ( guest_metrics ) ;
if ( metrics = = null | | ! metrics . PV_drivers_version . ContainsKey ( "major" ) | | ! metrics . PV_drivers_version . ContainsKey ( "minor" ) )
return "0.0" ;
return string . Format ( "{0}.{1}" , metrics . PV_drivers_version [ "major" ] , metrics . PV_drivers_version [ "minor" ] ) ;
}
}
2015-09-21 17:59:43 +02:00
public string GetVirtualisationWarningMessages ( )
{
VirtualisationStatus status = GetVirtualisationStatus ;
if ( virtualisation_status . HasFlag ( VirtualisationStatus . IO_DRIVERS_INSTALLED ) & & virtualisation_status . HasFlag ( VirtualisationStatus . MANAGEMENT_INSTALLED )
| | virtualisation_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 ( virtualisation_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 , String . Format ( "{0}.{1}" ,
guestMetrics . PV_drivers_version [ "major" ] ,
guestMetrics . PV_drivers_version [ "minor" ] ) ) ;
}
else
return Messages . PV_DRIVERS_OUT_OF_DATE_UNKNOWN_VERSION ;
}
return HasNewVirtualisationStates ? Messages . VIRTUALIZATION_STATE_VM_MANAGEMENT_AGENT_NOT_INSTALLED : Messages . PV_DRIVERS_NOT_INSTALLED ;
}
2015-09-16 19:22:16 +02:00
private VirtualisationStatus GetVirtualisationStatusOldVM
2013-06-24 13:41:48 +02:00
{
get
{
2015-09-16 19:22:16 +02:00
Debug . Assert ( ! HasNewVirtualisationStates ) ;
2013-06-24 13:41:48 +02:00
VM_guest_metrics vm_guest_metrics = Connection . Resolve ( guest_metrics ) ;
if ( ( DateTime . UtcNow - BodgeStartupTime ) . TotalMinutes < 2 )
{
// check to see if the metrics object has appeared, if so cancel the timer, no need to notify the property changed as this should be picked up on vm_guest_metrics being created.
if ( vm_guest_metrics ! = null & & vm_guest_metrics . PV_drivers_installed )
{
if ( vm_guest_metrics . PV_drivers_up_to_date )
2015-09-16 19:22:16 +02:00
return VirtualisationStatus . IO_DRIVERS_INSTALLED | VirtualisationStatus . MANAGEMENT_INSTALLED ;
2013-06-24 13:41:48 +02:00
else
2015-09-16 19:22:16 +02:00
return VirtualisationStatus . PV_DRIVERS_OUT_OF_DATE ;
2013-06-24 13:41:48 +02:00
}
return VirtualisationStatus . UNKNOWN ;
}
if ( vm_guest_metrics = = null | | ! vm_guest_metrics . PV_drivers_installed )
{
2015-09-18 16:48:59 +02:00
return 0 ;
2013-06-24 13:41:48 +02:00
}
else if ( ! vm_guest_metrics . PV_drivers_up_to_date )
{
2015-09-16 19:22:16 +02:00
return VirtualisationStatus . PV_DRIVERS_OUT_OF_DATE ;
2013-06-24 13:41:48 +02:00
}
else
{
2015-09-16 19:22:16 +02:00
return VirtualisationStatus . IO_DRIVERS_INSTALLED | VirtualisationStatus . MANAGEMENT_INSTALLED ;
2013-06-24 13:41:48 +02:00
}
}
}
2015-09-16 19:22:16 +02:00
private VirtualisationStatus GetVirtualisationStatusNewVM
2015-09-09 14:56:11 +02:00
{
2015-09-16 19:22:16 +02:00
get
{
Debug . Assert ( HasNewVirtualisationStates ) ;
2016-04-04 17:01:07 +02:00
var flags = HasStaticIP
2016-04-04 11:15:57 +02:00
? VirtualisationStatus . MANAGEMENT_INSTALLED
: 0 ;
2015-09-09 14:56:11 +02:00
var vm_guest_metrics = Connection . Resolve ( guest_metrics ) ;
2016-04-08 18:10:08 +02:00
if ( vm_guest_metrics ! = null & & vm_guest_metrics . PV_drivers_detected )
2015-09-09 14:56:11 +02:00
flags | = VirtualisationStatus . IO_DRIVERS_INSTALLED ;
2015-09-16 19:22:16 +02:00
if ( ( DateTime . UtcNow - BodgeStartupTime ) . TotalMinutes < 2 )
{
if ( flags . HasFlag ( VM . VirtualisationStatus . IO_DRIVERS_INSTALLED ) )
return flags ;
return VirtualisationStatus . UNKNOWN ;
}
return flags ;
2015-09-09 14:56:11 +02:00
}
2015-09-16 19:22:16 +02:00
}
2015-09-09 14:56:11 +02:00
2015-09-16 19:22:16 +02:00
/// <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
{
get
{
2015-09-16 19:33:12 +02:00
if ( Connection = = null )
return VirtualisationStatus . UNKNOWN ;
VM_metrics vm_metrics = Connection . Resolve ( metrics ) ;
if ( vm_metrics = = null | | power_state ! = vm_power_state . Running )
{
return VirtualisationStatus . UNKNOWN ;
}
2015-09-16 19:22:16 +02:00
return HasNewVirtualisationStates ? GetVirtualisationStatusNewVM : GetVirtualisationStatusOldVM ;
}
2015-09-09 14:56:11 +02:00
}
/// <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>
2015-09-14 17:21:44 +02:00
public bool HasNewVirtualisationStates
2015-09-09 14:56:11 +02:00
{
get
{
return IsWindows & & XenAdmin . Core . Helpers . DundeeOrGreater ( Connection ) ;
}
}
2013-06-24 13:41:48 +02:00
/// <summary>
/// Does this VM support ballooning? I.e., are tools installed, on a ballonable OS?
/// Doesn't check for Midnight Ride or licensing constraints.
/// </summary>
public bool has_ballooning
{
get
{
if ( Connection = = null )
return false ;
// For templates see comments in CA-34258/CA-34260: we cannot tell whether tools
// are installed so we offer ballooning if and only if the dynamic min != static_max.
if ( is_a_template )
return ( memory_dynamic_min ! = memory_static_max ) ;
VM_guest_metrics metrics = Connection . Resolve < VM_guest_metrics > ( guest_metrics ) ;
if ( metrics = = null )
return false ;
Dictionary < string , string > other_key = metrics . other ;
return ( other_key ! = null & & other_key . ContainsKey ( "feature-balloon" ) ) ;
}
}
/// <summary>
/// Whether to show advanced ballooning UI (i.e., separate setting of dynamic_max and static_max)
/// </summary>
public bool advanced_ballooning
{
get
{
return ( memory_dynamic_max ! = memory_static_max & & has_ballooning ) ;
}
}
/// <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
{
get
{
return BoolKey ( other_config , HIDE_FROM_XENCENTER ) ;
}
}
public bool HasNoDisksAndNoLocalCD
{
get
{
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
{
get
{
return other_config ! = null & & other_config . ContainsKey ( P2V_SOURCE_MACHINE ) & & other_config . ContainsKey ( P2V_IMPORT_DATE ) ;
}
}
/// <summary>
/// Sort in the following order:
/// 1) User Templates
/// 2) Windows VMs
/// 3) Other VMs (e.g. Linux)
/// 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 ,
Centos = 3 ,
2014-12-01 16:09:03 +01:00
CoreOS = 4 ,
Debian = 5 ,
2016-10-21 19:46:38 +02:00
NeoKylin = 6 ,
Oracle = 7 ,
RedHat = 8 ,
SciLinux = 9 ,
Suse = 10 ,
Ubuntu = 11 ,
Citrix = 12 ,
Solaris = 13 ,
Misc = 14 ,
Snapshot = 15 ,
SnapshotFromVmpp = 16 ,
Count = 17 //bump this if values are added
2013-06-24 13:41:48 +02:00
}
public VmTemplateType TemplateType
{
get
{
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 ( "centos" ) )
return VmTemplateType . Centos ;
if ( os . Contains ( "red hat" ) )
return VmTemplateType . RedHat ;
if ( os . Contains ( "oracle" ) )
return VmTemplateType . Oracle ;
if ( os . Contains ( "suse" ) )
return VmTemplateType . Suse ;
2015-03-27 14:34:59 +01:00
if ( os . Contains ( "scientific" ) )
return VmTemplateType . SciLinux ;
2013-06-24 13:41:48 +02:00
if ( os . Contains ( "windows" ) )
return VmTemplateType . Windows ;
if ( os . Contains ( "ubuntu" ) )
return VmTemplateType . Ubuntu ;
2016-10-21 19:46:38 +02:00
if ( os . Contains ( "kylin" ) )
return VmTemplateType . NeoKylin ;
2013-06-24 13:41:48 +02:00
if ( os . Contains ( "solaris" ) )
return VmTemplateType . Solaris ;
2014-12-01 16:09:03 +01:00
if ( os . Contains ( "coreos" ) )
return VmTemplateType . CoreOS ;
2013-06-24 13:41:48 +02:00
return VmTemplateType . Misc ;
}
}
public VmDescriptionType DescriptionType
{
get
{
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
{
get
{
// Don't i18n this
if ( IsP2V & & name_description . StartsWith ( "VM imported from physical machine" ) )
return "" ;
if ( DescriptionType = = VmDescriptionType . ReadOnly )
return PropertyManager . 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
{
get
{
return other_config ! = null & & other_config . ContainsKey ( P2V_SOURCE_MACHINE ) ? other_config [ P2V_SOURCE_MACHINE ] : "" ;
}
}
public DateTime P2V_ImportDate
{
get
{
if ( other_config = = null | | ! other_config . ContainsKey ( P2V_IMPORT_DATE ) )
return DateTime . MinValue ;
string importDate = other_config [ P2V_IMPORT_DATE ] ;
return TimeUtil . ParseISO8601DateTime ( importDate ) ;
}
}
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
2015-01-16 12:09:03 +01:00
if ( os_name . StartsWith ( "Microsoft<66> " ) )
2013-06-24 13:41:48 +02:00
{
2015-01-16 12:09:03 +01:00
if ( os_name ! = "Microsoft<66> " )
2013-06-24 13:41:48 +02:00
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
{
get
{
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
{
get
{
if ( other_config . ContainsKey ( "last_shutdown_time" ) )
{
return TimeUtil . ParseISO8601DateTime ( other_config [ "last_shutdown_time" ] ) ;
}
else
{
return 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
{
get
{
return StringToPriority ( this . ha_restart_priority ) ;
}
set
{
this . ha_restart_priority = PriorityToString ( value ) ;
}
}
2015-01-14 17:20:36 +01:00
public override string NameWithLocation
{
get
{
if ( this . Connection ! = null )
{
if ( this . is_a_real_vm )
{
return base . NameWithLocation ;
}
else if ( this . is_a_snapshot )
{
var snapshotOf = this . Connection . Resolve ( this . snapshot_of ) ;
2015-01-15 23:22:46 +01:00
if ( snapshotOf = = null )
2015-01-15 14:34:22 +01:00
return base . NameWithLocation ;
2015-01-14 17:20:36 +01:00
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 ) ;
}
}
2015-01-15 14:34:22 +01:00
return base . NameWithLocation ;
2015-01-14 17:20:36 +01:00
}
}
internal override string LocationString
{
get
{
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 ;
}
}
2013-06-24 13:41:48 +02:00
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 ) ;
2013-06-24 13:41:48 +02:00
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 ( )
{
HA_Restart_Priority haRestartPriority = HARestartPriority ;
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 ;
2013-06-24 13:41:48 +02:00
}
public static HA_Restart_Priority HaHighestProtectionAvailable ( IXenConnection connection )
{
2015-10-26 17:01:55 +01:00
return HA_Restart_Priority . Restart ;
2013-06-24 13:41:48 +02:00
}
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 is_a_real_vm & & 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
{
get
{
if ( Connection = = null )
return false ;
Pool myPool = Helpers . GetPoolOfOne ( Connection ) ;
if ( myPool = = null )
return false ;
2015-10-26 17:01:55 +01:00
return myPool . ha_enabled & & this . HARestartPriority ! = HA_Restart_Priority . DoNotRestart ;
2013-06-24 13:41:48 +02:00
}
}
/// <summary>
2015-10-26 17:01:55 +01:00
/// Calls set_ha_restart_priority
2013-06-24 13:41:48 +02:00
/// </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 ) ) ;
2013-06-24 13:41:48 +02:00
}
public bool AnyDiskFastClonable
{
get
{
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
{
get
{
if ( Connection = = null )
return false ;
foreach ( VBD vbd in Connection . ResolveAll ( VBDs ) )
{
if ( vbd . type ! = vbd_type . Disk )
continue ;
return true ;
}
return false ;
}
}
2016-07-04 14:21:52 +02:00
/// <summary>
/// Checks whether the VM is the dom0 (the flag is_control_domain may also apply to other control domains)
/// </summary>
public bool IsControlDomainZero
{
get
{
if ( ! is_control_domain )
return false ;
var host = Connection . Resolve ( resident_on ) ;
if ( host = = null )
return false ;
2017-01-09 17:14:03 +01:00
if ( ! Helper . IsNullOrEmptyOpaqueRef ( host . control_domain ) )
2016-07-04 14:21:52 +02:00
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 ;
}
}
2013-06-24 13:41:48 +02:00
public bool not_a_real_vm
{
get { return is_a_snapshot | | is_a_template | | is_control_domain ; }
}
public bool is_a_real_vm
{
get { return ! not_a_real_vm ; }
}
[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 ;
public bool IsBeingCreated
{
get { return _isBeingCreated ; }
set
{
_isBeingCreated = value ;
NotifyPropertyChanged ( "IsBeingCreated" ) ;
}
}
2013-06-24 13:41:48 +02:00
public XmlNode ProvisionXml
{
get
{
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 ;
}
}
set
{
other_config [ "disks" ] = value . OuterXml ;
}
}
public bool InstantTemplate
{
get
{
return BoolKey ( other_config , "instant" ) ;
}
}
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
{
get
{
Host host = Connection . Resolve ( affinity ) ;
if ( host = = null )
return Messages . NONE ;
return host . Name ;
}
}
/// <summary>
/// The virtualisation state of the vm as a friendly string (optimized, out of date, not installed, unknown)
/// </summary>
public string VirtualisationStatusString
{
get
{
2015-09-14 17:21:44 +02:00
if ( virtualisation_status . HasFlag ( VirtualisationStatus . IO_DRIVERS_INSTALLED | VirtualisationStatus . MANAGEMENT_INSTALLED ) )
2015-09-10 15:55:04 +02:00
{
2015-09-14 17:21:44 +02:00
if ( ! HasNewVirtualisationStates )
2015-09-10 15:55:04 +02:00
return string . Format ( Messages . VIRTUALIZATION_OPTIMIZED , VirtualisationVersion ) ;
else
return Messages . VIRTUALIZATION_STATE_VM_IO_DRIVERS_AND_MANAGEMENT_AGENT_INSTALLED ;
}
2015-09-09 14:56:11 +02:00
if ( virtualisation_status . HasFlag ( VirtualisationStatus . PV_DRIVERS_OUT_OF_DATE ) )
2013-06-24 13:41:48 +02:00
return string . Format ( Messages . VIRTUALIZATION_OUT_OF_DATE , VirtualisationVersion ) ;
2015-09-09 14:56:11 +02:00
2015-09-18 16:48:59 +02:00
if ( virtualisation_status = = 0 )
2013-12-13 14:29:00 +01:00
return Messages . PV_DRIVERS_NOT_INSTALLED ;
2015-09-09 14:56:11 +02:00
2015-09-10 15:55:04 +02:00
if ( virtualisation_status . HasFlag ( VM . VirtualisationStatus . IO_DRIVERS_INSTALLED )
& & ! virtualisation_status . HasFlag ( VirtualisationStatus . MANAGEMENT_INSTALLED ) )
2015-09-09 14:56:11 +02:00
return Messages . VIRTUALIZATION_STATE_VM_MANAGEMENT_AGENT_NOT_INSTALLED ;
return Messages . VIRTUALIZATION_UNKNOWN ;
2013-06-24 13:41:48 +02:00
}
}
public SnapshotsView SnapshotView
{
get { return _snapshotView ; }
set { _snapshotView = value ; }
}
public bool HasProvisionXML
{
get
{
return other_config ! = null & & other_config . ContainsKey ( "disks" ) ;
}
}
public bool BiosStringsCopied
{
get
{
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
{
get
{
foreach ( var vbd in this . Connection . ResolveAll < VBD > ( this . VBDs ) )
{
if ( vbd . IsCDROM )
{
return true ;
}
}
return false ;
}
}
public bool HasVGPUs
{
get
{
return ( VGPUs ! = null & & VGPUs . Count > 0 ) ;
}
}
2013-11-14 12:59:42 +01:00
public bool HasGPUPassthrough
{
get
{
if ( VGPUs ! = null & & VGPUs . Count > 0 )
{
var vGPUs = Connection . ResolveAll ( VGPUs ) ;
return vGPUs . Any ( vGPU = > vGPU ! = null & & vGPU . IsPassthrough ) ;
}
return false ;
}
}
2013-06-24 13:41:48 +02:00
public virtual IEnumerable < SR > SRs
{
get
{
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
{
get
{
//on pre-boston servers appliance is null
return appliance ! = null & & appliance . opaque_ref ! = null & &
appliance . opaque_ref . StartsWith ( "OpaqueRef:" ) & &
appliance . opaque_ref ! = "OpaqueRef:NULL" ;
}
}
/// <summary>
/// Try to determine if this VM is WSS - this is a best guess only
/// </summary>
public bool CouldBeWss
{
get
{
const string wssName = "Web Self Service" ;
return name_label . Contains ( wssName ) ;
}
}
2013-07-05 16:43:06 +02:00
public static List < XenRef < SR > > GetDRMissingSRs ( Session session , string vm , Session sessionTo )
2013-06-24 13:41:48 +02:00
{
2014-08-05 18:16:50 +02:00
return Helpers . CreedenceOrGreater ( sessionTo . Connection )
2013-07-05 16:43:06 +02:00
? VM . get_SRs_required_for_recovery ( session , vm , sessionTo . uuid )
2013-07-03 15:54:05 +02:00
: null ;
2013-06-24 13:41:48 +02:00
}
2014-06-05 14:00:14 +02:00
public long CoresPerSocket
{
get
{
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 ;
}
set
{
if ( value ! = CoresPerSocket )
{
Dictionary < string , string > newPlatform =
platform = = null ?
new Dictionary < string , string > ( ) :
new Dictionary < string , string > ( platform ) ;
newPlatform [ "cores-per-socket" ] = value . ToString ( ) ;
platform = newPlatform ;
}
}
}
public long MaxCoresPerSocket
{
get
{
var homeServer = Home ( ) ;
if ( homeServer ! = null )
return homeServer . CoresPerSocket ;
var maxCoresPerSocket = 0 ;
foreach ( var host in this . Connection . Cache . Hosts )
{
if ( host . CoresPerSocket > maxCoresPerSocket )
maxCoresPerSocket = host . CoresPerSocket ;
}
return maxCoresPerSocket ;
}
}
2014-06-06 16:33:22 +02:00
public bool HasValidVCPUConfiguration
{
2016-02-12 18:01:05 +01:00
get { return ValidVCPUConfiguration ( VCPUs_max , CoresPerSocket ) = = "" ; }
2014-06-06 16:33:22 +02:00
}
2016-02-12 18:01:05 +01:00
public static string ValidVCPUConfiguration ( long noOfVCPUs , long coresPerSocket )
2014-06-06 16:33:22 +02:00
{
2016-02-12 18:01:05 +01:00
if ( coresPerSocket > 0 )
{
if ( noOfVCPUs % coresPerSocket ! = 0 )
return Messages . CPU_TOPOLOGY_INVALID_REASON_MULTIPLE ;
2016-02-12 19:52:31 +01:00
if ( noOfVCPUs / coresPerSocket > MAX_SOCKETS )
2016-02-12 18:01:05 +01:00
return Messages . CPU_TOPOLOGY_INVALID_REASON_SOCKETS ;
}
return "" ;
2014-06-06 16:33:22 +02:00
}
2014-07-14 16:17:47 +02:00
public string Topology
{
get
{
var cores = CoresPerSocket ;
2016-02-12 18:01:05 +01:00
var sockets = ValidVCPUConfiguration ( VCPUs_max , cores ) = = "" ? VCPUs_max / cores : 0 ;
2014-07-14 16:17:47 +02:00
return GetTopology ( sockets , cores ) ;
}
}
public static string GetTopology ( long sockets , long cores )
{
if ( sockets = = 0 ) // invalid cores value
2016-02-12 18:01:05 +01:00
return cores = = 1 ? string . Format ( Messages . CPU_TOPOLOGY_STRING_INVALID_VALUE_1 ) : string . Format ( Messages . CPU_TOPOLOGY_STRING_INVALID_VALUE , cores ) ;
2014-07-14 16:17:47 +02:00
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 ) ;
}
2014-12-05 11:11:17 +01:00
2015-02-06 18:46:58 +01:00
public bool CanBeEnlightened
{
get { return other_config . ContainsKey ( "xscontainer-monitor" ) ; }
}
public bool IsEnlightened
{
get
{
var v = Get ( other_config , "xscontainer-monitor" ) ;
return v = = null ? false : v . ToLower ( ) = = "true" ;
}
}
2015-02-13 15:43:13 +01:00
public VDI CloudConfigDrive
{
get
{
var vbds = Connection . ResolveAll ( VBDs ) ;
return vbds . Select ( vbd = > Connection . Resolve ( vbd . VDI ) ) . FirstOrDefault ( vdi = > vdi ! = null & & vdi . IsCloudConfigDrive ) ;
}
}
public bool CanHaveCloudConfigDrive
{
get
{
if ( is_a_template & & TemplateType = = VmTemplateType . CoreOS )
return true ;
//otherwise check if it has a config drive
return CloudConfigDrive ! = null ;
}
}
2015-02-15 03:52:12 +01:00
public VM_Docker_Info DockerInfo
{
get
{
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
{
get
{
string xml = Get ( other_config , "docker_version" ) ;
if ( string . IsNullOrEmpty ( xml ) )
return null ;
VM_Docker_Version info = new VM_Docker_Version ( xml ) ;
return info ;
}
}
2015-02-19 15:09:08 +01:00
public bool ReadCachingEnabled
{
get
{
return ReadCachingVDIs . Count > 0 ;
}
}
/// <summary>
/// Return the list of VDIs that have Read Caching enabled
/// </summary>
public List < VDI > ReadCachingVDIs
{
get
{
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 ) ;
2015-03-13 14:16:19 +01:00
var resident_host = Connection . Resolve ( resident_on ) ;
if ( vdi ! = null & & resident_host ! = null & & vdi . ReadCachingEnabled ( resident_host ) )
2015-02-19 15:09:08 +01:00
readCachingVdis . Add ( vdi ) ;
}
return readCachingVdis ;
}
}
2015-03-16 20:54:55 +01:00
public string ReadCachingDisabledReason
2015-02-19 15:09:08 +01:00
{
2015-03-16 20:54:55 +01:00
// 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
get
2015-02-19 15:09:08 +01:00
{
2015-03-16 20:54:55 +01:00
var ans = VDI . ReadCachingDisabledReasonCode . UNKNOWN ;
2015-02-19 15:09:08 +01:00
foreach ( var vbd in Connection . ResolveAll ( VBDs ) . Where ( vbd = > vbd ! = null & & vbd . currently_attached ) )
{
var vdi = Connection . Resolve ( vbd . VDI ) ;
2015-03-16 20:54:55 +01:00
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 ;
2015-02-19 15:09:08 +01:00
}
}
}
2015-03-17 17:35:12 +01:00
2015-03-31 15:27:04 +02:00
/// <summary>
/// Whether the VM can be moved inside the pool (vdi copy + destroy)
/// </summary>
2015-03-17 17:35:12 +01:00
public bool CanBeMoved
{
get
{
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 . IsOwner ) ! = null ;
}
2015-03-31 15:27:04 +02:00
return false ;
}
}
2015-03-17 17:35:12 +01:00
2015-03-31 15:27:04 +02:00
/// <summary>
/// Whether the VM can be copied inside the pool (vm.copy)
/// </summary>
public bool CanBeCopied
{
get
{
if ( ! is_a_template & & ! Locked & & allowed_operations ! = null & & allowed_operations . Contains ( vm_operations . export ) & & power_state ! = vm_power_state . Suspended )
{
return true ;
}
2015-03-17 17:35:12 +01:00
return false ;
}
}
2015-06-10 16:04:38 +02:00
/// <summary>
/// Returns true if this VM is Windows.
/// </summary>
2015-12-08 15:12:11 +01:00
/// <remarks>
/// To get an acceptable result, this getter is trying to detect some specific cases before falling back to the viridian flag
2016-09-13 13:16:55 +02:00
/// that may not be correct at all times. (Linux distro can be detected if the guest agent is running on a Linux VM.)
///
/// Note that this test is better once the VM has already booted once: before then, we can't tell with complete certainty what
/// OS is inside the VM. In particular, this also catches the "Other Install Media" template, and unbooted VMs made from it.
/// </remarks>
2015-06-10 16:04:38 +02:00
public bool IsWindows
{
get
{
2015-12-08 15:12:11 +01:00
var gm = Connection . Resolve ( this . guest_metrics ) ;
2016-01-07 13:55:25 +01:00
if ( gm ! = null & & gm . IsVmNotWindows )
return false ;
var gmFromLastBootedRecord = GuestMetricsFromLastBootedRecord ;
if ( gmFromLastBootedRecord ! = null & & gmFromLastBootedRecord . IsVmNotWindows )
return false ;
2015-12-08 15:57:24 +01:00
2015-12-08 15:12:11 +01:00
//generic check
2015-06-10 16:04:38 +02:00
return
this . IsHVM & & BoolKey ( this . platform , "viridian" ) ;
}
}
2015-12-17 12:05:22 +01:00
2016-01-07 13:55:25 +01:00
private VM_guest_metrics GuestMetricsFromLastBootedRecord
{
get
{
if ( ! string . IsNullOrEmpty ( this . 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 ( this . last_booted_record ) ;
if ( v . Success )
{
string s = v . Groups [ 1 ] . ToString ( ) ;
return this . Connection . Resolve < VM_guest_metrics > ( new XenRef < VM_guest_metrics > ( s ) ) ;
}
}
return null ;
}
}
2015-12-17 12:05:22 +01:00
public bool WindowsUpdateCapable
{
get
{
return
this . has_vendor_device & & this . IsWindows ;
}
}
2016-02-06 08:18:07 +01:00
/// <summary>
/// Returns the VM IP address for SSH login.
/// </summary>
public string IPAddressForSSH
{
get
{
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 ;
}
}
2016-06-23 16:26:32 +02:00
public bool HciWarnBeforeShutdown
{
get
{
return other_config ! = null & & other_config . ContainsKey ( "hci-warn-before-shutdown" ) ;
2016-09-29 13:11:37 +02:00
}
}
public bool SupportsVcpuHotplug
{
get
{
return ! IsWindows & & Helpers . TampaOrGreater ( Connection ) & & ! Helpers . FeatureForbidden ( Connection , Host . RestrictVcpuHotplug ) ;
2016-06-23 16:26:32 +02:00
}
}
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
{
get
{
2016-10-01 01:23:28 +02:00
return Connection . Cache . PVS_proxies . FirstOrDefault ( p = > p . VM ! = null & & p . 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
}
}
2013-06-24 13:41:48 +02:00
}
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 ;
}
}
2015-02-15 03:52:12 +01:00
2013-06-24 13:41:48 +02:00
}