/* Copyright (c) Cloud Software Group, Inc. * * 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.Drawing; using System.Windows.Forms; using XenAPI; using XenAdmin.Core; using XenAdmin.Controls.CustomDataGraph; using XenAdmin.Dialogs; using XenAdmin.Actions; using XenAdmin.Controls.GradientPanel; namespace XenAdmin.TabPages { public partial class PerformancePage : BaseTabPage { private IXenObject _xenObject; private bool _disposed; private readonly CollectionChangeEventHandler Message_CollectionChangedWithInvoke; private ArchiveMaintainer _archiveMaintainer; public PerformancePage() { InitializeComponent(); Message_CollectionChangedWithInvoke = Program.ProgramInvokeHandler(Message_CollectionChanged); GraphList.SelectedGraphChanged += GraphList_SelectedGraphChanged; GraphList.MouseDown += GraphList_MouseDown; this.DataEventList.SetPlotNav(this.DataPlotNav); SetStyle(ControlStyles.ResizeRedraw, true); SetStyle(ControlStyles.OptimizedDoubleBuffer, true); //set text colour for gradient bar EventsLabel.ForeColor = VerticalGradientPanel.TextColor; base.Text = Messages.PERFORMANCE_TAB_TITLE; UpdateMoveButtons(); } public override string HelpID => "TabPagePerformance"; /// /// Clean up any resources being used. /// /// true if managed resources should be disposed; otherwise, false. protected override void Dispose(bool disposing) { if (_disposed) return; if (disposing) { DeregisterEvents(); _archiveMaintainer?.Dispose(); components?.Dispose(); _disposed = true; } base.Dispose(disposing); } private void GraphList_MouseDown(object sender, MouseEventArgs e) { if (e.Button == MouseButtons.Right) { contextMenuStrip.Show(GraphList, e.X, e.Y); } } private bool CanEnableGraphButtons() { return XenObject != null && GraphList.SelectedGraph != null; } private void UpdateMoveButtons() { bool canEnable = CanEnableGraphButtons(); moveUpButton.Enabled = canEnable && GraphList.SelectedGraphIndex != 0; moveUpToolStripMenuItem.Enabled = moveUpButton.Enabled; moveDownButton.Enabled = canEnable && GraphList.SelectedGraphIndex != GraphList.Count - 1; moveDownToolStripMenuItem.Enabled = moveDownButton.Enabled; } private void UpdateActionButtons() { bool canEnable = CanEnableGraphButtons(); VM vm = XenObject as VM; bool isRunning = XenObject != null && (vm != null) ? vm.current_operations.Count == 0 && vm.IsRunning() : (XenObject is Host); string newText = (vm != null && !isRunning) ? string.Format(Messages.GRAPHS_CANNOT_ADD_VM_HALTED, vm.Name()) : string.Empty; newGraphToolStripMenuItem.Enabled = isRunning; newGraphToolStripMenuItem.ToolTipText = newText; newGraphToolStripContextMenuItem.Enabled = isRunning; newGraphToolStripContextMenuItem.ToolTipText = newText; string editText=(vm != null && !isRunning) ? string.Format(Messages.GRAPHS_CANNOT_EDIT_VM_HALTED, vm.Name()) : string.Empty; editGraphToolStripMenuItem.Enabled = canEnable && isRunning; editGraphToolStripMenuItem.ToolTipText = editText; editGraphToolStripContextMenuItem.Enabled = canEnable && isRunning; editGraphToolStripContextMenuItem.ToolTipText = editText; deleteGraphToolStripMenuItem.Enabled = canEnable && GraphList.Count > 1; deleteGraphToolStripContextMenuItem.Enabled = canEnable && GraphList.Count > 1; restoreDefaultGraphsToolStripMenuItem.Enabled = XenObject != null && !GraphList.ShowingDefaultGraphs; restoreDefaultGraphsToolStripContextMenuItem.Enabled = XenObject != null && !GraphList.ShowingDefaultGraphs; } private void UpdateAllButtons() { UpdateMoveButtons(); UpdateActionButtons(); } private void GraphList_SelectedGraphChanged() { UpdateAllButtons(); } public IXenObject XenObject { private get { return _xenObject; } set { DataEventList.Clear(); DeregisterEvents(); _xenObject = value; RegisterEvents(); var newArchiveMaintainer = new ArchiveMaintainer(value); newArchiveMaintainer.ArchivesUpdated += ArchiveMaintainer_ArchivesUpdated; GraphList.ArchiveMaintainer = newArchiveMaintainer; DataPlotNav.ArchiveMaintainer = newArchiveMaintainer; try { _archiveMaintainer?.Dispose(); } catch (ObjectDisposedException) { // we have already called a dispose } _archiveMaintainer = newArchiveMaintainer; if (_xenObject != null) { GraphList.LoadGraphs(XenObject); LoadEvents(); newArchiveMaintainer.Start(); } RefreshAll(); } } private bool FeatureForbidden { get { return Helpers.FeatureForbidden(XenObject, Host.RestrictPerformanceGraphs); } } private void LoadDataSources() { if (XenObject == null) return; var action = new GetDataSourcesAction(XenObject); action.Completed += SaveGraphs; action.RunAsync(); } private void LoadEvents() { foreach(XenAPI.Message m in XenObject.Connection.Cache.Messages) { CheckMessage(m, CollectionChangeAction.Add); } } private void CheckMessage(XenAPI.Message m, CollectionChangeAction a) { if (!m.ShowOnGraphs() || m.cls != cls.VM) return; Host h = XenObject as Host; if (h != null) { List resVMs = h.Connection.ResolveAll(h.resident_VMs); foreach (VM v in resVMs) { if (v.uuid == m.obj_uuid) { if (a == CollectionChangeAction.Add) DataEventList.AddEvent(new DataEvent(m.timestamp.ToLocalTime().Ticks, 0, m)); else DataEventList.RemoveEvent(new DataEvent(m.timestamp.ToLocalTime().Ticks, 0, m)); break; } } } else if (XenObject is VM) { if (m.obj_uuid != Helpers.GetUuid(XenObject)) return; if (a == CollectionChangeAction.Add) DataEventList.AddEvent(new DataEvent(m.timestamp.ToLocalTime().Ticks, 0, m)); else DataEventList.RemoveEvent(new DataEvent(m.timestamp.ToLocalTime().Ticks, 0, m)); } } private void RegisterEvents() { if (XenObject == null) return; Pool pool = Helpers.GetPoolOfOne(XenObject.Connection); if(pool != null) pool.PropertyChanged += pool_PropertyChanged; XenObject.Connection.Cache.RegisterCollectionChanged(Message_CollectionChangedWithInvoke); } private void Message_CollectionChanged(object sender, CollectionChangeEventArgs e) { Program.AssertOnEventThread(); XenAPI.Message m = ((XenAPI.Message)e.Element); CheckMessage(m, e.Action); } private void pool_PropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == "gui_config") { Dictionary gui_config = Helpers.GetGuiConfig((IXenObject)sender); string uuid = Helpers.GetUuid(XenObject); foreach (string key in gui_config.Keys) { if (!Palette.OtherConfigUUIDRegex.IsMatch(key) || !key.Contains(uuid)) continue; string value = gui_config[key]; int argb; if (!Int32.TryParse(value, out argb)) continue; string[] strs = key.Split('.'); // just set the color, we dont care what it is Palette.SetCustomColor(Palette.GetUuid(strs[strs.Length - 1], XenObject), Color.FromArgb(argb)); } Program.Invoke(this, () => { GraphList.LoadGraphs(XenObject); RefreshAll(); }); } } private void DeregisterEvents() { if (XenObject == null) return; var pool = Helpers.GetPoolOfOne(XenObject.Connection); if (pool != null) pool.PropertyChanged -= pool_PropertyChanged; if (_archiveMaintainer != null) { _archiveMaintainer.ArchivesUpdated -= ArchiveMaintainer_ArchivesUpdated; } XenObject.Connection.Cache.DeregisterCollectionChanged(Message_CollectionChangedWithInvoke); } public override void PageHidden() { DeregisterEvents(); _archiveMaintainer?.Dispose(); } private void ArchiveMaintainer_ArchivesUpdated() { Program.Invoke(this, RefreshAll); } private void RefreshAll() { DataPlotNav.RefreshXRange(false); } private void MoveGraphUp() { if (XenObject == null) return; int index = GraphList.SelectedGraphIndex; if (GraphList.AuthorizedRole) { GraphList.ExchangeGraphs(index, index - 1); GraphList.SaveGraphs(); } } private void MoveGraphDown() { if (XenObject == null) return; int index = GraphList.SelectedGraphIndex; if (GraphList.AuthorizedRole) { GraphList.ExchangeGraphs(index, index + 1); GraphList.SaveGraphs(); } } private void NewGraph() { if (XenObject == null) return; if (GraphList.AuthorizedRole) { using (var dialog = new GraphDetailsDialog(GraphList)) dialog.ShowDialog(); } } private void EditGraph() { if (XenObject == null) return; if (GraphList.AuthorizedRole) { using (var dialog = new GraphDetailsDialog(GraphList, GraphList.SelectedGraph)) dialog.ShowDialog(); } } private void DeleteGraph() { using (ThreeButtonDialog dlog = new WarningDialog(string.Format(Messages.DELETE_GRAPH_MESSAGE, GraphList.SelectedGraph.DisplayName.EscapeAmpersands()), ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo)) { if (dlog.ShowDialog(this) == DialogResult.Yes) if (GraphList.AuthorizedRole) { GraphList.DeleteGraph(GraphList.SelectedGraph); LoadDataSources(); } } } private void RestoreDefaultGraphs() { using (ThreeButtonDialog dlog = new WarningDialog(Messages.GRAPHS_RESTORE_DEFAULT_MESSAGE, ThreeButtonDialog.ButtonYes, ThreeButtonDialog.ButtonNo)) { if (dlog.ShowDialog(this) == DialogResult.Yes) if (GraphList.AuthorizedRole) { GraphList.RestoreDefaultGraphs(); LoadDataSources(); } } } private void SaveGraphs(ActionBase sender) { if (!(sender is GetDataSourcesAction action)) return; Program.Invoke(Program.MainWindow, () => { var dataSourceItems = DataSourceItemList.BuildList(action.XenObject, action.DataSources); GraphList.SaveGraphs(dataSourceItems); }); } private void LastYearZoom() { DataPlotNav.ZoomToRange(TimeSpan.Zero, TimeSpan.FromDays(366) - TimeSpan.FromSeconds(1)); } private void LastMonthZoom() { DataPlotNav.ZoomToRange(TimeSpan.Zero, TimeSpan.FromDays(30) - TimeSpan.FromSeconds(1)); } private void LastWeekZoom() { DataPlotNav.ZoomToRange(TimeSpan.Zero, TimeSpan.FromDays(7) - TimeSpan.FromSeconds(1)); } private void LastDayZoom() { DataPlotNav.ZoomToRange(TimeSpan.Zero, TimeSpan.FromDays(1) - TimeSpan.FromSeconds(1)); } private void LastHourZoom() { DataPlotNav.ZoomToRange(TimeSpan.Zero, TimeSpan.FromHours(1) - TimeSpan.FromSeconds(1)); } private void LastTenMinutesZoom() { DataPlotNav.ZoomToRange(TimeSpan.Zero, TimeSpan.FromMinutes(10) - TimeSpan.FromSeconds(1)); } #region Control event handlers private void addGraphToolStripMenuItem_Click(object sender, EventArgs e) { NewGraph(); } private void editGraphToolStripMenuItem_Click(object sender, EventArgs e) { EditGraph(); } private void deleteGraphToolStripMenuItem_Click(object sender, EventArgs e) { DeleteGraph(); } private void restoreDefaultGraphsToolStripMenuItem_Click(object sender, EventArgs e) { RestoreDefaultGraphs(); } private void moveUpButton_Click(object sender, EventArgs e) { MoveGraphUp(); } private void moveDownButton_Click(object sender, EventArgs e) { MoveGraphDown(); } private void graphActionsButton_Click(object sender, EventArgs e) { graphActionsMenuStrip.Show(graphActionsButton, 0, graphActionsButton.Height); } private void graphActionsMenuStrip_Opening(object sender, CancelEventArgs e) { UpdateActionButtons(); } private void zoomButton_Click(object sender, EventArgs e) { zoomMenuStrip.Show(zoomButton, 0, zoomButton.Height); } private void lastYearToolStripMenuItem_Click(object sender, EventArgs e) { LastYearZoom(); } private void lastMonthToolStripMenuItem_Click(object sender, EventArgs e) { LastMonthZoom(); } private void lastWeekToolStripMenuItem_Click(object sender, EventArgs e) { LastWeekZoom(); } private void lastDayToolStripMenuItem_Click(object sender, EventArgs e) { LastDayZoom(); } private void lastHourToolStripMenuItem_Click(object sender, EventArgs e) { LastHourZoom(); } private void lastTenMinutesToolStripMenuItem_Click(object sender, EventArgs e) { LastTenMinutesZoom(); } private void contextMenuStrip_Opening(object sender, CancelEventArgs e) { UpdateActionButtons(); } private void moveUpToolStripMenuItem_Click(object sender, EventArgs e) { MoveGraphUp(); } private void moveDownToolStripMenuItem_Click(object sender, EventArgs e) { MoveGraphDown(); } private void newGraphToolStripContextMenuItem_Click(object sender, EventArgs e) { NewGraph(); } private void editGraphToolStripContextMenuItem_Click(object sender, EventArgs e) { EditGraph(); } private void deleteGraphToolStripContextMenuItem_Click(object sender, EventArgs e) { DeleteGraph(); } private void restoreDefaultGraphsToolStripContextMenuItem_Click(object sender, EventArgs e) { RestoreDefaultGraphs(); } private void lastYearToolStripContextMenuItem_Click(object sender, EventArgs e) { LastYearZoom(); } private void lastMonthToolStripContextMenuItem_Click(object sender, EventArgs e) { LastMonthZoom(); } private void lastWeekToolStripContextMenuItem_Click(object sender, EventArgs e) { LastWeekZoom(); } private void lastDayToolStripContextMenuItem_Click(object sender, EventArgs e) { LastDayZoom(); } private void lastHourToolStripContextMenuItem_Click(object sender, EventArgs e) { LastHourZoom(); } private void lastTenMinutesToolStripContextMenuItem_Click(object sender, EventArgs e) { LastTenMinutesZoom(); } private void GraphList_MouseDoubleClick(object sender, MouseEventArgs e) { UpdateActionButtons(); if (editGraphToolStripMenuItem.Enabled) EditGraph(); } #endregion } }