2017-01-16 20:59:50 +01:00
|
|
|
|
/* Copyright (c) Citrix Systems, Inc.
|
2013-11-14 13:59:08 +01: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;
|
2013-11-14 12:06:50 +01:00
|
|
|
|
using System.Collections.Generic;
|
2013-11-14 12:23:35 +01:00
|
|
|
|
using System.ComponentModel;
|
|
|
|
|
using System.Linq;
|
2013-11-14 12:06:50 +01:00
|
|
|
|
using System.Windows.Forms;
|
2013-11-14 12:44:05 +01:00
|
|
|
|
using XenAdmin.Controls;
|
2015-01-22 18:25:45 +01:00
|
|
|
|
using XenAdmin.Controls.Common;
|
2013-11-14 12:06:50 +01:00
|
|
|
|
using XenAdmin.Controls.GPU;
|
2015-01-19 17:49:47 +01:00
|
|
|
|
using XenAdmin.Core;
|
2013-11-14 12:06:50 +01:00
|
|
|
|
using XenAPI;
|
|
|
|
|
|
|
|
|
|
namespace XenAdmin.TabPages
|
|
|
|
|
{
|
|
|
|
|
public partial class GpuPage : BaseTabPage
|
|
|
|
|
{
|
2013-11-14 12:23:35 +01:00
|
|
|
|
private const int ROW_GAP = 5;
|
2013-11-14 12:06:50 +01:00
|
|
|
|
|
|
|
|
|
public GpuPage()
|
|
|
|
|
{
|
|
|
|
|
InitializeComponent();
|
|
|
|
|
Text = Messages.GPU;
|
2013-11-14 12:23:35 +01:00
|
|
|
|
PGPU_CollectionChangedWithInvoke = Program.ProgramInvokeHandler(PGPU_CollectionChanged);
|
2013-11-14 12:06:50 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:23:35 +01:00
|
|
|
|
|
|
|
|
|
private readonly CollectionChangeEventHandler PGPU_CollectionChangedWithInvoke;
|
|
|
|
|
|
2013-11-14 12:06:50 +01:00
|
|
|
|
private IXenObject xenObject;
|
2013-11-14 12:23:35 +01:00
|
|
|
|
List<PGPU> pGPUs = new List<PGPU>();
|
2013-11-14 12:06:50 +01:00
|
|
|
|
|
|
|
|
|
/// <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();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:23:35 +01:00
|
|
|
|
private bool _rebuilding;
|
2013-11-14 12:06:50 +01:00
|
|
|
|
|
|
|
|
|
private void Rebuild()
|
|
|
|
|
{
|
|
|
|
|
Program.AssertOnEventThread();
|
2013-11-14 12:23:35 +01:00
|
|
|
|
if (!Visible)
|
2013-11-14 12:06:50 +01:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:23:35 +01:00
|
|
|
|
// 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)
|
2013-12-11 11:30:06 +01:00
|
|
|
|
where pGpu.supported_VGPU_types.Count > 0 && (xenObject is Pool || xenObject == host)
|
2013-11-14 12:23:35 +01:00
|
|
|
|
orderby host, pGpu.Name ascending
|
|
|
|
|
select pGpu
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
foreach (PGPU pGpu in pGPUs)
|
2013-11-14 12:06:50 +01:00
|
|
|
|
{
|
2013-11-14 12:23:35 +01:00
|
|
|
|
RegisterPgpuHandlers(pGpu);
|
|
|
|
|
|
|
|
|
|
var enabledTypes = pGpu.Connection.ResolveAll(pGpu.enabled_VGPU_types);
|
2013-12-11 11:31:01 +01:00
|
|
|
|
var supportedTypes = pGpu.Connection.ResolveAll(pGpu.supported_VGPU_types);
|
2013-11-14 12:23:35 +01:00
|
|
|
|
|
2013-12-11 11:31:01 +01:00
|
|
|
|
var newSettings = new GpuSettings(enabledTypes.ToArray(), supportedTypes.ToArray(), pGpu.Name);
|
2013-11-14 12:23:35 +01:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2015-01-19 17:49:47 +01:00
|
|
|
|
if (Helpers.VGpuCapability(xenObject.Connection))
|
|
|
|
|
AddRowToPanel(CreateGpuPlacementPolicyPanel(), ref top);
|
2013-11-14 12:44:05 +01:00
|
|
|
|
|
2013-11-14 12:23:35 +01:00
|
|
|
|
foreach (GpuSettings settings in listSettings)
|
|
|
|
|
{
|
2013-12-11 11:30:06 +01:00
|
|
|
|
AddRowToPanel(new GpuRow(xenObject, settingsToPGPUs[settings]), ref top);
|
2013-11-14 12:06:50 +01:00
|
|
|
|
}
|
2013-11-14 12:23:35 +01:00
|
|
|
|
|
2015-01-22 18:25:45 +01:00
|
|
|
|
if (listSettings.Count == 0)
|
|
|
|
|
AddRowToPanel(CreateNoGpuPanel(), ref top);
|
|
|
|
|
|
2013-11-14 12:06:50 +01:00
|
|
|
|
// 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();
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:44:05 +01:00
|
|
|
|
private GpuPlacementPolicyPanel CreateGpuPlacementPolicyPanel()
|
|
|
|
|
{
|
|
|
|
|
return new GpuPlacementPolicyPanel
|
|
|
|
|
{
|
|
|
|
|
MinimumSize = new System.Drawing.Size(393, 35),
|
|
|
|
|
Name = "gpuPlacementPolicyPanel1",
|
|
|
|
|
XenObject = xenObject
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-22 18:25:45 +01:00
|
|
|
|
private AutoHeightLabel CreateNoGpuPanel()
|
|
|
|
|
{
|
|
|
|
|
return new AutoHeightLabel
|
|
|
|
|
{
|
|
|
|
|
Name = "noGpuPanel1",
|
2015-03-31 18:26:37 +02:00
|
|
|
|
Text = xenObject is Pool ? Messages.NO_GPU_IN_POOL : Messages.NO_GPU_ON_HOST
|
2015-01-22 18:25:45 +01:00
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:06:50 +01:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-01-22 18:25:45 +01:00
|
|
|
|
private void AddRowToPanel(Control row, ref int top)
|
2013-11-14 12:06:50 +01:00
|
|
|
|
{
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:28:43 +01:00
|
|
|
|
private GpuRow FindRow(PGPU pgpu)
|
|
|
|
|
{
|
|
|
|
|
return pgpu != null ? pageContainerPanel.Controls.OfType<GpuRow>().FirstOrDefault(row => row.PGPUs.Contains(pgpu)) : null;
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 12:06:50 +01:00
|
|
|
|
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
|
|
|
|
|
}
|
2013-11-14 12:23:35 +01:00
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
{
|
2013-11-14 12:28:43 +01:00
|
|
|
|
if (e.PropertyName == "resident_VGPUs")
|
|
|
|
|
{
|
|
|
|
|
var pgpu = sender as PGPU;
|
|
|
|
|
var gpuRow = FindRow(pgpu);
|
|
|
|
|
if (gpuRow != null)
|
|
|
|
|
gpuRow.RefreshGpu(pgpu);
|
|
|
|
|
}
|
|
|
|
|
|
2013-11-14 13:34:55 +01:00
|
|
|
|
if (e.PropertyName == "enabled_VGPU_types" || e.PropertyName == "supported_VGPU_types")
|
2013-11-14 12:23:35 +01:00
|
|
|
|
{
|
|
|
|
|
Rebuild();
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-11-14 13:34:55 +01:00
|
|
|
|
|
2013-11-14 12:23:35 +01:00
|
|
|
|
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;
|
2013-12-11 11:31:01 +01:00
|
|
|
|
public readonly VGPU_type[] SupportedVgpuTypes;
|
|
|
|
|
public readonly string GpuName;
|
2013-11-14 12:23:35 +01:00
|
|
|
|
|
2013-12-11 11:31:01 +01:00
|
|
|
|
public GpuSettings(VGPU_type[] enabledVgpuTypes, VGPU_type[] supportedVgpuTypes, string name)
|
2013-11-14 12:23:35 +01:00
|
|
|
|
{
|
2013-12-11 11:31:01 +01:00
|
|
|
|
EnabledVgpuTypes = enabledVgpuTypes;
|
|
|
|
|
Array.Sort(EnabledVgpuTypes);
|
|
|
|
|
|
|
|
|
|
SupportedVgpuTypes = supportedVgpuTypes;
|
|
|
|
|
Array.Sort(SupportedVgpuTypes);
|
|
|
|
|
|
|
|
|
|
GpuName = name;
|
2013-11-14 12:23:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
2013-12-11 11:31:01 +01:00
|
|
|
|
private static bool EqualArrays(VGPU_type[] x, VGPU_type[] y)
|
2013-11-14 12:23:35 +01:00
|
|
|
|
{
|
2013-12-11 11:31:01 +01:00
|
|
|
|
if ((x == null || x.Length == 0) &&
|
|
|
|
|
(y == null || y.Length == 0))
|
2013-11-14 12:23:35 +01:00
|
|
|
|
return true;
|
|
|
|
|
|
2013-12-11 11:31:01 +01:00
|
|
|
|
if ((x == null || x.Length == 0) ||
|
|
|
|
|
(y == null || y.Length == 0))
|
2013-11-14 12:23:35 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
2013-12-11 11:31:01 +01:00
|
|
|
|
if (x.Length != y.Length)
|
2013-11-14 12:23:35 +01:00
|
|
|
|
return false;
|
|
|
|
|
|
2013-12-11 11:31:01 +01:00
|
|
|
|
for (int i = 0; i < x.Length; i++)
|
2013-11-14 12:23:35 +01:00
|
|
|
|
{
|
2013-12-11 11:31:01 +01:00
|
|
|
|
if (!x[i].Equals(y[i]))
|
2013-11-14 12:23:35 +01:00
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-11 11:31:01 +01:00
|
|
|
|
|
|
|
|
|
public bool Equals(GpuSettings other)
|
|
|
|
|
{
|
|
|
|
|
return GpuName.Equals(other.GpuName) && EqualArrays(EnabledVgpuTypes, other.EnabledVgpuTypes)
|
|
|
|
|
&& EqualArrays(SupportedVgpuTypes, other.SupportedVgpuTypes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-11-14 12:23:35 +01:00
|
|
|
|
public override string ToString()
|
|
|
|
|
{
|
|
|
|
|
return string.Join(",", EnabledVgpuTypes.Select(t => t.model_name).ToArray());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
2013-11-14 12:06:50 +01:00
|
|
|
|
}
|
|
|
|
|
}
|