mirror of
https://github.com/xcp-ng/xenadmin.git
synced 2024-11-23 20:36:33 +01:00
dbdd3cc02c
New properties: - Pool.HasGpu = Pool has at least one PGPU - Pool.HasVGpu = Pool has at least one PGPU that HasVGpu - PGPU.HasVGpu = PGPU has at least one supported_VGPU_type that is not pass-through New or modified helper functions: - Helpers.GpuCapability = GPU feature not restricted (by licensing) and Pool.HasGpu - Helpers.VGpuCapability = vGPU feature not restricted (by licensing) and Pool.HasVGpu - Helpers.ClearwaterSp1OrGreater = API version is 2.1 or greater The GPU dialogs are displayed as follows: - GPU page on VM properties dialog: Visible only if VM.CanHaveGpu and the GPU feature not restricted (by licensing) - GPU page on New VM Wizard: Visible only if VM.CanHaveGpu and the pool has GPU capability (Helpers.GpuCapability) - GPU page on Pool properties dialog: Visible only if the pool has vGPU capability (Helpers.VGpuCapability) - GPU tab: Visible only if the pool has GPU capability (Helpers.GpuCapability) and is Clearwater SP1 or greater - On the GPU tab, the "Placement policy" panel: Visible only if the pool has vGPU capability (Helpers.VGpuCapability) - On the GPU tab, the "Edit" button on the "vGPU types" panel: Visible only if the PGPU.HasVGpu and vGPU feature not restricted (by licensing) Also: - VM.CanHaveVGpu function renamed to CanHaveGpu - On the GPU tab, renamed "Allowed vGPU types" to "vGPU types
338 lines
12 KiB
C#
338 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 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);
|
|
}
|
|
|
|
// 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 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());
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|