/* 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 pGPUs = new List(); /// /// The object that the panel is displaying GPU info for. /// 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 oldControls = new List(pageContainerPanel.Controls.Count); foreach (Control c in pageContainerPanel.Controls) { oldControls.Add(c); } // Group pGPUs with the same settings Dictionary> settingsToPGPUs = new Dictionary>(); // all PGPUs with a particular setting List listSettings = new List(); // 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()); 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, 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().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_CollectionChangedWithInvoke); xenObject.Connection.Cache.RegisterCollectionChanged(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_CollectionChangedWithInvoke); foreach (PGPU pgpu in xenObject.Connection.Cache.PGPUs) { UnregisterPgpuHandlers(pgpu); } } internal class GpuSettings : IEquatable { 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()); } } } }