xenadmin/XenAdmin/TabPages/GpuPage.cs

360 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.Common;
using XenAdmin.Controls.GPU;
using XenAdmin.Core;
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 && (xenObject is Pool || xenObject == host)
orderby host, pGpu.Name ascending
select pGpu
);
foreach (PGPU pGpu in pGPUs)
{
RegisterPgpuHandlers(pGpu);
var enabledTypes = pGpu.Connection.ResolveAll(pGpu.enabled_VGPU_types);
var supportedTypes = pGpu.Connection.ResolveAll(pGpu.supported_VGPU_types);
var newSettings = new GpuSettings(enabledTypes.ToArray(), supportedTypes.ToArray(), pGpu.Name);
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;
if (Helpers.VGpuCapability(xenObject.Connection))
AddRowToPanel(CreateGpuPlacementPolicyPanel(), ref top);
foreach (GpuSettings settings in listSettings)
{
AddRowToPanel(new GpuRow(xenObject, settingsToPGPUs[settings]), ref top);
}
if (listSettings.Count == 0)
AddRowToPanel(CreateNoGpuPanel(), 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 AutoHeightLabel CreateNoGpuPanel()
{
return new AutoHeightLabel
{
Name = "noGpuPanel1",
Text = xenObject is Pool ? Messages.NO_GPU_IN_POOL : Messages.NO_GPU_ON_HOST
};
}
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(Control 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);
}
}
public override void PageHidden()
{
UnregisterHandlers();
var gpuPlacementPolicyPanel = pageContainerPanel.Controls.OfType<GpuPlacementPolicyPanel>().FirstOrDefault();
if (gpuPlacementPolicyPanel != null)
gpuPlacementPolicyPanel.UnregisterHandlers();
}
internal class GpuSettings : IEquatable<GpuSettings>
{
public readonly VGPU_type[] EnabledVgpuTypes;
public readonly VGPU_type[] SupportedVgpuTypes;
public readonly string GpuName;
public GpuSettings(VGPU_type[] enabledVgpuTypes, VGPU_type[] supportedVgpuTypes, string name)
{
EnabledVgpuTypes = enabledVgpuTypes;
Array.Sort(EnabledVgpuTypes);
SupportedVgpuTypes = supportedVgpuTypes;
Array.Sort(SupportedVgpuTypes);
GpuName = name;
}
private static bool EqualArrays(VGPU_type[] x, VGPU_type[] y)
{
if ((x == null || x.Length == 0) &&
(y == null || y.Length == 0))
return true;
if ((x == null || x.Length == 0) ||
(y == null || y.Length == 0))
return false;
if (x.Length != y.Length)
return false;
for (int i = 0; i < x.Length; i++)
{
if (!x[i].Equals(y[i]))
return false;
}
return true;
}
public bool Equals(GpuSettings other)
{
return GpuName.Equals(other.GpuName) && EqualArrays(EnabledVgpuTypes, other.EnabledVgpuTypes)
&& EqualArrays(SupportedVgpuTypes, other.SupportedVgpuTypes);
}
public override string ToString()
{
return string.Join(",", EnabledVgpuTypes.Select(t => t.model_name).ToArray());
}
}
}
}