2013-06-24 13:41:48 +02:00
/ * 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.ComponentModel ;
using System.Globalization ;
using System.Text ;
using XenAdmin.Core ;
using XenAPI ;
namespace XenAdmin.Controls.Ballooning
{
public class VMMemoryControlsEdit : VMMemoryControlsBase
{
// Was ballooning on at the time when we entered the dialog?
// (Like other settings on the dialog, it deliberately doesn't reflect later changes: see CA-34476).
protected bool ballooning = true ;
// The maximum value allowed for memory for this VM
private long maxMemAllowed = VM . DEFAULT_MEM_ALLOWED ;
protected bool firstPaint = true ;
public override List < VM > VMs
{
set
{
base . VMs = value ;
ballooning = vm0 . has_ballooning ;
firstPaint = true ;
maxMemAllowed = CalcMaxMemAllowed ( ) ;
}
}
2015-08-20 13:41:39 +02:00
private double _static_min ;
protected double static_min
2013-06-24 13:41:48 +02:00
{
get
{
System . Diagnostics . Trace . Assert ( ballooning | | Program . RunInAutomatedTestMode ) ; // would be better to edit the test XML file to have VMs and templates with ballooning
return _static_min ;
}
set
{
_static_min = value ;
}
}
// These properties should be abstract, but you can't have an abstract class as the
// base class of a control, otherwise the control can't be edited in the Designer.
[Browsable(false)]
2015-08-20 13:41:39 +02:00
public virtual double dynamic_min
2013-06-24 13:41:48 +02:00
{
get { return 0 ; }
}
[Browsable(false)]
2015-08-20 13:41:39 +02:00
public virtual double dynamic_max
2013-06-24 13:41:48 +02:00
{
get { return 0 ; }
}
[Browsable(false)]
2015-08-20 13:41:39 +02:00
public virtual double static_max
2013-06-24 13:41:48 +02:00
{
get { return 0 ; }
}
// A bit hacky, but needed to fix CA-35710. The ballooning dialog has to call this before
// closing, otherwise the settings on the spinners may not have taken yet.
public virtual void UnfocusSpinners ( )
{
SelectNextControl ( Controls [ 0 ] , true , true , true , true ) ;
}
private long CalcMaxMemAllowed ( )
{
long ans = ( vms . Count = = 0 ? VM . DEFAULT_MEM_ALLOWED : vms [ 0 ] . MaxMemAllowed ) ;
foreach ( VM vm in vms )
{
long max = vm . MaxMemAllowed ;
if ( max < ans )
ans = max ;
}
return ans ;
}
2015-08-20 13:41:39 +02:00
protected double maxDynMin = - 1 ; // signal value for no constraint
2013-06-24 13:41:48 +02:00
protected void CalcMaxDynMin ( )
{
maxDynMin = - 1 ;
if ( vm0 . power_state = = vm_power_state . Running | | vm0 . power_state = = vm_power_state . Paused )
{
// Calculate the maximum possible value of dynamic_min, by looking at all the other VMs on this host
// and obeying the constraint sum(dynamic_min) + control domain + virtualisation overhead <= total host memory
Host host = vm0 . Connection . Resolve ( vm0 . resident_on ) ;
if ( host ! = null )
{
Host_metrics host_metrics = host . Connection . Resolve ( host . metrics ) ;
if ( host_metrics ! = null )
{
2015-08-20 13:41:39 +02:00
double sum_dyn_min = host . memory_overhead ;
2013-06-24 13:41:48 +02:00
foreach ( VM vm in host . Connection . ResolveAll ( host . resident_VMs ) )
{
sum_dyn_min + = vm . memory_overhead ;
if ( vm . is_control_domain )
{
VM_metrics vmm = vm . Connection . Resolve ( vm . metrics ) ;
if ( vmm ! = null )
sum_dyn_min + = vmm . memory_actual ;
}
else if ( ! vms . Contains ( vm ) )
sum_dyn_min + = vm . memory_dynamic_min ;
}
maxDynMin = host_metrics . memory_total - sum_dyn_min ;
if ( maxDynMin < 0 )
maxDynMin = 0 ;
maxDynMin / = vms . Count ;
}
}
}
}
// Maximum for dynamic_min spinner: if we have set a maxDynMin, use it,
// except constrained by static_min and dynamic_max.
2015-08-20 13:41:39 +02:00
protected double DynMinSpinnerMax
2013-06-24 13:41:48 +02:00
{
get
{
2015-08-20 13:41:39 +02:00
double maxDM = dynamic_max ;
2013-06-24 13:41:48 +02:00
if ( maxDynMin > = 0 & & maxDynMin < maxDM )
{
maxDM = maxDynMin ;
if ( maxDM < static_min )
maxDM = static_min ;
}
// If we've already managed to exceed that value, don't trash the user's setting
if ( maxDM < dynamic_min )
maxDM = dynamic_min ;
return maxDM ;
}
}
// Minimum for dynamic_min spinner: constrained by both static_min, and a proportion of static_max
2015-08-20 13:41:39 +02:00
protected double DynMinSpinnerMin
2013-06-24 13:41:48 +02:00
{
get
{
2015-08-20 13:41:39 +02:00
double minDM = static_min ;
long limit = ( long ) ( static_max * GetMemoryRatio ( ) ) ;
2013-06-24 13:41:48 +02:00
if ( limit > minDM )
minDM = limit ;
if ( minDM > dynamic_min )
minDM = dynamic_min ;
return minDM ;
}
}
// Static_max also has a corresponding limit: it can't go to more than 1/frac of maxDynMin,
// or there is no legal value of dynamic_min.
2015-08-20 13:41:39 +02:00
protected double StatMaxSpinnerMax
2013-06-24 13:41:48 +02:00
{
get
{
double frac = GetMemoryRatio ( ) ;
2015-08-20 13:41:39 +02:00
double maxSM = maxDynMin > = 0 & & MemorySpinnerMax * frac > ( long ) maxDynMin
? maxDynMin / frac
2013-06-24 13:41:48 +02:00
: MemorySpinnerMax ;
if ( maxSM < static_max )
maxSM = static_max ;
return maxSM ;
}
}
2015-08-20 13:41:39 +02:00
protected double MemorySpinnerMax
2013-06-24 13:41:48 +02:00
{
get
{
2015-08-20 13:41:39 +02:00
double server = vm0 . memory_static_max ;
2013-06-24 13:41:48 +02:00
return ( server > maxMemAllowed ? server : maxMemAllowed ) ;
}
}
2015-08-20 13:41:39 +02:00
protected double CalcIncrement ( string units )
2013-06-24 13:41:48 +02:00
{
2015-08-20 13:41:39 +02:00
if ( units = = "MB" )
{
// Calculate a suitable increment for dynamic_min and dynamic_max, if static_max small
int i ;
for ( i = 1 ; i < static_max / Util . BINARY_MEGA / 8 & & i < 128 ; i * = 2 )
;
return i * Util . BINARY_MEGA ;
}
else
{
return ( 0.1 * Util . BINARY_GIGA ) ;
}
2013-06-24 13:41:48 +02:00
}
protected double GetMemoryRatio ( )
{
return GetMemoryRatio ( vms . ToArray ( ) ) ;
}
/// <summary>
/// Get the most constrained memory ratio for a set of VMs.
/// It is assumed they are all on the same pool.
/// </summary>
public static double GetMemoryRatio ( params VM [ ] vms )
{
bool hvm = false ;
bool pv = false ;
foreach ( VM vm in vms )
{
if ( vm . IsHVM )
hvm = true ;
else
pv = true ;
}
Pool pool = Helpers . GetPoolOfOne ( vms [ 0 ] . Connection ) ;
Dictionary < string , string > poolOtherConfig = Helpers . GetOtherConfig ( pool ) ;
if ( ! pv ) // only HVM
return GetMemoryRatioValue ( poolOtherConfig , true ) ;
else if ( ! hvm ) // only PV
return GetMemoryRatioValue ( poolOtherConfig , false ) ;
else // both types of VM: return the more constraining ratio
{
double hvmRatio = GetMemoryRatioValue ( poolOtherConfig , true ) ;
double pvRatio = GetMemoryRatioValue ( poolOtherConfig , false ) ;
return ( hvmRatio > = pvRatio ? hvmRatio : pvRatio ) ;
}
}
private static double GetMemoryRatioValue ( Dictionary < string , string > poolOtherConfig , bool hvm )
{
string key = ( hvm ? "memory-ratio-hvm" : "memory-ratio-pv" ) ;
string value ;
double frac = - 1.0 ;
if ( poolOtherConfig . TryGetValue ( key , out value ) )
{
try { frac = Convert . ToDouble ( value , CultureInfo . InvariantCulture ) ; }
catch ( FormatException ) { }
catch ( OverflowException ) { }
}
if ( frac < 0.0 | | frac > 1.0 )
frac = 0.25 ; // default value for both HVM and PV if ratio absent or nonsensical
return frac ;
}
}
}