2023-01-24 15:29:31 +01:00
/ * Copyright ( c ) Cloud Software Group , Inc .
2013-06-24 13:41:48 +02:00
*
* 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.Drawing ;
2023-06-08 12:24:39 +02:00
using System.Linq ;
2013-06-24 13:41:48 +02:00
using System.Windows.Forms ;
using XenAdmin.Actions ;
using XenAdmin.Core ;
using XenAPI ;
namespace XenAdmin.SettingsPanels
{
2023-06-08 12:24:39 +02:00
public partial class CpuMemoryEditPage : UserControl , IEditPage
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
private static readonly log4net . ILog log = log4net . LogManager . GetLogger ( System . Reflection . MethodBase . GetCurrentMethod ( ) ? . DeclaringType ) ;
private VM _vm ;
private bool _validToSave = true ;
private long _origVCpus ;
private long _origVCpusMax ;
private long _origVCpusAtStartup ;
private decimal _origVCpuWeight ;
private decimal _currentVCpuWeight ;
private bool _isVCpuHotplugSupported ;
private int _minVCpus ;
private long _prevVCpusMax ;
2016-09-29 13:11:37 +02:00
// Please note that the comboBoxVCPUs control can represent two different VM properties, depending whether the VM supports vCPU hotplug or not:
// If 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)
// If 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.
// We use the _OrigVCPUs variable to store the original value that populates this combo box (VCPUs_max if hotplug is allowed, otherwise VCPUs_at_startup)
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
private bool HasVCpuChanged = > _origVCpus ! = ( long ) comboBoxVCPUs . SelectedItem ;
2020-04-12 02:27:49 +02:00
2023-06-08 12:24:39 +02:00
private bool HasVCpuWeightChanged = > _origVCpuWeight ! = _currentVCpuWeight ;
2020-04-12 02:27:49 +02:00
2023-06-08 12:24:39 +02:00
private bool HasVCpusAtStartupChanged = >
_isVCpuHotplugSupported & & _origVCpusAtStartup ! = ( long ) comboBoxInitialVCPUs . SelectedItem ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
private bool HasTopologyChanged = > _vm . GetCoresPerSocket ( ) ! = comboBoxTopology . CoresPerSocket ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
private long SelectedVCpusMax = > ( long ) comboBoxVCPUs . SelectedItem ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
private long SelectedVCpusAtStartup = > _isVCpuHotplugSupported
? ( long ) comboBoxInitialVCPUs . SelectedItem
: ( long ) comboBoxVCPUs . SelectedItem ;
2013-06-24 13:41:48 +02:00
2020-06-18 02:20:29 +02:00
public Image Image = > Images . StaticImages . _000_CPU_h32bit_16 ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
public string SubText = > string . Format ( Messages . CPU_SUB , SelectedVCpusAtStartup ) ;
public CpuMemoryEditPage ( )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
InitializeComponent ( ) ;
transparentTrackBar1 . Scroll + = tbPriority_Scroll ;
Text = Messages . CPU ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
private void InitializeVCpuControls ( )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
lblVCPUs . Text = _isVCpuHotplugSupported
? Messages . VM_CPUMEMPAGE_MAX_VCPUS_LABEL
: Messages . VM_CPUMEMPAGE_VCPUS_LABEL ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
labelInitialVCPUs . Text = _vm . power_state = = vm_power_state . Halted
? Messages . VM_CPUMEMPAGE_INITIAL_VCPUS_LABEL
: Messages . VM_CPUMEMPAGE_CURRENT_VCPUS_LABEL ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
labelInitialVCPUs . Visible = comboBoxInitialVCPUs . Visible = _isVCpuHotplugSupported ;
comboBoxInitialVCPUs . Enabled = _isVCpuHotplugSupported & &
( _vm . power_state = = vm_power_state . Halted | |
_vm . power_state = = vm_power_state . Running ) ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
comboBoxVCPUs . Enabled = comboBoxTopology . Enabled = _vm . power_state = = vm_power_state . Halted ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
comboBoxTopology . Populate ( _vm . VCPUs_at_startup , _vm . VCPUs_max , _vm . GetCoresPerSocket ( ) ,
_vm . MaxCoresPerSocket ( ) ) ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
// CA-12941
// We set a sensible maximum based on the template, but if the user sets something higher
// from the CLI then use that as the maximum.
var maxAllowed = _vm . MaxVCPUsAllowed ( ) ;
var maxVCpus = maxAllowed < _origVCpus ? _origVCpus : maxAllowed ;
PopulateVCpus ( maxVCpus , _origVCpus ) ;
if ( _isVCpuHotplugSupported )
PopulateVCpusAtStartup ( _origVCpusMax , _origVCpusAtStartup ) ;
transparentTrackBar1 . Value =
Convert . ToInt32 ( Math . Log ( Convert . ToDouble ( _vm . GetVcpuWeight ( ) ) ) / Math . Log ( 4.0d ) ) ;
panel1 . Enabled = _vm . power_state = = vm_power_state . Halted ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
private void Repopulate ( )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
var vm = _vm ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
_isVCpuHotplugSupported = vm . SupportsVcpuHotplug ( ) ;
_minVCpus = vm . MinVCPUs ( ) ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
label1 . Text = Messages . VM_CPUMEMPAGE_RUBRIC ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
if ( _isVCpuHotplugSupported )
label1 . Text + = Messages . VM_CPUMEMPAGE_RUBRIC_HOTPLUG ;
if ( _vm . power_state ! = vm_power_state . Halted )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
if ( _isVCpuHotplugSupported )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
labelInfo . Text = Messages . VM_CPUMEMPAGE_MAX_VCPUS_READONLY ;
if ( _vm . power_state ! = vm_power_state . Running )
labelInfo . Text + = Messages . VM_CPUMEMPAGE_CURRENT_VCPUS_READONLY ;
2013-06-24 13:41:48 +02:00
}
else
{
2023-06-08 12:24:39 +02:00
labelInfo . Text = Messages . VCPU_ONLY_WHEN_HALTED ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
tableLayoutPanelInfo . Visible = true ;
2013-06-24 13:41:48 +02:00
}
else
{
2023-06-08 12:24:39 +02:00
tableLayoutPanelInfo . Visible = false ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
_origVCpusMax = vm . VCPUs_max > 0 ? vm . VCPUs_max : 1 ;
_origVCpusAtStartup = vm . VCPUs_at_startup > 0 ? vm . VCPUs_at_startup : 1 ;
_origVCpuWeight = _currentVCpuWeight ;
_origVCpus = _isVCpuHotplugSupported ? _origVCpusMax : _origVCpusAtStartup ;
_prevVCpusMax = _origVCpusMax ; // we use variable in RefreshCurrentVCPUs for checking if VcpusAtStartup and VcpusMax were equal before VcpusMax changed
2016-09-29 13:11:37 +02:00
2023-06-08 12:24:39 +02:00
_currentVCpuWeight = Convert . ToDecimal ( vm . GetVcpuWeight ( ) ) ;
2016-09-29 13:11:37 +02:00
2023-06-08 12:24:39 +02:00
InitializeVCpuControls ( ) ;
2016-09-29 13:11:37 +02:00
2023-06-08 12:24:39 +02:00
_validToSave = true ;
2016-09-29 13:11:37 +02:00
}
2023-06-08 12:24:39 +02:00
private void PopulateVCpuComboBox ( ComboBox comboBox , long min , long max , long currentValue ,
Predicate < long > isValid )
2016-09-29 13:11:37 +02:00
{
comboBox . BeginUpdate ( ) ;
comboBox . Items . Clear ( ) ;
2023-06-08 12:24:39 +02:00
for ( var i = min ; i < = max ; + + i )
2016-09-29 13:11:37 +02:00
{
if ( i = = currentValue | | isValid ( i ) )
comboBox . Items . Add ( i ) ;
}
2023-06-08 12:24:39 +02:00
2016-09-29 13:11:37 +02:00
if ( currentValue > max )
comboBox . Items . Add ( currentValue ) ;
comboBox . SelectedItem = currentValue ;
comboBox . EndUpdate ( ) ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
private void PopulateVCpus ( long maxVCpus , long currentVCpus )
2016-02-12 18:01:05 +01:00
{
2023-06-08 12:24:39 +02:00
PopulateVCpuComboBox ( comboBoxVCPUs , 1 , maxVCpus , currentVCpus , i = > comboBoxTopology . IsValidVCPU ( i ) ) ;
2016-09-29 13:11:37 +02:00
}
2023-06-08 12:24:39 +02:00
private void PopulateVCpusAtStartup ( long max , long currentValue )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
var min = _vm . power_state = = vm_power_state . Halted ? 1 : _origVCpusAtStartup ;
PopulateVCpuComboBox ( comboBoxInitialVCPUs , min , max , currentValue , i = > true ) ;
2016-09-29 13:11:37 +02:00
}
2023-06-08 12:24:39 +02:00
private void ShowCpuWarnings ( IReadOnlyCollection < string > warnings )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
var show = warnings . Count > 0 ;
cpuWarningLabel . Text = show ? string . Join ( $"{Environment.NewLine}{Environment.NewLine}" , warnings ) : null ;
cpuWarningPictureBox . Visible = cpuWarningLabel . Visible = show ;
2016-02-12 18:01:05 +01:00
}
2023-06-08 12:24:39 +02:00
private void ShowTopologyWarnings ( IReadOnlyCollection < string > warnings )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
var show = warnings . Count > 0 ;
topologyWarningLabel . Text = show ? string . Join ( $"{Environment.NewLine}{Environment.NewLine}" , warnings ) : null ;
topologyPictureBox . Visible = topologyWarningLabel . Visible = show ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
private void ValidateVCpuSettings ( )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
if ( _vm = = null | | ! comboBoxVCPUs . Enabled )
return ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
var homeHost = _vm . Home ( ) ;
var maxPhysicalCpus = _vm . Connection . Cache . Hosts . Select ( h = > h . host_CPUs . Count ) . Max ( ) ;
var homeHostPhysicalCpus = homeHost ? . host_CPUs . Count ;
var warnings = new List < string > ( ) ;
if ( comboBoxVCPUs . SelectedItem ! = null & & maxPhysicalCpus < SelectedVCpusMax )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
if ( homeHostPhysicalCpus ! = null & & homeHostPhysicalCpus < SelectedVCpusMax & &
maxPhysicalCpus > = SelectedVCpusMax )
{
warnings . Add ( Messages . VM_CPUMEMPAGE_VCPU_HOME_HOST_WARNING ) ;
}
else if ( maxPhysicalCpus < SelectedVCpusMax )
{
warnings . Add ( Messages . VM_CPUMEMPAGE_VCPU_WARNING ) ;
}
2016-09-29 13:11:37 +02:00
}
2023-06-08 12:24:39 +02:00
if ( comboBoxVCPUs . SelectedItem ! = null & & SelectedVCpusMax < _minVCpus )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
warnings . Add ( string . Format ( Messages . VM_CPUMEMPAGE_VCPU_MIN_WARNING , _minVCpus ) ) ;
2016-09-29 13:11:37 +02:00
}
2023-06-08 12:24:39 +02:00
if ( comboBoxVCPUs . SelectedItem ! = null & & SelectedVCpusMax > VM . MAX_VCPUS_FOR_NON_TRUSTED_VMS )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
warnings . Add ( string . Format ( Messages . VCPUS_UNTRUSTED_VM_WARNING , VM . MAX_VCPUS_FOR_NON_TRUSTED_VMS , BrandManager . ProductBrand ) ) ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
if ( comboBoxInitialVCPUs . SelectedItem ! = null & & SelectedVCpusAtStartup < _minVCpus )
2014-06-05 14:00:14 +02:00
{
2023-06-08 12:24:39 +02:00
warnings . Add ( string . Format ( Messages . VM_CPUMEMPAGE_VCPU_MIN_WARNING , _minVCpus ) ) ;
2014-06-05 14:00:14 +02:00
}
2023-06-08 12:24:39 +02:00
ShowCpuWarnings ( warnings ) ;
2014-06-05 14:00:14 +02:00
}
2023-06-08 12:24:39 +02:00
private void ValidateTopologySettings ( )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
var warnings = new List < string > ( ) ;
if ( comboBoxVCPUs . SelectedItem ! = null )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
var topologyWarning = VM . ValidVCPUConfiguration ( ( long ) comboBoxVCPUs . SelectedItem , comboBoxTopology . CoresPerSocket ) ;
if ( ! string . IsNullOrEmpty ( topologyWarning ) )
{
warnings . Add ( $"{topologyWarning}." ) ;
}
2016-09-29 13:11:37 +02:00
}
2023-06-08 12:24:39 +02:00
ShowTopologyWarnings ( warnings ) ;
2016-09-29 13:11:37 +02:00
}
2023-06-08 12:24:39 +02:00
private void RefreshCurrentVCpus ( )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
// refresh comboBoxInitialVCPUs if it's visible and populated
if ( comboBoxInitialVCPUs . Visible & & comboBoxInitialVCPUs . Items . Count > 0 )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
// 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 ;
2016-09-29 13:11:37 +02:00
}
}
2023-06-08 12:24:39 +02:00
#region IEditPage
2013-06-24 13:41:48 +02:00
public AsyncAction SaveSettings ( )
{
2023-06-08 12:24:39 +02:00
var actions = new List < AsyncAction > ( ) ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
if ( HasVCpuWeightChanged )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
_vm . SetVcpuWeight ( Convert . ToInt32 ( _currentVCpuWeight ) ) ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
if ( HasVCpuChanged | | HasVCpusAtStartupChanged )
2014-06-05 14:00:14 +02:00
{
2023-06-08 12:24:39 +02:00
actions . Add ( new ChangeVCPUSettingsAction ( _vm , SelectedVCpusMax , SelectedVCpusAtStartup ) ) ;
2014-06-05 14:00:14 +02:00
}
2016-09-29 13:11:37 +02:00
if ( HasTopologyChanged )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
_vm . SetCoresPerSocket ( comboBoxTopology . CoresPerSocket ) ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
switch ( actions . Count )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
case 0 :
return null ;
case 1 :
return actions [ 0 ] ;
default :
{
var multipleAction = new MultipleAction ( _vm . Connection , "" , "" , "" , actions , true ) ;
return multipleAction ;
}
2013-06-24 13:41:48 +02:00
}
}
/// <summary>
2023-06-08 12:24:39 +02:00
/// Must be a VM.
2013-06-24 13:41:48 +02:00
/// </summary>
2023-06-08 12:24:39 +02:00
public void SetXenObjects ( IXenObject orig , IXenObject clone )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
_vm = ( VM ) clone ;
Repopulate ( ) ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
public bool ValidToSave = > _validToSave ;
/** Show local validation balloon tooltips */
public void ShowLocalValidationMessages ( )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
// not applicable
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
public void HideLocalValidationMessages ( )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
// not applicable
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
/** Unregister listeners, dispose balloon tooltips, etc. */
public void Cleanup ( )
2013-06-24 13:41:48 +02:00
{
2023-06-08 12:24:39 +02:00
// not applicable
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
public bool HasChanged = > HasVCpuChanged | | HasTopologyChanged | |
HasVCpusAtStartupChanged | | HasVCpuWeightChanged ;
2013-06-24 13:41:48 +02:00
2023-06-08 12:24:39 +02:00
#endregion
2018-12-11 15:34:35 +01:00
2023-06-08 12:24:39 +02:00
#region Events
2018-12-11 15:34:35 +01:00
2023-06-08 12:24:39 +02:00
private void comboBoxTopology_SelectedIndexChanged ( object sender , EventArgs e )
2018-12-11 15:34:35 +01:00
{
2023-06-08 12:24:39 +02:00
ValidateTopologySettings ( ) ;
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
private void comboBoxInitialVCPUs_SelectedIndexChanged ( object sender , EventArgs e )
2016-09-29 13:11:37 +02:00
{
2023-06-08 12:24:39 +02:00
ValidateVCpuSettings ( ) ;
2016-09-29 13:11:37 +02:00
}
2023-06-08 12:24:39 +02:00
private void comboBoxVCPUs_SelectedIndexChanged ( object sender , EventArgs e )
2014-06-06 16:33:22 +02:00
{
2023-06-08 12:24:39 +02:00
ValidateVCpuSettings ( ) ;
comboBoxTopology . Update ( ( long ) comboBoxVCPUs . SelectedItem ) ;
2018-12-11 15:34:35 +01:00
ValidateTopologySettings ( ) ;
2023-06-08 12:24:39 +02:00
RefreshCurrentVCpus ( ) ;
2014-06-06 16:33:22 +02:00
}
2023-06-08 12:24:39 +02:00
private void tbPriority_Scroll ( object sender , EventArgs e )
2014-06-06 16:33:22 +02:00
{
2023-06-08 12:24:39 +02:00
_currentVCpuWeight = Convert . ToDecimal ( Math . Pow ( 4.0d , Convert . ToDouble ( transparentTrackBar1 . Value ) ) ) ;
if ( transparentTrackBar1 . Value = = transparentTrackBar1 . Max )
_currentVCpuWeight - - ;
2014-06-06 16:33:22 +02:00
}
2023-06-08 12:24:39 +02:00
#endregion
2013-06-24 13:41:48 +02:00
}
2023-06-08 12:24:39 +02:00
}