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 ;
2016-09-29 13:11:37 +02:00
using System.Text ;
2013-06-24 13:41:48 +02:00
using System.Windows.Forms ;
using XenAPI ;
using XenAdmin.Controls ;
using XenAdmin.Controls.Ballooning ;
using XenAdmin.Core ;
namespace XenAdmin.Wizards.NewVMWizard
{
public partial class Page_CpuMem : XenTabPage
{
private VM Template ;
2018-10-05 14:41:15 +02:00
// number of spinners to show
enum MemoryMode
{
JustMemory = 1 ,
MinimumAndMaximum = 2 ,
MinimumMaximumAndStaticMax = 3
}
MemoryMode memoryMode = MemoryMode . JustMemory ;
2013-06-24 13:41:48 +02:00
double memoryRatio = 0.0 ; // the permitted ratio of dynamic_min / static_max
bool initialising = true ;
2016-09-29 13:11:37 +02:00
private bool isVcpuHotplugSupported ;
// Please note that the comboBoxVCPUs control can represent two different VM properties, depending whether the VM supports vCPU hotplug or not:
// When vCPU hotplug is not supported, comboBoxVCPUs represents the initial number of vCPUs (VCPUs_at_startup). In this case we will also set the VM property VCPUs_max to the same value.
// When vCPU hotplug is supported, comboBoxVCPUs represents the maximum number of vCPUs (VCPUs_max). And the initial number of vCPUs is represented in comboBoxInitialVCPUs (which is only visible in this case)
2013-06-24 13:41:48 +02:00
public Page_CpuMem ( )
{
InitializeComponent ( ) ;
}
public override string Text
{
get { return Messages . NEWVMWIZARD_CPUMEMPAGE_NAME ; }
}
public override string PageTitle
{
get { return Messages . NEWVMWIZARD_CPUMEMPAGE_TITLE ; }
}
public override string HelpID
{
get { return "CPU&Memory" ; }
}
2018-03-09 01:31:46 +01:00
protected override void PageLoadedCore ( PageLoadedDirection direction )
2013-06-24 13:41:48 +02:00
{
if ( SelectedTemplate = = Template )
return ;
initialising = true ;
Template = SelectedTemplate ;
2017-09-03 04:33:29 +02:00
if ( Template . has_ballooning ( ) & & ! Helpers . FeatureForbidden ( Template , Host . RestrictDMC ) )
2018-10-05 14:41:15 +02:00
memoryMode = Template . memory_dynamic_max = = Template . memory_static_max ? MemoryMode . MinimumAndMaximum : MemoryMode . MinimumMaximumAndStaticMax ;
2013-06-24 13:41:48 +02:00
else
2018-10-05 14:41:15 +02:00
memoryMode = MemoryMode . JustMemory ;
2013-06-24 13:41:48 +02:00
memoryRatio = VMMemoryControlsEdit . GetMemoryRatio ( Template ) ;
FreeSpinnerLimits ( ) ;
2018-10-05 14:41:15 +02:00
if ( memoryMode = = MemoryMode . JustMemory )
2013-06-24 13:41:48 +02:00
{
2018-08-29 16:01:51 +02:00
spinnerDynMin . Initialize ( Template . memory_static_max , Template . memory_static_max ) ;
labelDynMin . Text = Messages . MEMORY_COLON ;
2013-06-24 13:41:48 +02:00
}
else
{
2018-08-29 16:01:51 +02:00
labelDynMin . Text = Messages . DYNAMIC_MIN_COLON ;
spinnerDynMin . Initialize ( Template . memory_dynamic_min , Template . memory_static_max ) ;
2013-06-24 13:41:48 +02:00
FreeSpinnerLimits ( ) ; // same as CA-33831
2018-08-29 16:01:51 +02:00
spinnerDynMax . Initialize ( Template . memory_dynamic_max , Template . memory_static_max ) ;
2018-10-05 14:41:15 +02:00
if ( memoryMode = = MemoryMode . MinimumMaximumAndStaticMax )
2013-06-24 13:41:48 +02:00
{
FreeSpinnerLimits ( ) ;
2018-08-29 16:01:51 +02:00
spinnerStatMax . Initialize ( Template . memory_static_max , Template . memory_static_max ) ;
2013-06-24 13:41:48 +02:00
}
}
2018-10-05 14:41:15 +02:00
labelDynMaxInfo . Visible = labelDynMax . Visible = spinnerDynMax . Visible = memoryMode = = MemoryMode . MinimumAndMaximum | | memoryMode = = MemoryMode . MinimumMaximumAndStaticMax ;
labelStatMaxInfo . Visible = labelStatMax . Visible = spinnerStatMax . Visible = memoryMode = = MemoryMode . MinimumMaximumAndStaticMax ;
2013-06-24 13:41:48 +02:00
2017-09-03 04:33:29 +02:00
isVcpuHotplugSupported = Template . SupportsVcpuHotplug ( ) ;
2016-09-29 13:11:37 +02:00
_prevVCPUsMax = Template . VCPUs_max ; // we use variable in RefreshCurrentVCPUs for checking if VcpusAtStartup and VcpusMax were equal before VcpusMax changed
label5 . Text = GetRubric ( ) ;
InitialiseVcpuControls ( ) ;
2014-06-05 14:03:06 +02:00
2016-03-10 12:44:48 +01:00
SetSpinnerLimitsAndIncrement ( ) ;
2013-06-24 13:41:48 +02:00
ValuesUpdated ( ) ;
initialising = false ;
}
2016-12-08 15:47:58 +01:00
public override void SelectDefaultControl ( )
{
comboBoxVCPUs . Select ( ) ;
}
2016-09-29 13:11:37 +02:00
private void InitialiseVcpuControls ( )
{
labelVCPUs . Text = isVcpuHotplugSupported
? Messages . VM_CPUMEMPAGE_MAX_VCPUS_LABEL
: Messages . VM_CPUMEMPAGE_VCPUS_LABEL ;
labelInitialVCPUs . Text = Messages . VM_CPUMEMPAGE_INITIAL_VCPUS_LABEL ;
labelInitialVCPUs . Visible = comboBoxInitialVCPUs . Visible = isVcpuHotplugSupported ;
2017-09-05 03:15:38 +02:00
comboBoxTopology . Populate ( Template . VCPUs_at_startup , Template . VCPUs_max , Template . GetCoresPerSocket ( ) , Template . MaxCoresPerSocket ( ) ) ;
2017-09-03 04:33:29 +02:00
PopulateVCPUs ( Template . MaxVCPUsAllowed ( ) , isVcpuHotplugSupported ? Template . VCPUs_max : Template . VCPUs_at_startup ) ;
2016-09-29 13:11:37 +02:00
if ( isVcpuHotplugSupported )
PopulateVCPUsAtStartup ( Template . VCPUs_max , Template . VCPUs_at_startup ) ;
}
private void PopulateVCPUComboBox ( ComboBox comboBox , long min , long max , long currentValue , Predicate < long > isValid )
2016-02-12 18:01:05 +01:00
{
2016-09-29 13:11:37 +02:00
comboBox . BeginUpdate ( ) ;
comboBox . Items . Clear ( ) ;
for ( long i = min ; i < = max ; + + i )
2016-02-12 18:01:05 +01:00
{
2016-09-29 13:11:37 +02:00
if ( i = = currentValue | | isValid ( i ) )
comboBox . Items . Add ( i ) ;
2016-02-12 18:01:05 +01:00
}
2016-09-29 13:11:37 +02:00
if ( currentValue > max )
comboBox . Items . Add ( currentValue ) ;
comboBox . SelectedItem = currentValue ;
comboBox . EndUpdate ( ) ;
}
private void PopulateVCPUs ( long maxVCPUs , long currentVCPUs )
{
PopulateVCPUComboBox ( comboBoxVCPUs , 1 , maxVCPUs , currentVCPUs , i = > comboBoxTopology . IsValidVCPU ( i ) ) ;
}
private void PopulateVCPUsAtStartup ( long max , long currentValue )
{
PopulateVCPUComboBox ( comboBoxInitialVCPUs , 1 , max , currentValue , i = > true ) ;
}
private string GetRubric ( )
{
StringBuilder sb = new StringBuilder ( ) ;
sb . Append ( Messages . NEWVMWIZARD_CPUMEMPAGE_RUBRIC ) ;
// add hotplug text
if ( isVcpuHotplugSupported )
sb . Append ( Messages . VM_CPUMEMPAGE_RUBRIC_HOTPLUG ) ;
return sb . ToString ( ) ;
2016-02-12 18:01:05 +01:00
}
2013-06-24 13:41:48 +02:00
public VM SelectedTemplate { private get ; set ; }
// Return the larger of the template's MaxMemAllowed and the template's static max,
// to avoid crashes in the spinners (CA-40041).
private long MaxMemAllowed
{
get
{
long msm = Template . memory_static_max ;
2017-09-03 04:33:29 +02:00
long mma = Template . MaxMemAllowed ( ) ;
2013-06-24 13:41:48 +02:00
return ( msm > mma ? msm : mma ) ;
}
}
private void FreeSpinnerLimits ( )
{
long maxMemAllowed = MaxMemAllowed ;
spinnerDynMin . SetRange ( 0 , maxMemAllowed ) ;
spinnerDynMax . SetRange ( 0 , maxMemAllowed ) ;
spinnerStatMax . SetRange ( 0 , maxMemAllowed ) ;
}
2016-03-10 12:44:48 +01:00
private void SetSpinnerLimitsAndIncrement ( )
2013-06-24 13:41:48 +02:00
{
2016-03-10 12:44:48 +01:00
spinnerDynMin . Increment = spinnerDynMax . Increment = spinnerStatMax . Increment = VMMemoryControlsEdit . CalcIncrement ( Template . memory_static_max , spinnerDynMin . Units ) ;
2013-06-24 13:41:48 +02:00
long maxMemAllowed = MaxMemAllowed ;
2015-08-20 13:41:39 +02:00
double min = Template . memory_static_min ;
2018-10-05 14:41:15 +02:00
if ( memoryMode = = MemoryMode . JustMemory )
2013-06-24 13:41:48 +02:00
{
spinnerDynMin . SetRange ( min , maxMemAllowed ) ;
2018-09-24 12:32:00 +02:00
ShowMemoryMinMaxInformation ( labelDynMinInfo , min , maxMemAllowed ) ;
2013-06-24 13:41:48 +02:00
return ;
}
2015-08-20 13:41:39 +02:00
long min2 = ( long ) ( SelectedMemoryStaticMax * memoryRatio ) ;
2013-06-24 13:41:48 +02:00
if ( min < min2 )
min = min2 ;
2015-08-20 13:41:39 +02:00
double max = SelectedMemoryDynamicMax ;
2013-06-24 13:41:48 +02:00
if ( max < min )
max = min ;
spinnerDynMin . SetRange ( min , max ) ;
2018-09-24 12:32:00 +02:00
ShowMemoryMinMaxInformation ( labelDynMinInfo , min , max ) ;
2013-06-24 13:41:48 +02:00
spinnerDynMax . SetRange ( SelectedMemoryDynamicMin ,
2018-10-05 14:41:15 +02:00
memoryMode = = MemoryMode . MinimumAndMaximum ? maxMemAllowed : SelectedMemoryStaticMax ) ;
2018-09-24 12:32:00 +02:00
ShowMemoryMinMaxInformation ( labelDynMaxInfo , SelectedMemoryDynamicMin ,
2018-10-05 14:41:15 +02:00
memoryMode = = MemoryMode . MinimumAndMaximum ? maxMemAllowed : SelectedMemoryStaticMax ) ;
2018-09-24 12:32:00 +02:00
2013-06-24 13:41:48 +02:00
spinnerStatMax . SetRange ( SelectedMemoryDynamicMax , maxMemAllowed ) ;
2018-09-24 12:32:00 +02:00
ShowMemoryMinMaxInformation ( labelStatMaxInfo , SelectedMemoryDynamicMax , maxMemAllowed ) ;
2013-06-24 13:41:48 +02:00
}
public void DisableMemoryControls ( )
{
spinnerDynMin . Enabled = false ;
spinnerDynMax . Enabled = false ;
spinnerStatMax . Enabled = false ;
}
2015-08-20 13:41:39 +02:00
public double SelectedMemoryDynamicMin
2013-06-24 13:41:48 +02:00
{
get
{
return spinnerDynMin . Value ;
}
}
2015-08-20 13:41:39 +02:00
public double SelectedMemoryDynamicMax
2013-06-24 13:41:48 +02:00
{
get
{
2018-10-05 14:41:15 +02:00
return memoryMode = = MemoryMode . JustMemory ? spinnerDynMin . Value : spinnerDynMax . Value ;
2013-06-24 13:41:48 +02:00
}
}
2015-08-20 13:41:39 +02:00
public double SelectedMemoryStaticMax
2013-06-24 13:41:48 +02:00
{
get
{
return
2018-10-05 14:41:15 +02:00
memoryMode = = MemoryMode . JustMemory ? spinnerDynMin . Value :
memoryMode = = MemoryMode . MinimumAndMaximum ? spinnerDynMax . Value :
2013-06-24 13:41:48 +02:00
spinnerStatMax . Value ;
}
}
2016-09-29 13:11:37 +02:00
public long SelectedVcpusMax
2013-06-24 13:41:48 +02:00
{
get
{
2016-02-12 18:01:05 +01:00
return ( long ) comboBoxVCPUs . SelectedItem ;
2013-06-24 13:41:48 +02:00
}
}
2016-09-29 13:11:37 +02:00
public long SelectedVcpusAtStartup
{
get
{
return isVcpuHotplugSupported ? ( long ) comboBoxInitialVCPUs . SelectedItem : ( long ) comboBoxVCPUs . SelectedItem ;
}
}
2014-06-05 14:03:06 +02:00
public long SelectedCoresPerSocket
{
get
{
return comboBoxTopology . CoresPerSocket ;
}
}
2013-06-24 13:41:48 +02:00
public override List < KeyValuePair < string , string > > PageSummary
{
get
{
List < KeyValuePair < string , string > > sum = new List < KeyValuePair < string , string > > ( ) ;
2016-09-29 13:11:37 +02:00
if ( isVcpuHotplugSupported )
{
sum . Add ( new KeyValuePair < string , string > ( Messages . NEWVMWIZARD_CPUMEMPAGE_MAX_VCPUS , SelectedVcpusMax . ToString ( ) ) ) ;
sum . Add ( new KeyValuePair < string , string > ( Messages . NEWVMWIZARD_CPUMEMPAGE_INITIAL_VCPUS , SelectedVcpusAtStartup . ToString ( ) ) ) ;
}
else
{
sum . Add ( new KeyValuePair < string , string > ( Messages . NEWVMWIZARD_CPUMEMPAGE_VCPUS , SelectedVcpusAtStartup . ToString ( ) ) ) ;
}
2014-06-05 14:03:06 +02:00
sum . Add ( new KeyValuePair < string , string > ( Messages . NEWVMWIZARD_CPUMEMPAGE_TOPOLOGY , comboBoxTopology . Text ) ) ;
2018-10-05 14:41:15 +02:00
if ( memoryMode = = MemoryMode . JustMemory )
2015-09-10 15:36:46 +02:00
sum . Add ( new KeyValuePair < string , string > ( Messages . MEMORY , Util . MemorySizeStringSuitableUnits ( SelectedMemoryStaticMax , false ) ) ) ;
2013-06-24 13:41:48 +02:00
else
{
2015-09-10 15:36:46 +02:00
sum . Add ( new KeyValuePair < string , string > ( Messages . DYNAMIC_MIN , Util . MemorySizeStringSuitableUnits ( SelectedMemoryDynamicMin , false ) ) ) ;
sum . Add ( new KeyValuePair < string , string > ( Messages . DYNAMIC_MAX , Util . MemorySizeStringSuitableUnits ( SelectedMemoryDynamicMax , false ) ) ) ;
2018-10-05 14:41:15 +02:00
if ( memoryMode = = MemoryMode . MinimumMaximumAndStaticMax )
2015-09-10 15:36:46 +02:00
sum . Add ( new KeyValuePair < string , string > ( Messages . STATIC_MAX , Util . MemorySizeStringSuitableUnits ( SelectedMemoryStaticMax , false ) ) ) ;
2013-06-24 13:41:48 +02:00
}
return sum ;
}
}
private void ValuesUpdated ( )
{
CheckForError ( ) ;
OnPageUpdated ( ) ;
}
private void CheckForError ( )
{
long max_mem_total = 0 ;
long max_mem_free = 0 ;
long max_vcpus = 0 ;
Host max_mem_total_host = null ;
Host max_mem_free_host = null ;
Host max_vcpus_host = null ;
foreach ( Host host in Connection . Cache . Hosts )
{
long host_cpus = 0 ;
foreach ( Host_cpu cpu in Connection . Cache . Host_cpus )
{
if ( cpu . host . opaque_ref . Equals ( host . opaque_ref ) )
host_cpus + + ;
}
Host_metrics metrics = Connection . Resolve ( host . metrics ) ;
if ( host_cpus > max_vcpus )
{
max_vcpus = host_cpus ;
max_vcpus_host = host ;
}
if ( metrics ! = null & & metrics . memory_total > max_mem_total )
{
max_mem_total = metrics . memory_total ;
max_mem_total_host = host ;
}
// The available memory of a server is taken to be the current memory_free,
// plus however much we can squeeze down the existing VMs. This assumes
// that the overhead won't increase when we create the new VM, so it
// has false negatives.
2017-09-03 04:33:29 +02:00
long memory_free = host . memory_available_calc ( ) ;
2013-06-24 13:41:48 +02:00
if ( metrics ! = null & & memory_free > max_mem_free )
{
max_mem_free = memory_free ;
max_mem_free_host = host ;
}
}
if ( max_mem_total_host ! = null & & SelectedMemoryDynamicMin > max_mem_total )
{
ErrorPanel . Visible = true ;
2016-11-03 16:30:56 +01:00
ErrorLabel . Text = string . Format ( Messages . NEWVMWIZARD_CPUMEMPAGE_MEMORYWARN1 , Helpers . GetName ( max_mem_total_host ) . Ellipsise ( 50 ) , Util . MemorySizeStringSuitableUnits ( max_mem_total , false ) ) ;
2013-06-24 13:41:48 +02:00
}
else if ( max_mem_free_host ! = null & & SelectedMemoryDynamicMin > max_mem_free )
{
ErrorPanel . Visible = true ;
2016-11-03 16:30:56 +01:00
ErrorLabel . Text = string . Format ( Messages . NEWVMWIZARD_CPUMEMPAGE_MEMORYWARN2 , Helpers . GetName ( max_mem_free_host ) . Ellipsise ( 50 ) , Util . MemorySizeStringSuitableUnits ( max_mem_free , false ) ) ;
2013-06-24 13:41:48 +02:00
}
2016-09-29 13:11:37 +02:00
else if ( max_vcpus_host ! = null & & SelectedVcpusMax > max_vcpus )
2013-06-24 13:41:48 +02:00
{
ErrorPanel . Visible = true ;
2016-11-03 16:30:56 +01:00
ErrorLabel . Text = string . Format ( Messages . NEWVMWIZARD_CPUMEMPAGE_VCPUSWARN , Helpers . GetName ( max_vcpus_host ) . Ellipsise ( 50 ) , max_vcpus ) ;
2013-06-24 13:41:48 +02:00
}
else
{
ErrorPanel . Visible = false ;
}
}
2018-09-24 12:32:00 +02:00
private void ShowMemoryMinMaxInformation ( Label label , double min , double max )
{
label . Text = string . Format (
Messages . NEWVMWIZARD_CPUMEMPAGE_MEMORYINFO ,
FormatMemory ( min ) ,
FormatMemory ( max ) ) ;
}
private string FormatMemory ( double numberOfBytes )
{
return Util . MemorySizeStringSuitableUnits ( numberOfBytes , true ) ;
}
2013-06-24 13:41:48 +02:00
private void vCPU_ValueChanged ( object sender , EventArgs e )
{
2016-02-12 18:01:05 +01:00
comboBoxTopology . Update ( ( long ) comboBoxVCPUs . SelectedItem ) ;
2013-06-24 13:41:48 +02:00
ValuesUpdated ( ) ;
2014-06-06 16:33:22 +02:00
ValidateVCPUSettings ( ) ;
2016-09-29 13:11:37 +02:00
RefreshCurrentVCPUs ( ) ;
2013-06-24 13:41:48 +02:00
}
private void memory_ValueChanged ( object sender , EventArgs e )
{
if ( initialising )
return ;
2016-03-10 12:44:48 +01:00
SetSpinnerLimitsAndIncrement ( ) ;
2013-06-24 13:41:48 +02:00
ValuesUpdated ( ) ;
}
2014-06-06 16:33:22 +02:00
private void ValidateVCPUSettings ( )
{
2016-02-12 18:01:05 +01:00
if ( comboBoxVCPUs . SelectedItem ! = null )
labelInvalidVCPUWarning . Text = VM . ValidVCPUConfiguration ( ( long ) comboBoxVCPUs . SelectedItem , comboBoxTopology . CoresPerSocket ) ;
2014-06-06 16:33:22 +02:00
}
2016-09-29 13:11:37 +02:00
private long _prevVCPUsMax ;
private void RefreshCurrentVCPUs ( )
{
// refresh comboBoxInitialVCPUs if it's visible and populated
if ( comboBoxInitialVCPUs . Visible & & comboBoxInitialVCPUs . Items . Count > 0 )
{
// VcpusAtStartup is always <= VcpusMax
// So if VcpusMax is decreased below VcpusAtStartup, then VcpusAtStartup is decreased to that number too
// If VcpusAtStartup and VcpusMax are equal, and VcpusMax is changed, then VcpusAtStartup is changed to match
// But if the numbers are unequal, and VcpusMax is changed but is still higher than VcpusAtStartup, then VcpusAtStartup is unchanged
var newValue = SelectedVcpusAtStartup ;
if ( SelectedVcpusMax < SelectedVcpusAtStartup )
newValue = SelectedVcpusMax ;
else if ( SelectedVcpusAtStartup = = _prevVCPUsMax & & SelectedVcpusMax ! = _prevVCPUsMax )
newValue = SelectedVcpusMax ;
PopulateVCPUsAtStartup ( SelectedVcpusMax , newValue ) ;
_prevVCPUsMax = SelectedVcpusMax ;
}
}
2014-06-06 16:33:22 +02:00
private void comboBoxTopology_SelectedIndexChanged ( object sender , EventArgs e )
{
ValidateVCPUSettings ( ) ;
}
2013-06-24 13:41:48 +02:00
}
}