xenadmin/XenAdmin/Dialogs/ScheduledSnapshots/ScheduledSnapshotsDialog.cs
Konstantina Chremmou 37e3810d73 Removed the background worker and added an async action to retrieve the local
server time (previously on the UI  thread) and also update the message list
for each snapshot schedule. Preserve column sorting and multiple selection
when the list is rebuilt. Do not enforce minimum size on the form since it
is not resizable.

Signed-off-by: Konstantina Chremmou <konstantina.chremmou@citrix.com>
2017-09-11 13:22:12 +01:00

565 lines
22 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.Drawing;
using System.Linq;
using System.Windows.Forms;
using XenAdmin.Actions;
using XenAdmin.Alerts;
using XenAdmin.Controls;
using XenAdmin.Core;
using XenAdmin.Properties;
using XenAdmin.Wizards.NewPolicyWizard;
using XenAPI;
namespace XenAdmin.Dialogs.ScheduledSnapshots
{
public partial class ScheduledSnapshotsDialog: XenDialogBase
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly Pool Pool;
private bool updatingPolicies;
public ScheduledSnapshotsDialog(Pool pool)
: base(pool.Connection)
{
Pool = pool;
InitializeComponent();
chevronButton1.Text = Messages.SHOW_RUN_HISTORY;
chevronButton1.Image = Properties.Resources.PDChevronDown;
ColumnExpand.DefaultCellStyle.NullValue = null;
comboBoxTimeSpan.SelectedIndex = 0;
dataGridViewRunHistory.Columns[2].ValueType = typeof(DateTime);
dataGridViewRunHistory.Columns[2].DefaultCellStyle.Format = Messages.DATEFORMAT_DMY_HM;
panelHistory.Visible = false;
RefreshPoolTitle(pool);
RefreshButtons();
}
public ScheduledSnapshotsDialog() { }
private PolicyRow SelectedVmssRow
{
get
{
if (dataGridViewPolicies.SelectedRows.Count > 0)
return dataGridViewPolicies.SelectedRows[0] as PolicyRow;
return null;
}
}
private int RunHistoryTimeSpan
{
get
{
switch (comboBoxTimeSpan.SelectedIndex)
{
case 0: //top 10 messages (default)
return 0;
case 1:
return 24; //messages from past 24 Hrs
case 2:
return 7 * 24; //messages from last 7 days
default:
return 0;
}
}
}
private class PolicyRow : DataGridViewRow
{
private DataGridViewTextBoxCell _name = new DataGridViewTextBoxCell();
private DataGridViewTextBoxCell _numVMs = new DataGridViewTextBoxCell();
private DataGridViewTextBoxCell _nextRunTime = new DataGridViewTextBoxCell();
private DataGridViewTextBoxCell _status = new DataGridViewTextBoxCell();
private DataGridViewTextAndImageCell _lastResult = new DataGridViewTextAndImageCell();
private readonly DateTime? _serverLocalTime;
private readonly VMSS _policy;
private readonly List<XenAPI.Message> _alertMessages;
public bool IsBusy { get; set; }
public VMSS Policy { get { return _policy; } }
public List<XenAPI.Message> AlertMessages { get { return _alertMessages; } }
public string PolicyName { get; private set; }
public string PolicyStatus { get; private set; }
public int PolicyVmCount { get; private set; }
public DateTime? PolicyNextRunTime { get; private set; }
public string PolicyLastResult { get; private set; }
private Bitmap PolicyLastResultImage { get; set; }
public PolicyRow(VMSS policy, List<XenAPI.Message> alertMessages, DateTime? serverLocalTime)
{
Cells.AddRange(_name, _status, _numVMs, _nextRunTime, _lastResult);
_policy = policy;
_alertMessages = alertMessages;
_serverLocalTime = serverLocalTime;
RefreshRow();
}
private void RefreshRow()
{
PolicyName = _policy.Name();
PolicyVmCount = _policy.VMs.FindAll(vm => _policy.Connection.Resolve(vm).is_a_real_vm()).Count;
PolicyStatus = _policy.enabled ? Messages.ENABLED : Messages.DISABLED;
if (_serverLocalTime.HasValue)
PolicyNextRunTime = _policy.GetNextRunTime(_serverLocalTime.Value);
else
PolicyNextRunTime = null;
if (_alertMessages.Count > 0)
{
if (_alertMessages[0].priority == PolicyAlert.INFO_PRIORITY)
{
PolicyLastResult = Messages.VMSS_SUCCEEDED;
PolicyLastResultImage = Resources._075_TickRound_h32bit_16;
}
else
{
PolicyLastResult = Messages.FAILED;
PolicyLastResultImage = Resources._075_WarningRound_h32bit_16;
}
}
else
{
PolicyLastResult = Messages.NOT_YET_RUN;
PolicyLastResultImage = null;
}
_name.Value = PolicyName;
_numVMs.Value = PolicyVmCount;
_status.Value = PolicyStatus;
_lastResult.Value = PolicyLastResult;
_lastResult.Image = PolicyLastResultImage;
_nextRunTime.Value = PolicyNextRunTime.HasValue
? HelpersGUI.DateTimeToString(PolicyNextRunTime.Value, Messages.DATEFORMAT_DMY_HM, true)
: Messages.VMSS_HOST_NOT_LIVE;
}
}
private void RefreshPoolTitle(Pool pool)
{
int protectedVMs = 0;
int realVMs = 0;
foreach (var vm in pool.Connection.Cache.VMs)
{
if (vm.is_a_real_vm() && vm.Show(Properties.Settings.Default.ShowHiddenVMs))
{
realVMs++;
if (vm.Connection.Resolve(vm.snapshot_schedule) != null)
protectedVMs++;
}
}
labelPolicyTitle.Text = string.Format(Helpers.IsPool(pool.Connection)
? Messages.VMSS_SCHEDULED_SNAPSHOTS_DEFINED_FOR_POOL
: Messages.VMSS_SCHEDULED_SNAPSHOTS_DEFINED_FOR_SERVER,
pool.Name().Ellipsise(45), protectedVMs, realVMs);
}
private void VMSSCollectionChanged(object sender, EventArgs e)
{
LoadPolicies();
}
private void LoadPolicies()
{
var action = new LoadVmssAction(Pool.Connection);
action.Completed += action_Completed;
action.RunAsync();
}
private void action_Completed(ActionBase sender)
{
var action = sender as LoadVmssAction;
if (action == null || !action.Succeeded)
return;
Program.Invoke(Program.MainWindow, () =>
{
try
{
panelLoading.Visible = false;
updatingPolicies = true;
var selectedPolicyUuids = (from PolicyRow row in dataGridViewPolicies.SelectedRows
select row.Policy.uuid).ToList();
var rowList = from kvp in action.SnapshotSchedules select new PolicyRow(kvp.Key, kvp.Value, action.ServerLocalTime);
Func<PolicyRow, object> comparer = p => p.PolicyName;
if (dataGridViewPolicies.SortedColumn != null)
{
if (dataGridViewPolicies.SortedColumn.Index == NameColum.Index)
comparer = p => p.PolicyName;
else if (dataGridViewPolicies.SortedColumn.Index == EnabledColumn.Index)
comparer = p => p.PolicyStatus;
else if (dataGridViewPolicies.SortedColumn.Index == ColumnVMs.Index)
comparer = p => p.PolicyVmCount;
else if (dataGridViewPolicies.SortedColumn.Index == DescriptionColum.Index)
comparer = p => p.PolicyNextRunTime;
else if (dataGridViewPolicies.SortedColumn.Index == ColumnLastResult.Index)
comparer = p => p.PolicyLastResult;
}
var rows = dataGridViewPolicies.SortOrder == SortOrder.Descending
? rowList.OrderByDescending(comparer) : rowList.OrderBy(comparer);
dataGridViewPolicies.SuspendLayout();
dataGridViewPolicies.Rows.Clear();
dataGridViewPolicies.Rows.AddRange(rows.Cast<DataGridViewRow>().ToArray());
foreach (PolicyRow row in dataGridViewPolicies.Rows)
row.Selected = selectedPolicyUuids.Contains(row.Policy.uuid);
if (dataGridViewPolicies.SelectedRows.Count == 0 && dataGridViewPolicies.Rows.Count > 0)
dataGridViewPolicies.Rows[0].Selected = true;
}
finally
{
dataGridViewPolicies.ResumeLayout();
updatingPolicies = false;
if (action.ServerLocalTime.HasValue)
{
string time= HelpersGUI.DateTimeToString(action.ServerLocalTime.Value, Messages.DATEFORMAT_WDMY_HM_LONG, true);
labelServerTime.Text = string.Format(Messages.SERVER_TIME, time);
}
RefreshPoolTitle(Pool);
RefreshButtons();
RefreshHistoryLabel();
RefreshHistoryGrid();
}
});
}
private void VMProtectionPoliciesDialog_Load(object sender, EventArgs e)
{
panelLoading.Visible = true;
Pool.Connection.Cache.RegisterBatchCollectionChanged<VMSS>(VMSSCollectionChanged);
LoadPolicies();
}
private void VMProtectionPoliciesDialog_FormClosed(object sender, FormClosedEventArgs e)
{
Pool.Connection.Cache.DeregisterBatchCollectionChanged<VMSS>(VMSSCollectionChanged);
}
private void dataGridViewPolicies_SelectionChanged(object sender, EventArgs e)
{
if (updatingPolicies)
return;
RefreshButtons();
RefreshHistoryLabel();
RefreshHistoryGrid();
}
private void RefreshButtons()
{
if (dataGridViewPolicies.SelectedRows.Count == 1)
{
var row = SelectedVmssRow;
buttonEnable.Text = row.Policy.enabled ? Messages.DISABLE : Messages.ENABLE;
buttonEnable.Enabled = !row.IsBusy && (row.Policy.VMs.Count != 0 || row.Policy.enabled);
buttonProperties.Enabled = !row.IsBusy;
buttonRunNow.Enabled = !row.IsBusy && row.Policy.enabled;
comboBoxTimeSpan.Enabled = !row.IsBusy;
}
else
{
buttonProperties.Enabled = buttonEnable.Enabled = buttonRunNow.Enabled =
comboBoxTimeSpan.Enabled = false;
}
buttonDelete.Enabled = (from PolicyRow row in dataGridViewPolicies.SelectedRows where !row.IsBusy select row).Any();
}
#region Button event handlers
private void buttonNew_Click(object sender, System.EventArgs e)
{
new NewPolicyWizard(Pool).Show(this);
}
private void buttonCancel_Click(object sender, System.EventArgs e)
{
this.Close();
}
private void buttonEnable_Click(object sender, EventArgs e)
{
var row = SelectedVmssRow;
if (row != null)
{
row.IsBusy = true;
RefreshButtons();
new ChangePolicyEnabledAction(row.Policy).RunAsync();
}
}
private void buttonRunNow_Click(object sender, EventArgs e)
{
var row = SelectedVmssRow;
if (row != null)
{
row.IsBusy = true;
RefreshButtons();
new RunPolicyNowAction(row.Policy).RunAsync();
}
}
private void buttonProperties_Click(object sender, EventArgs e)
{
var row = SelectedVmssRow;
if (row != null)
{
using (PropertiesDialog propertiesDialog = new PropertiesDialog(row.Policy))
propertiesDialog.ShowDialog(this);
}
}
private void buttonDelete_Click(object sender, EventArgs e)
{
var selectedPolicies = new List<VMSS>();
int numberOfProtectedVMs = 0;
foreach (PolicyRow row in dataGridViewPolicies.SelectedRows)
{
selectedPolicies.Add(row.Policy);
numberOfProtectedVMs += row.Policy.VMs.Count;
}
string text = selectedPolicies.Count == 1
? String.Format(numberOfProtectedVMs == 0
? Messages.CONFIRM_DELETE_POLICY_0
: Messages.CONFIRM_DELETE_POLICY, selectedPolicies[0].Name(), numberOfProtectedVMs)
: string.Format(numberOfProtectedVMs == 0
? Messages.CONFIRM_DELETE_POLICIES_0
: Messages.CONFIRM_DELETE_POLICIES, numberOfProtectedVMs);
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Warning, text, Messages.DELETE_VMSS_TITLE),
ThreeButtonDialog.ButtonYes,
ThreeButtonDialog.ButtonNo))
{
if (dlg.ShowDialog(this) == DialogResult.Yes)
{
foreach (PolicyRow row in dataGridViewPolicies.SelectedRows)
row.IsBusy = true;
RefreshButtons();
new DestroyPolicyAction(Pool.Connection, selectedPolicies).RunAsync();
}
}
}
private void chevronButton1_ButtonClick(object sender, EventArgs e)
{
if (chevronButton1.Text == Messages.HIDE_RUN_HISTORY)
{
chevronButton1.Text = Messages.SHOW_RUN_HISTORY;
chevronButton1.Image = Properties.Resources.PDChevronDown;
panelHistory.Visible = false;
}
else
{
chevronButton1.Text = Messages.HIDE_RUN_HISTORY;
chevronButton1.Image = Properties.Resources.PDChevronUp;
panelHistory.Visible = true;
}
}
private void chevronButton1_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Space)
chevronButton1_ButtonClick(sender, e);
}
#endregion
internal override string HelpName
{
get
{
return "VMSnapshotSchedulesDialog";
}
}
private void dataGridViewRunHistory_CellClick(object sender, DataGridViewCellEventArgs e)
{
if (e.RowIndex >= 0)
{
HistoryRow row = (HistoryRow)dataGridViewRunHistory.Rows[e.RowIndex];
if (row.Alert.Type != "info")
{
row.Expanded = !row.Expanded;
row.RefreshRow();
}
}
}
private void comboBoxTimeSpan_SelectedIndexChanged(object sender, EventArgs e)
{
RefreshHistoryGrid();
}
private void RefreshHistoryGrid()
{
try
{
dataGridViewRunHistory.SuspendLayout();
dataGridViewRunHistory.Rows.Clear();
var row = SelectedVmssRow;
if (row == null)
return;
var vmss = row.Policy;
var messages = row.AlertMessages;
int hoursFromNow = RunHistoryTimeSpan;
DateTime currentTime = DateTime.Now;
DateTime offset = currentTime.Add(new TimeSpan(-hoursFromNow, 0, 0));
if (hoursFromNow == 0)
{
for (int i = 0; i < 10 && i < messages.Count; i++)
{
var msg = messages[i];
var alert = new PolicyAlert(msg.priority, msg.name, msg.timestamp, msg.body, vmss.Name());
dataGridViewRunHistory.Rows.Add(new HistoryRow(alert));
}
}
else
{
foreach (var msg in messages)
{
if (msg.timestamp >= offset)
{
var alert = new PolicyAlert(msg.priority, msg.name, msg.timestamp, msg.body, vmss.Name());
dataGridViewRunHistory.Rows.Add(new HistoryRow(alert));
}
else
break;
}
}
}
finally
{
dataGridViewRunHistory.ResumeLayout();
}
}
private void RefreshHistoryLabel()
{
var row = SelectedVmssRow;
if (row == null)
{
labelHistory.Text = "";
return;
}
string name = row.Policy.Name();
// ellipsise if necessary
using (Graphics g = labelHistory.CreateGraphics())
{
int maxWidth = labelShow.Left - labelHistory.Left;
int availableWidth = maxWidth - (int)g.MeasureString(string.Format(Messages.HISTORY_FOR_POLICY, ""), labelHistory.Font).Width;
name = name.Ellipsise(new Rectangle(0, 0, availableWidth, labelHistory.Height), labelHistory.Font);
}
labelHistory.Text = string.Format(Messages.HISTORY_FOR_POLICY, name);
}
private class HistoryRow : DataGridViewRow
{
private DataGridViewImageCell _expand = new DataGridViewImageCell();
private DataGridViewTextAndImageCell _result = new DataGridViewTextAndImageCell();
private DataGridViewTextBoxCell _dateTime = new DataGridViewTextBoxCell();
private DataGridViewTextBoxCell _description = new DataGridViewTextBoxCell();
public readonly PolicyAlert Alert;
public HistoryRow(PolicyAlert alert)
{
Alert = alert;
Cells.AddRange(_expand, _result, _dateTime, _description);
RefreshRow();
}
[DefaultValue(false)]
public bool Expanded { get; set; }
public void RefreshRow()
{
_expand.Value = Expanded ? Resources.expanded_triangle : Resources.contracted_triangle;
if (Alert.Type == "info")
_expand.Value = null;
if (Alert.Type == "error")
{
_result.Image = Properties.Resources._075_WarningRound_h32bit_16;
_result.Value = Messages.ERROR;
}
else if (Alert.Type == "warn")
{
_result.Image = Properties.Resources._075_WarningRound_h32bit_16;
_result.Value = Messages.WARNING;
}
else if (Alert.Type == "info")
{
_result.Image = Properties.Resources._075_TickRound_h32bit_16;
_result.Value = Messages.INFORMATION;
}
_dateTime.Value = Alert.Time;
if (Alert.Type == "error")
_description.Value = Expanded ? string.Format("{0}\r\n{1}", Alert.ShortFormatBody, Alert.Text) : Alert.ShortFormatBody.Ellipsise(80);
else
_description.Value = Expanded ? Alert.Text : Alert.ShortFormatBody.Ellipsise(90);
}
}
}
}