/* 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
}
}