mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-23 20:36:33 +01:00
da5408bf21
Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
328 lines
12 KiB
C#
328 lines
12 KiB
C#
/* 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.Linq;
|
|
using System.Windows.Forms;
|
|
using XenAdmin.Controls;
|
|
using XenAdmin.Controls.GPU;
|
|
using XenAPI;
|
|
|
|
namespace XenAdmin.TabPages
|
|
{
|
|
public partial class GpuPage : BaseTabPage
|
|
{
|
|
private const int ROW_GAP = 5;
|
|
|
|
public GpuPage()
|
|
{
|
|
InitializeComponent();
|
|
Text = Messages.GPU;
|
|
PGPU_CollectionChangedWithInvoke = Program.ProgramInvokeHandler(PGPU_CollectionChanged);
|
|
}
|
|
|
|
|
|
private readonly CollectionChangeEventHandler PGPU_CollectionChangedWithInvoke;
|
|
|
|
private IXenObject xenObject;
|
|
List<PGPU> pGPUs = new List<PGPU>();
|
|
|
|
/// <summary>
|
|
/// The object that the panel is displaying GPU info for.
|
|
/// </summary>
|
|
public IXenObject XenObject
|
|
{
|
|
set
|
|
{
|
|
System.Diagnostics.Trace.Assert(value is Pool || value is Host);
|
|
xenObject = value;
|
|
|
|
Rebuild();
|
|
}
|
|
}
|
|
|
|
private bool _rebuilding;
|
|
|
|
private void Rebuild()
|
|
{
|
|
Program.AssertOnEventThread();
|
|
if (!Visible)
|
|
return;
|
|
_rebuilding = true;
|
|
pageContainerPanel.SuspendLayout();
|
|
|
|
// Store a list of the current controls. We remove them at the end because it makes less flicker that way.
|
|
List<Control> oldControls = new List<Control>(pageContainerPanel.Controls.Count);
|
|
foreach (Control c in pageContainerPanel.Controls)
|
|
{
|
|
oldControls.Add(c);
|
|
}
|
|
|
|
// Group pGPUs with the same settings
|
|
Dictionary<GpuSettings, List<PGPU>> settingsToPGPUs = new Dictionary<GpuSettings, List<PGPU>>(); // all PGPUs with a particular setting
|
|
List<GpuSettings> listSettings = new List<GpuSettings>(); // also make a list of GpuSettings to preserve the order
|
|
|
|
pGPUs.Clear();
|
|
|
|
var allPgpus = xenObject.Connection.Cache.PGPUs;
|
|
pGPUs.AddRange(from pGpu in allPgpus
|
|
let host = xenObject.Connection.Resolve(pGpu.host)
|
|
where pGpu.supported_VGPU_types.Count > 0
|
|
orderby host, pGpu.Name ascending
|
|
select pGpu
|
|
);
|
|
|
|
foreach (PGPU pGpu in pGPUs)
|
|
{
|
|
RegisterPgpuHandlers(pGpu);
|
|
|
|
var enabledTypes = pGpu.Connection.ResolveAll(pGpu.enabled_VGPU_types);
|
|
|
|
if (enabledTypes.Count > 1)
|
|
{
|
|
enabledTypes.Sort((t1, t2) =>
|
|
{
|
|
int result = t1.Capacity.CompareTo(t2.Capacity);
|
|
return result != 0 ? result : t1.Name.CompareTo(t2.Name);
|
|
});
|
|
}
|
|
|
|
var newSettings = new GpuSettings(enabledTypes.ToArray());
|
|
|
|
var existingSettings = settingsToPGPUs.Keys.FirstOrDefault(ss => ss.Equals(newSettings));
|
|
|
|
if (existingSettings == null) // we've not seen these settings on another pGPU
|
|
{
|
|
settingsToPGPUs.Add(newSettings, new List<PGPU>());
|
|
listSettings.Add(newSettings);
|
|
existingSettings = newSettings;
|
|
}
|
|
settingsToPGPUs[existingSettings].Add(pGpu);
|
|
}
|
|
|
|
int initScroll = pageContainerPanel.VerticalScroll.Value;
|
|
int top = pageContainerPanel.Padding.Top - initScroll;
|
|
|
|
AddRowToPanel(CreateGpuPlacementPolicyPanel(), ref top);
|
|
|
|
foreach (GpuSettings settings in listSettings)
|
|
{
|
|
AddRowToPanel(new GpuRow(xenObject.Connection, settingsToPGPUs[settings]), ref top);
|
|
}
|
|
|
|
// Remove old controls
|
|
foreach (Control c in oldControls)
|
|
{
|
|
pageContainerPanel.Controls.Remove(c);
|
|
int scroll = initScroll;
|
|
if (scroll > pageContainerPanel.VerticalScroll.Maximum)
|
|
scroll = pageContainerPanel.VerticalScroll.Maximum;
|
|
pageContainerPanel.VerticalScroll.Value = scroll;
|
|
c.Dispose();
|
|
}
|
|
_rebuilding = false;
|
|
pageContainerPanel.ResumeLayout();
|
|
ReLayout();
|
|
}
|
|
|
|
private GpuPlacementPolicyPanel CreateGpuPlacementPolicyPanel()
|
|
{
|
|
return new GpuPlacementPolicyPanel
|
|
{
|
|
MinimumSize = new System.Drawing.Size(393, 35),
|
|
Name = "gpuPlacementPolicyPanel1",
|
|
XenObject = xenObject
|
|
};
|
|
}
|
|
|
|
private void ReLayout()
|
|
{
|
|
Program.AssertOnEventThread();
|
|
if (_rebuilding)
|
|
return;
|
|
|
|
int initScroll = pageContainerPanel.VerticalScroll.Value;
|
|
int top = pageContainerPanel.Padding.Top - initScroll;
|
|
foreach (Control row in pageContainerPanel.Controls)
|
|
{
|
|
row.Top = top;
|
|
top += row.Height + ROW_GAP;
|
|
}
|
|
}
|
|
|
|
private void AddRowToPanel(UserControl row, ref int top)
|
|
{
|
|
row.Top = top;
|
|
row.Left = pageContainerPanel.Padding.Left - pageContainerPanel.HorizontalScroll.Value;
|
|
SetRowWidth(row);
|
|
row.Anchor = AnchorStyles.Top | AnchorStyles.Left;
|
|
top += row.Height + ROW_GAP;
|
|
row.Resize += row_Resize;
|
|
pageContainerPanel.Controls.Add(row);
|
|
}
|
|
|
|
private GpuRow FindRow(PGPU pgpu)
|
|
{
|
|
return pgpu != null ? pageContainerPanel.Controls.OfType<GpuRow>().FirstOrDefault(row => row.PGPUs.Contains(pgpu)) : null;
|
|
}
|
|
|
|
private void pageContainerPanel_SizeChanged(object sender, EventArgs e)
|
|
{
|
|
foreach (Control row in pageContainerPanel.Controls)
|
|
SetRowWidth(row);
|
|
}
|
|
|
|
void row_Resize(object sender, EventArgs e)
|
|
{
|
|
ReLayout();
|
|
}
|
|
|
|
private void SetRowWidth(Control row)
|
|
{
|
|
row.Width = pageContainerPanel.Width - pageContainerPanel.Padding.Left - 25; // It won't drop below row.MinimumSize.Width though
|
|
}
|
|
|
|
private void PGPU_CollectionChanged(object sender, CollectionChangeEventArgs e)
|
|
{
|
|
if (e.Action == CollectionChangeAction.Remove)
|
|
{
|
|
PGPU pgpu = e.Element as PGPU;
|
|
UnregisterPgpuHandlers(pgpu);
|
|
}
|
|
XenObject = xenObject;
|
|
}
|
|
|
|
private void pgpu_PropertyChanged(object sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName == "resident_VGPUs")
|
|
{
|
|
var pgpu = sender as PGPU;
|
|
var gpuRow = FindRow(pgpu);
|
|
if (gpuRow != null)
|
|
gpuRow.RefreshGpu(pgpu);
|
|
}
|
|
|
|
if (e.PropertyName == "enabled_VGPU_types" || e.PropertyName == "supported_VGPU_types")
|
|
{
|
|
Rebuild();
|
|
}
|
|
}
|
|
|
|
private void RegisterPgpuHandlers(PGPU pgpu)
|
|
{
|
|
pgpu.PropertyChanged -= pgpu_PropertyChanged;
|
|
pgpu.PropertyChanged += pgpu_PropertyChanged;
|
|
}
|
|
|
|
private void UnregisterPgpuHandlers(PGPU pgpu)
|
|
{
|
|
pgpu.PropertyChanged -= pgpu_PropertyChanged;
|
|
}
|
|
|
|
private void RegisterHandlers()
|
|
{
|
|
if (xenObject == null)
|
|
return;
|
|
|
|
xenObject.Connection.Cache.DeregisterCollectionChanged<PGPU>(PGPU_CollectionChangedWithInvoke);
|
|
xenObject.Connection.Cache.RegisterCollectionChanged<PGPU>(PGPU_CollectionChangedWithInvoke);
|
|
|
|
foreach (PGPU pgpu in xenObject.Connection.Cache.PGPUs)
|
|
{
|
|
UnregisterPgpuHandlers(pgpu);
|
|
RegisterPgpuHandlers(pgpu);
|
|
}
|
|
}
|
|
|
|
private void GpuPage_VisibleChanged(object sender, EventArgs e)
|
|
{
|
|
if (Visible)
|
|
RegisterHandlers();
|
|
else
|
|
UnregisterHandlers();
|
|
}
|
|
|
|
private void UnregisterHandlers()
|
|
{
|
|
if (xenObject == null)
|
|
return;
|
|
|
|
xenObject.Connection.Cache.DeregisterCollectionChanged<PGPU>(PGPU_CollectionChangedWithInvoke);
|
|
|
|
foreach (PGPU pgpu in xenObject.Connection.Cache.PGPUs)
|
|
{
|
|
UnregisterPgpuHandlers(pgpu);
|
|
}
|
|
}
|
|
|
|
|
|
internal class GpuSettings : IEquatable<GpuSettings>
|
|
{
|
|
public readonly VGPU_type[] EnabledVgpuTypes;
|
|
|
|
public GpuSettings(VGPU_type[] vgpuTypes)
|
|
{
|
|
EnabledVgpuTypes = vgpuTypes;
|
|
}
|
|
|
|
public bool Equals(GpuSettings other)
|
|
{
|
|
if ((EnabledVgpuTypes == null || EnabledVgpuTypes.Length == 0) &&
|
|
(other.EnabledVgpuTypes == null || other.EnabledVgpuTypes.Length == 0))
|
|
return true;
|
|
|
|
if ((EnabledVgpuTypes == null || EnabledVgpuTypes.Length == 0) ||
|
|
(other.EnabledVgpuTypes == null || other.EnabledVgpuTypes.Length == 0))
|
|
return false;
|
|
|
|
if (EnabledVgpuTypes.Length != other.EnabledVgpuTypes.Length)
|
|
return false;
|
|
|
|
for (int i = 0; i < EnabledVgpuTypes.Length; i++)
|
|
{
|
|
if (!EnabledVgpuTypes[i].Equals(other.EnabledVgpuTypes[i]))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return string.Join(",", EnabledVgpuTypes.Select(t => t.model_name).ToArray());
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|