xenadmin/XenAdmin/Wizards/PatchingWizard/PatchingWizard_SelectServers.cs
Tim Liu d432cf5477 CA-293776: The order in which a single update is applied on the slave… (#2180)
* CA-293776: The order in which a single update is applied on the slaves is not the same as the order in which they are displayed

Apply the same Hosts sorting rules for all the PatchingWizard Pages .

Signed-off-by: Tim Liu <tim.liu@citrix.com>

* CA-293776: The order in which a single update is applied on the slaves is not the same as the order in which they are displayed

Place orderby clause after where clause

Signed-off-by: Tim Liu <tim.liu@citrix.com>
2018-08-20 15:46:58 +01:00

1056 lines
44 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.Drawing;
using System.Windows.Forms;
using XenAdmin.Controls;
using XenAdmin.Controls.DataGridViewEx;
using XenAdmin.Core;
using XenAdmin.Dialogs;
using XenAdmin.Network;
using XenAdmin.Properties;
using XenAPI;
using XenAdmin.Alerts;
using System.Linq;
namespace XenAdmin.Wizards.PatchingWizard
{
public partial class PatchingWizard_SelectServers : XenTabPage
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private const int PLUS_MINUS_COL = 0;
private const int POOL_CHECKBOX_COL = 1;
private const int POOL_ICON_HOST_CHECKBOX_COL = 2;
private const int UNCHECKED = 0;
private const int CHECKED = 1;
private const int INDETERMINATE = 2;
private bool poolSelectionOnly;
public XenServerPatchAlert SelectedUpdateAlert { private get; set; }
public XenServerPatchAlert FileFromDiskAlert { private get; set; }
public bool FileFromDiskHasUpdateXml { private get; set; }
public WizardMode WizardMode { private get; set; }
public PatchingWizard_SelectServers()
{
InitializeComponent();
dataGridViewHosts.CheckBoxClicked += dataGridViewHosts_CheckBoxClicked;
}
public override string Text
{
get
{
return Messages.PATCHINGWIZARD_SELECTSERVERPAGE_TEXT;
}
}
public override string PageTitle
{
get
{
return Messages.PATCHINGWIZARD_SELECTSERVERPAGE_TITLE;
}
}
public override string HelpID
{
get { return "SelectServers"; }
}
protected override void PageLoadedCore(PageLoadedDirection direction)
{
poolSelectionOnly = WizardMode == WizardMode.AutomatedUpdates || SelectedUpdateAlert != null || FileFromDiskAlert != null;
switch (WizardMode)
{
case WizardMode.AutomatedUpdates:
label1.Text = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_RUBRIC_AUTOMATED_MODE;
break;
case WizardMode.NewVersion:
label1.Text = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_RUBRIC_NEW_VERSION_MODE;
break;
case WizardMode.SingleUpdate:
label1.Text = poolSelectionOnly ? Messages.PATCHINGWIZARD_SELECTSERVERPAGE_RUBRIC_POOL_SELECTION : Messages.PATCHINGWIZARD_SELECTSERVERPAGE_RUBRIC_DEFAULT;
break;
}
// catch selected servers, in order to restore selection after the dataGrid is reloaded
List<Host> selectedServers = SelectedServers;
dataGridViewHosts.Rows.Clear();
List<IXenConnection> xenConnections = ConnectionsManager.XenConnectionsCopy;
xenConnections.Sort();
int licensedPoolCount = 0;
int poolCount = 0;
foreach (IXenConnection xenConnection in xenConnections)
{
// add pools, their members and standalone hosts
Pool pool = Helpers.GetPool(xenConnection);
bool hasPool = pool != null;
PatchingHostsDataGridViewRow poolRow = null;
if (hasPool)
{
poolRow = new PatchingHostsDataGridViewRow(pool);
dataGridViewHosts.Rows.Add(poolRow);
poolRow.Enabled = false;
}
Host[] hosts = xenConnection.Cache.Hosts;
if (hosts.Length > 0)
{
poolCount++;
var automatedUpdatesRestricted = hosts.Any(Host.RestrictBatchHotfixApply); //if any host is not licensed for automated updates
if (!automatedUpdatesRestricted)
licensedPoolCount++;
}
Array.Sort(hosts);
PatchingHostsDataGridViewRow masterRow = null;
foreach (Host host in hosts)
{
var hostRow = new PatchingHostsDataGridViewRow(host, hasPool, !poolSelectionOnly) {ParentPoolRow = poolRow};
dataGridViewHosts.Rows.Add(hostRow);
string tooltipText;
hostRow.Enabled = CanEnableRow(host, out tooltipText);
hostRow.Cells[3].ToolTipText = tooltipText;
//Enable the pool row
if (poolRow != null && hostRow.Enabled)
poolRow.Enabled = true;
if (masterRow == null) //this will be true for the first iteration
masterRow = hostRow;
}
if (poolRow != null && !poolRow.Enabled && masterRow != null)
poolRow.Cells[3].ToolTipText = masterRow.Cells[3].ToolTipText;
}
if (WizardMode == WizardMode.NewVersion && licensedPoolCount > 0) // in NewVersion mode and at least one pool licensed for automated updates
{
applyUpdatesCheckBox.Visible = true;
applyUpdatesCheckBox.Text = poolCount == licensedPoolCount
? Messages.PATCHINGWIZARD_SELECTSERVERPAGE_APPLY_UPDATES
: Messages.PATCHINGWIZARD_SELECTSERVERPAGE_APPLY_UPDATES_MIXED;
}
else // not in NewVersion mode or all pools unlicensed
{
applyUpdatesCheckBox.Visible = false;
}
// restore server selection
SelectServers(selectedServers);
}
public override void SelectDefaultControl()
{
dataGridViewHosts.Select();
}
private bool CanEnableRow(Host host, out string tooltipText)
{
//if host is unreachable
if (!host.IsLive())
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_HOST_UNREACHABLE;
return false;
}
return WizardMode == WizardMode.AutomatedUpdates
? CanEnableRowAutomatedUpdates(host, out tooltipText)
: CanEnableRowNonAutomated(host, out tooltipText);
}
private bool CanEnableRowAutomatedUpdates(Host host, out string tooltipText)
{
var poolOfOne = Helpers.GetPoolOfOne(host.Connection);
// This check is first because it generally can't be fixed, it's a property of the host
if (poolOfOne != null && poolOfOne.IsAutoUpdateRestartsForbidden()) // Forbids update auto restarts
{
tooltipText = Messages.POOL_FORBIDS_AUTOMATED_UPDATES;
return false;
}
var pool = Helpers.GetPool(host.Connection);
if (WizardMode != WizardMode.NewVersion && pool != null && !pool.IsPoolFullyUpgraded()) //partially upgraded pool is not supported
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_AUTOMATED_UPDATES_NOT_SUPPORTED_PARTIALLY_UPGRADED;
return false;
}
//check updgrade sequences
var minimalPatches = WizardMode == WizardMode.NewVersion
? Updates.GetMinimalPatches(host)
: Updates.GetMinimalPatches(host.Connection);
if (minimalPatches == null) //version not supported
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_AUTOMATED_UPDATES_NOT_SUPPORTED_HOST_VERSION;
return false;
}
//check all hosts are licensed for automated updates (there may be restrictions on individual hosts)
if (host.Connection.Cache.Hosts.Any(Host.RestrictBatchHotfixApply))
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_HOST_UNLICENSED_FOR_AUTOMATED_UPDATES;
return false;
}
var us = Updates.GetPatchSequenceForHost(host, minimalPatches);
if (us == null)
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_SERVER_NOT_AUTO_UPGRADABLE;
return false;
}
//if host is up to date
if (us.Count == 0)
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_SERVER_UP_TO_DATE;
return false;
}
tooltipText = null;
return true;
}
private bool CanEnableRowNonAutomated(Host host, out string tooltipText)
{
tooltipText = null;
if (!host.CanApplyHotfixes() && (Helpers.ElyOrGreater(host) || SelectedUpdateType != UpdateType.ISO))
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_HOST_UNLICENSED;
return false;
}
switch (SelectedUpdateType)
{
case UpdateType.NewRetail:
case UpdateType.Existing:
if (Helpers.ElyOrGreater(host))
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_PATCH_NOT_APPLICABLE;
return false;
}
string reason;
if (!IsHostAmongApplicable(host, out reason))
{
tooltipText = reason;
return false;
}
if (!Helpers.ElyOrGreater(host) && Helpers.ElyOrGreater(host.Connection)) // host is pre-Ely, but the master is Ely or greater
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_CANNOT_INSTALL_UPDATE_MASTER_POST_7_0;
return false;
}
return true;
case UpdateType.ISO:
//from Ely onwards, iso does not mean supplemental pack
if (!Helpers.CreamOrGreater(host.Connection))
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_CANNOT_INSTALL_SUPP_PACKS;
return false;
}
if (WizardMode == WizardMode.AutomatedUpdates || SelectedUpdateAlert != null || FileFromDiskAlert != null)
return IsHostAmongApplicable(host, out tooltipText);
// here a file from disk was selected, but it was not an update (FileFromDiskAlert == null)
if ((!Helpers.ElyOrGreater(host.Connection) && FileFromDiskHasUpdateXml) ||
(Helpers.ElyOrGreater(host.Connection) && !FileFromDiskHasUpdateXml))
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_PATCH_NOT_APPLICABLE;
return false;
}
return true;
}
return true;
}
private bool IsHostAmongApplicable(Host host, out string tooltipText)
{
string patchUuidFromAlert = null;
List<Host> applicableHosts = null;
if (SelectedUpdateAlert != null)
{
applicableHosts = SelectedUpdateAlert.DistinctHosts;
if(SelectedUpdateAlert.Patch != null)
patchUuidFromAlert = SelectedUpdateAlert.Patch.Uuid;
}
else if (FileFromDiskAlert != null)
{
applicableHosts = FileFromDiskAlert.DistinctHosts;
if (FileFromDiskAlert.Patch != null)
patchUuidFromAlert = FileFromDiskAlert.Patch.Uuid;
}
tooltipText = null;
if (applicableHosts == null)
return true;
if (host.Connection.Cache.Hosts.Length == 1 && applicableHosts.Contains(host)) //standalone host
return true;
if (WizardMode == WizardMode.NewVersion)
{
if (applicableHosts.Contains(host))
{
var nonApplicables = host.Connection.Cache.Hosts.Count(h =>
!applicableHosts.Contains(h) && !string.IsNullOrEmpty(patchUuidFromAlert) &&
!isPatchApplied(patchUuidFromAlert, h));
if (0 < nonApplicables && nonApplicables < host.Connection.Cache.Hosts.Length)
{
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_NEW_VERSION_UPGRADE_SLAVES_FIRST;
return false;
}
}
}
if (!applicableHosts.Contains(host) && !string.IsNullOrEmpty(patchUuidFromAlert))
{
if (isPatchApplied(patchUuidFromAlert, host))
{
if (applyUpdatesCheckBox.Checked)
return CanEnableRowAutomatedUpdates(host, out tooltipText);
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_PATCH_ALREADY_APPLIED;
return false;
}
tooltipText = Messages.PATCHINGWIZARD_SELECTSERVERPAGE_PATCH_NOT_APPLICABLE;
return false;
}
return true;
}
private bool isPatchApplied(string uuid, Host host)
{
if (Helpers.ElyOrGreater(host))
{
return host.AppliedUpdates().Any(u => u != null && string.Equals(u.uuid, uuid, StringComparison.InvariantCultureIgnoreCase));
}
else
{
List<Pool_patch> hostPatches = host.AppliedPatches();
foreach (Pool_patch patch in hostPatches)
{
if (string.Equals(patch.uuid, uuid, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
}
return false;
}
protected override void PageLeaveCore(PageLoadedDirection direction, ref bool cancel)
{
if (direction == PageLoadedDirection.Forward)
{
if (!AllSelectedHostsConnected())
{
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
row.UpdateIcon();
dataGridViewHosts.Invalidate();
cancel = true;
return;
}
//Upload the patches to the masters if it is necessary
List<Host> masters = SelectedMasters;
//Do RBAC check
foreach (Host master in masters)
{
if (!(Role.CanPerform(new RbacMethodList("pool_patch.apply"), master.Connection)))
{
string nameLabel = master.Name();
Pool pool = Helpers.GetPoolOfOne(master.Connection);
if (pool != null)
nameLabel = pool.Name();
using (var dlg = new ThreeButtonDialog(new ThreeButtonDialog.Details(SystemIcons.Warning, string.Format(Messages.RBAC_UPDATES_WIZARD, master.Connection.Username, nameLabel), Messages.UPDATES_WIZARD)))
{
dlg.ShowDialog(this);
}
cancel = true;
return;
}
}
}
}
private bool AllSelectedHostsConnected()
{
var disconnectedServerNames = new List<string>();
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
if ((int)row.Cells[POOL_CHECKBOX_COL].Value > UNCHECKED && row.IsPoolOrStandaloneHost)
{
IXenConnection connection = ((IXenObject) row.Tag).Connection;
if (connection == null || !connection.IsConnected)
disconnectedServerNames.Add(((IXenObject) row.Tag).Name());
}
}
if (disconnectedServerNames.Count > 0)
{
using (var dlg = new ThreeButtonDialog(
new ThreeButtonDialog.Details(SystemIcons.Warning,
string.Format(Messages.UPDATES_WIZARD_DISCONNECTED_SERVER, Helpers.StringifyList(disconnectedServerNames)),
Messages.UPDATES_WIZARD)))
{
dlg.ShowDialog(this);
}
return false;
}
return true;
}
public override bool EnableNext()
{
bool clearAllButtonEnabled = false;
bool selectAllButtonEnabled = false;
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
if (row.IsPoolOrStandaloneHost && row.Enabled)
{
int val = (int)row.Cells[POOL_CHECKBOX_COL].Value;
if (val > UNCHECKED)
{
clearAllButtonEnabled = true;
if (val == INDETERMINATE)
selectAllButtonEnabled = true;
}
else
selectAllButtonEnabled = true;
}
}
buttonClearAll.Enabled = clearAllButtonEnabled;
buttonSelectAll.Enabled = selectAllButtonEnabled;
return clearAllButtonEnabled;
}
#region Accessors
public Pool_patch Patch { private get; set; }
public List<Host> SelectedMasters
{
get
{
List<Host> result = new List<Host>();
foreach (Host selectedServer in SelectedServers)
{
Host master = Helpers.GetMaster(selectedServer.Connection);
if (!result.Contains(master))
result.Add(master);
}
return result;
}
}
public List<Host> SelectedServers
{
get
{
if (poolSelectionOnly)
{
var enabledHosts = new List<Host>();
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
if (row.IsAHostRow && row.Enabled)
enabledHosts.Add((Host)row.Tag);
}
if (WizardMode != WizardMode.SingleUpdate)
//prechecks will fail in automated updates mode if one of the hosts is unreachable
return SelectedPools.SelectMany(p => p.Connection.Cache.Hosts.Where(host => enabledHosts.Contains(host)).OrderBy(host => host)).ToList();
//prechecks will issue warning but allow updates to be installed on the reachable hosts only
return SelectedPools.SelectMany(p => p.Connection.Cache.Hosts.Where(host => host.IsLive() && enabledHosts.Contains(host)).OrderBy(host => host)).ToList();
}
else
{
List<Host> hosts = new List<Host>();
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
if (row.IsSelectableHost)
{
if ((row.HasPool && ((int)row.Cells[POOL_ICON_HOST_CHECKBOX_COL].Value) == CHECKED) || (!row.HasPool && ((int)row.Cells[POOL_CHECKBOX_COL].Value) == CHECKED))
hosts.Add((Host)row.Tag);
}
}
return hosts;
}
}
}
public List<Pool> SelectedPools
{
get
{
List<Pool> pools = new List<Pool>();
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
if (row.Tag is Pool)
{
if (((int)row.Cells[POOL_CHECKBOX_COL].Value) != UNCHECKED && !pools.Contains((Pool)row.Tag))
pools.Add((Pool)row.Tag);
}
else if (row.Tag is Host)
{
if (((int)row.Cells[POOL_CHECKBOX_COL].Value) != UNCHECKED)
{
Host host = (Host)row.Tag;
Pool pool = Helpers.GetPoolOfOne(host.Connection);
if (pool != null && !pools.Contains(pool))
pools.Add(pool);
}
}
}
return pools;
}
}
public bool ApplyUpdatesToNewVersion
{
get
{
return applyUpdatesCheckBox.Visible && applyUpdatesCheckBox.Checked;
}
}
public UpdateType SelectedUpdateType { private get; set; }
public void SelectServers(List<Host> selectedServers)
{
if (selectedServers.Count > 0)
{
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
if (row.IsSelectableHost)
{
var host = (Host) row.Tag;
if (selectedServers.Contains(host))
dataGridViewHosts.CheckBoxChange(row.Index,
Helpers.GetPool(host.Connection) != null
? POOL_ICON_HOST_CHECKBOX_COL
: POOL_CHECKBOX_COL);
}
else if (poolSelectionOnly && row.IsSelectablePool)
{
// select the pools of the selected servers
var pool = (Pool) row.Tag;;
foreach (var host in pool.Connection.Cache.Hosts)
{
if (selectedServers.Contains(host))
{
dataGridViewHosts.CheckBoxChange(row.Index, POOL_CHECKBOX_COL);
break;
}
}
}
}
}
}
public void DisableUnselectedServers()
{
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
if (row.Enabled && row.CheckValue == UNCHECKED)
{
row.Enabled = false;
}
}
}
#endregion
private void buttonSelectAll_Click(object sender, EventArgs e)
{
CheckAllCheckBoxes(CHECKED);
}
private void CheckAllCheckBoxes(int value)
{
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
if (row.IsSelectableHost)
{
if (row.HasPool && (int)row.Cells[POOL_ICON_HOST_CHECKBOX_COL].Value != value)
dataGridViewHosts.CheckBoxChange(row.Index, POOL_ICON_HOST_CHECKBOX_COL);
else if ((int)row.Cells[POOL_CHECKBOX_COL].Value != value)
dataGridViewHosts.CheckBoxChange(row.Index, POOL_CHECKBOX_COL);
}
else if (row.IsSelectablePool && (int)row.Cells[POOL_CHECKBOX_COL].Value != value)
{
dataGridViewHosts.CheckBoxChange(row.Index, POOL_CHECKBOX_COL);
}
}
}
private void buttonClearAll_Click(object sender, EventArgs e)
{
CheckAllCheckBoxes(UNCHECKED);
}
private void dataGridViewHosts_CheckBoxClicked(object sender, EventArgs e)
{
OnPageUpdated();
}
private void applyUpdatesCheckBox_CheckedChanged(object sender, System.EventArgs e)
{
PatchingHostsDataGridViewRow masterRow = null;
foreach (PatchingHostsDataGridViewRow row in dataGridViewHosts.Rows)
{
var host = row.Tag as Host;
if (host != null)
{
string tooltipText;
row.Enabled = CanEnableRow(host, out tooltipText);
row.Cells[3].ToolTipText = tooltipText;
if (row.ParentPoolRow != null)
{
if (row.Enabled)
{
row.ParentPoolRow.Enabled = true;
row.ParentPoolRow.Cells[3].ToolTipText = null;
}
if (masterRow == null)
{
masterRow = row;
if (!row.Enabled)
row.ParentPoolRow.Cells[3].ToolTipText = row.Cells[3].ToolTipText;
}
}
}
else
{
row.Enabled = false;
masterRow = null;//reset the stored masterRow
}
}
}
#region Nested items
private class PatchingHostsDataGridView : CollapsingPoolHostDataGridView
{
protected override void OnCellPainting(DataGridViewCellPaintingEventArgs e)
{
base.OnCellPainting(e);
if (e.RowIndex >= 0 && Rows[e.RowIndex].Tag is Host)
{
PatchingHostsDataGridViewRow row = (PatchingHostsDataGridViewRow)Rows[e.RowIndex];
if (row.HasPool && (e.ColumnIndex == POOL_CHECKBOX_COL || e.ColumnIndex == PLUS_MINUS_COL))
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
else if (!row.HasPool && e.ColumnIndex == PLUS_MINUS_COL)
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
else if (row.HasPool && !row.IsSelectableHost && e.ColumnIndex == POOL_ICON_HOST_CHECKBOX_COL)
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
else if (!row.HasPool && !row.Enabled && e.ColumnIndex == POOL_CHECKBOX_COL)
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
}
if (e.RowIndex >= 0 && Rows[e.RowIndex].Tag is Pool)
{
PatchingHostsDataGridViewRow row = (PatchingHostsDataGridViewRow)Rows[e.RowIndex];
if (!row.Enabled && e.ColumnIndex == POOL_CHECKBOX_COL)
{
e.PaintBackground(e.ClipBounds, true);
e.Handled = true;
}
}
}
public override void CheckBoxChange(int RowIndex, int ColumnIndex)
{
if (RowIndex >= 0 && !((PatchingHostsDataGridViewRow)Rows[RowIndex]).Enabled)
return;
if (RowIndex >= 0 && Rows[RowIndex].Tag is Host)
{
if (ColumnIndex == POOL_ICON_HOST_CHECKBOX_COL && Rows[RowIndex].Cells[ColumnIndex].Value is int) //Checkbox
{
int hostNewValue = ClickCheckBox(RowIndex, ColumnIndex);
PatchingHostsDataGridViewRow poolRow = FindPoolRow(RowIndex);
bool allHostSameValue = true;
bool atLeastOneHostChecked = false;
for (int i = poolRow.Index + 1; i < Rows.Count; i++)
{
if (Rows[i].Tag is Host &&
((PatchingHostsDataGridViewRow)Rows[i]).HasPool)
{
if (((int)Rows[i].Cells[POOL_ICON_HOST_CHECKBOX_COL].Value) == CHECKED)
{
atLeastOneHostChecked = true;
}
if (((PatchingHostsDataGridViewRow)Rows[i]).Enabled && ((int)Rows[i].Cells[POOL_ICON_HOST_CHECKBOX_COL].Value) != hostNewValue)
{
allHostSameValue = false;
}
}
else
break;
}
if (allHostSameValue)
{
poolRow.Cells[POOL_CHECKBOX_COL].Value = hostNewValue;
}
else if (atLeastOneHostChecked)
{
poolRow.Cells[POOL_CHECKBOX_COL].Value = INDETERMINATE;
}
}
else if (ColumnIndex == POOL_CHECKBOX_COL)
{
ClickCheckBox(RowIndex, ColumnIndex);
}
}
else if (RowIndex >= 0 && Rows[RowIndex].Tag is Pool)
{
if (ColumnIndex == POOL_CHECKBOX_COL)
{
ClickCheckBox(RowIndex, ColumnIndex);
for (int i = RowIndex + 1; i < Rows.Count; i++)
{
if (Rows[i].Tag is Host && ((PatchingHostsDataGridViewRow)Rows[i]).HasPool)
{
var value = (int)Rows[RowIndex].Cells[ColumnIndex].Value;
if (value == UNCHECKED || value == CHECKED)
Rows[i].Cells[POOL_ICON_HOST_CHECKBOX_COL].Value = value;
}
else break;
}
}
}
OnCheckBoxClicked();
}
private PatchingHostsDataGridViewRow FindPoolRow(int rowIndex)
{
for (int i = rowIndex; i >= 0; i--)
{
if (Rows[i].Tag is Pool)
return (PatchingHostsDataGridViewRow)Rows[i];
}
return null;
}
private int ClickCheckBox(int rowIndex, int columnIndex)
{
int value = (int)Rows[rowIndex].Cells[columnIndex].Value;
if (value == CHECKED)
{
Rows[rowIndex].Cells[columnIndex].Value = UNCHECKED;
return UNCHECKED;
}
else
{
Rows[rowIndex].Cells[columnIndex].Value = CHECKED;
return CHECKED;
}
}
protected override void SortColumns()
{
PatchingHostsDataGridViewRow firstRow = Rows[0] as PatchingHostsDataGridViewRow;
if (firstRow == null)
return;
if (columnToBeSortedIndex == firstRow.NameCellIndex ||
columnToBeSortedIndex == firstRow.VersionCellIndex)
SortAndRebuildTree(new CollapsingPoolHostRowSorter<PatchingHostsDataGridViewRow>(direction, columnToBeSortedIndex));
}
}
private class PatchingHostsDataGridViewRow : CollapsingPoolHostDataGridViewRow
{
private class DataGridViewNameCell : DataGridViewTextBoxCell
{
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
{
Pool pool = value as Pool;
if (pool != null)
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue, errorText, cellStyle, advancedBorderStyle, paintParts);
else
{
Host host = value as Host;
if (host != null)
{
PatchingHostsDataGridViewRow row = (PatchingHostsDataGridViewRow)this.DataGridView.Rows[this.RowIndex];
if (row.HasPool)
{
Image hostIcon = Images.GetImage16For(host);
base.Paint(graphics, clipBounds,
new Rectangle(cellBounds.X + 16, cellBounds.Y, cellBounds.Width - 16,
cellBounds.Height), rowIndex, cellState, value, formattedValue,
errorText, cellStyle, advancedBorderStyle, paintParts);
if ((cellState & DataGridViewElementStates.Selected) != 0 && row.Enabled)
{
using (var brush = new SolidBrush(DataGridView.DefaultCellStyle.SelectionBackColor))
graphics.FillRectangle(
brush, cellBounds.X,
cellBounds.Y, hostIcon.Width, cellBounds.Height);
}
else
{
using (var brush = new SolidBrush(DataGridView.DefaultCellStyle.BackColor))
graphics.FillRectangle(brush,
cellBounds.X, cellBounds.Y, hostIcon.Width, cellBounds.Height);
}
if (row.Enabled)
graphics.DrawImage(hostIcon, cellBounds.X, cellBounds.Y + 3, hostIcon.Width,
hostIcon.Height);
else
graphics.DrawImage(hostIcon,
new Rectangle(cellBounds.X, cellBounds.Y + 3,
hostIcon.Width, hostIcon.Height),
0, 0, hostIcon.Width, hostIcon.Height, GraphicsUnit.Pixel,
Drawing.GreyScaleAttributes);
}
else
{
base.Paint(graphics, clipBounds, cellBounds, rowIndex, cellState, value, formattedValue,
errorText, cellStyle, advancedBorderStyle, paintParts);
}
}
}
}
}
private class DataGridViewIconCell : DataGridViewImageCell
{
protected override void Paint(Graphics graphics, Rectangle clipBounds, Rectangle cellBounds, int rowIndex, DataGridViewElementStates cellState, object value, object formattedValue, string errorText, DataGridViewCellStyle cellStyle, DataGridViewAdvancedBorderStyle advancedBorderStyle, DataGridViewPaintParts paintParts)
{
Image icon = value as Image;
if (icon == null)
return;
PatchingHostsDataGridViewRow row = (PatchingHostsDataGridViewRow)DataGridView.Rows[RowIndex];
if ((cellState & DataGridViewElementStates.Selected) != 0 && row.Enabled)
{
using (var brush = new SolidBrush(DataGridView.DefaultCellStyle.SelectionBackColor))
graphics.FillRectangle(
brush, cellBounds.X,
cellBounds.Y, cellBounds.Width, cellBounds.Height);
}
else
{
using (var brush = new SolidBrush(DataGridView.DefaultCellStyle.BackColor))
graphics.FillRectangle(brush, cellBounds.X,
cellBounds.Y, cellBounds.Width, cellBounds.Height);
}
if (row.Enabled)
graphics.DrawImage(icon, cellBounds.X, cellBounds.Y + 3, icon.Width, icon.Height);
else
graphics.DrawImage(icon, new Rectangle(cellBounds.X, cellBounds.Y + 3, icon.Width, icon.Height),
0, 0, icon.Width, icon.Height, GraphicsUnit.Pixel,
Drawing.GreyScaleAttributes);
}
}
private DataGridViewCell _poolIconHostCheckCell;
private DataGridViewTextBoxCell _versionCell;
private readonly bool _showHostCheckBox = true;
public PatchingHostsDataGridViewRow(Pool pool)
: base(pool)
{
SetupCells();
}
public PatchingHostsDataGridViewRow(Host host, bool hasPool, bool showHostCheckBox = true)
: base(host, hasPool)
{
_showHostCheckBox = showHostCheckBox;
SetupCells();
}
public int VersionCellIndex
{
get { return Cells.IndexOf(_versionCell); }
}
public override bool IsCheckable
{
get { return !HasPool; }
}
public override bool Enabled
{
get
{
return base.Enabled;
}
set
{
base.Enabled = value;
UpdateDetails();
}
}
public int CheckValue
{
get {
return IsPoolOrStandaloneHost
? (int) Cells[POOL_CHECKBOX_COL].Value
: (int) Cells[POOL_ICON_HOST_CHECKBOX_COL].Value;
}
}
public bool IsSelectableHost
{
get { return IsAHostRow && Enabled && (_showHostCheckBox || !HasPool); }
}
public bool IsSelectablePool
{
get { return IsAPoolRow && Enabled; }
}
private void SetupCells()
{
_poolCheckBoxCell = new DataGridViewCheckBoxCell { ThreeState = true };
_expansionCell = new DataGridViewImageCell();
if (IsPoolOrStandaloneHost)
_poolIconHostCheckCell = new DataGridViewIconCell();
else
_poolIconHostCheckCell = new DataGridViewCheckBoxCell();
_nameCell = new DataGridViewNameCell();
_versionCell = new DataGridViewTextBoxCell();
Cells.AddRange(_expansionCell, _poolCheckBoxCell, _poolIconHostCheckCell, _nameCell, _versionCell);
this.UpdateDetails();
}
private void UpdateDetails()
{
var pool = Tag as Pool;
if (pool != null)
{
Host master = pool.Connection.Resolve(pool.master);
if (_poolCheckBoxCell.Value == null)
_poolCheckBoxCell.Value = CheckState.Unchecked;
_expansionCell.Value = Resources.tree_minus;
_poolIconHostCheckCell.Value = Images.GetImage16For(pool);
_nameCell.Value = pool;
_versionCell.Value = master.ProductVersionTextShort();
return;
}
var host = Tag as Host;
if (host != null)
{
if (_poolCheckBoxCell.Value == null)
_poolCheckBoxCell.Value = CheckState.Unchecked;
_expansionCell.Value = Resources.tree_plus;
if (_hasPool)
{
if (_poolIconHostCheckCell.Value == null)
_poolIconHostCheckCell.Value = CheckState.Unchecked;
}
else
_poolIconHostCheckCell.Value = Images.GetImage16For(host);
_nameCell.Value = host;
_versionCell.Value = host.ProductVersionTextShort();
}
}
internal void UpdateIcon()
{
if (_poolIconHostCheckCell is DataGridViewImageCell)
{
_poolIconHostCheckCell.Value = Images.GetImage16For((IXenObject)Tag);
}
}
public PatchingHostsDataGridViewRow ParentPoolRow { get; set; }
}
#endregion
}
}