2023-01-24 15:29:31 +01:00
/ * Copyright ( c ) Cloud Software Group , Inc .
2013-06-24 13:41:48 +02:00
*
* 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 ;
2023-02-13 14:47:21 +01:00
using System.Diagnostics ;
2018-12-20 13:19:44 +01:00
using System.IO ;
2021-06-21 14:21:53 +02:00
using System.Linq ;
2013-06-24 13:41:48 +02:00
using System.Windows.Forms ;
using XenAdmin.Controls ;
using XenAdmin.Dialogs ;
using XenAPI ;
2021-03-16 02:50:45 +01:00
using XenAdmin.Core ;
2013-06-24 13:41:48 +02:00
2023-02-13 14:47:21 +01:00
namespace XenAdmin.Wizards.BugToolWizard
2013-06-24 13:41:48 +02:00
{
public partial class BugToolPageRetrieveData : XenTabPage
{
2023-02-13 14:47:21 +01:00
private static readonly log4net . ILog Log = log4net . LogManager . GetLogger ( System . Reflection . MethodBase . GetCurrentMethod ( ) . DeclaringType ) ;
2021-06-21 14:21:53 +02:00
private const int MAX_DOWNLOADS_PER_CONNECTION = 3 ;
2023-02-13 14:47:21 +01:00
public string OutputFile { get ; set ; }
2021-06-21 14:21:53 +02:00
2013-06-24 13:41:48 +02:00
public BugToolPageRetrieveData ( )
{
InitializeComponent ( ) ;
2021-03-16 02:50:45 +01:00
labelBlurb . Text = string . Format ( labelBlurb . Text , BrandManager . BrandConsole ) ;
2023-02-13 14:47:21 +01:00
labelBlurbCis . Text = string . Format ( labelBlurbCis . Text , BrandManager . Cis ) ;
linkLabelBlurb . Text = InvisibleMessages . CIS_URL ;
2013-06-24 13:41:48 +02:00
}
2018-12-20 13:19:44 +01:00
#region XenTabPage overrides
2021-03-16 02:50:45 +01:00
public override string Text = > Messages . BUGTOOL_PAGE_RETRIEVEDATA_TEXT ;
2013-06-24 13:41:48 +02:00
2021-03-16 02:50:45 +01:00
public override string PageTitle = > Messages . BUGTOOL_PAGE_RETRIEVEDATA_PAGE_TITLE ;
2013-06-24 13:41:48 +02:00
2021-03-16 02:50:45 +01:00
public override string HelpID = > "CompileReport" ;
2013-06-24 13:41:48 +02:00
public override bool EnableNext ( )
{
2023-02-13 14:47:21 +01:00
var allCompleted = AllActionsCompleted ( out var successExists , out _ , out var packageStatusReportFailed , considerDownloadReportRow : _packagedReport ) ;
return allCompleted & & successExists & & ! packageStatusReportFailed ;
2013-06-24 13:41:48 +02:00
}
2018-03-09 01:31:46 +01:00
protected override void PageLoadedCore ( PageLoadedDirection direction )
2013-06-24 13:41:48 +02:00
{
if ( direction = = PageLoadedDirection . Forward )
2018-12-20 13:19:44 +01:00
RunActions ( ) ;
2013-06-24 13:41:48 +02:00
}
2018-03-09 00:55:50 +01:00
protected override void PageLeaveCore ( PageLoadedDirection direction , ref bool cancel )
2013-06-24 13:41:48 +02:00
{
2023-02-13 14:47:21 +01:00
_packagedReport = false ;
2018-12-20 13:19:44 +01:00
if ( direction = = PageLoadedDirection . Forward )
return ;
2018-12-20 13:33:26 +01:00
CancelActions ( ref cancel ) ;
2013-06-24 13:41:48 +02:00
}
2018-12-20 13:33:26 +01:00
public override void PageCancelled ( ref bool cancel )
2013-06-24 13:41:48 +02:00
{
2018-12-20 13:33:26 +01:00
CancelActions ( ref cancel ) ;
2013-06-24 13:41:48 +02:00
}
2018-12-20 13:19:44 +01:00
#endregion
2016-12-21 14:33:04 +01:00
2018-12-20 13:19:44 +01:00
#region Properties
2013-06-24 13:41:48 +02:00
public List < Host > SelectedHosts { private get ; set ; }
2018-12-20 13:19:44 +01:00
public List < Capability > CapabilityList { private get ; set ; }
public string OutputFolder { get ; private set ; }
2023-02-13 14:47:21 +01:00
private bool _packagedReport ;
2021-06-21 14:21:53 +02:00
#endregion
2013-06-24 13:41:48 +02:00
2023-02-13 14:47:21 +01:00
/// <summary>
/// Check if all actions attached to the rows in the DataGridView have been run (successfully or otherwise).
/// </summary>
/// <param name="successExists">True if at least one row actions has <see cref="StatusReportRow.IsSuccessful"/> set to True</param>
/// <param name="failureExists">True if at least one row actions has failed</param>
/// <param name="packageStatusReportFailed">True if the packaging status report action has failed</param>
/// <param name="considerDownloadReportRow">If true, <see cref="PackageStatusReportRow"/> actions will contribute to the check.
/// If false, <see cref="PackageStatusReportRow"/> will be ignored and the method will ignore their state.</param>
/// <returns>True if all actions are in a successful state.</returns>
private bool AllActionsCompleted ( out bool successExists , out bool failureExists , out bool packageStatusReportFailed , bool considerDownloadReportRow = false )
2013-06-24 13:41:48 +02:00
{
2021-06-21 14:21:53 +02:00
var allComplete = true ;
2018-12-20 13:19:44 +01:00
successExists = false ;
failureExists = false ;
2023-02-13 14:47:21 +01:00
packageStatusReportFailed = false ;
2018-12-20 13:19:44 +01:00
foreach ( var row in dataGridViewEx1 . Rows )
{
var srRow = ( StatusReportRow ) row ;
2023-02-13 14:47:21 +01:00
if ( ! considerDownloadReportRow & & srRow is ZipStatusReportRow )
2021-06-21 14:21:53 +02:00
{
continue ;
}
2018-12-20 13:19:44 +01:00
2023-02-13 14:47:21 +01:00
if ( ! srRow . IsCompleted )
{
allComplete = false ;
}
else if ( srRow . IsSuccessful )
{
2018-12-20 13:19:44 +01:00
successExists = true ;
2023-02-13 14:47:21 +01:00
}
else if ( srRow is ZipStatusReportRow )
{
packageStatusReportFailed = true ;
failureExists = true ;
}
2018-12-20 13:19:44 +01:00
else
2023-02-13 14:47:21 +01:00
{
2018-12-20 13:19:44 +01:00
failureExists = true ;
2023-02-13 14:47:21 +01:00
}
2018-12-20 13:19:44 +01:00
}
2021-06-21 14:21:53 +02:00
return allComplete ;
2013-06-24 13:41:48 +02:00
}
2023-02-13 14:47:21 +01:00
/// <summary>
/// Run all non-completed actions that are still queued, if they can be run.
/// Heuristic is based on the result of <see cref="CanRunRowAction"/>.
/// </summary>
/// <param name="considerDownloadReportRow">Needs to be set to true if you want to also actions queued under <see cref="PackageStatusReportRow"/> rows</param>
private void RunQueuedActions ( bool considerDownloadReportRow = false )
{
foreach ( var row in dataGridViewEx1 . Rows )
{
var srRow = ( StatusReportRow ) row ;
if ( ! considerDownloadReportRow & & srRow is ZipStatusReportRow )
{
continue ;
}
if ( ! srRow . IsCompleted & & CanRunRowAction ( srRow ) )
{
RegisterRowEvents ( srRow ) ;
RunRowAction ( srRow ) ;
}
}
}
2018-12-20 13:33:26 +01:00
private void CancelActions ( ref bool cancel )
2013-06-24 13:41:48 +02:00
{
2023-02-13 14:47:21 +01:00
var allCompleted = AllActionsCompleted ( out _ , out _ , out _ , considerDownloadReportRow : true ) ;
2018-12-20 13:33:26 +01:00
if ( allCompleted )
return ;
2023-02-13 14:47:21 +01:00
using ( var warningDialog = new WarningDialog ( Messages . BUGTOOL_PAGE_RETRIEVEDATA_CONFIRM_CANCEL ,
2020-04-22 15:47:03 +02:00
ThreeButtonDialog . ButtonYes , ThreeButtonDialog . ButtonNo )
{ WindowTitle = Messages . BUGTOOL_PAGE_RETRIEVEDATA_PAGE_TITLE } )
2018-12-20 13:33:26 +01:00
{
2023-02-13 14:47:21 +01:00
if ( warningDialog . ShowDialog ( this ) ! = DialogResult . Yes )
2018-12-20 13:33:26 +01:00
{
cancel = true ;
return ;
}
}
2018-12-20 13:19:44 +01:00
foreach ( var r in dataGridViewEx1 . Rows )
2013-06-24 13:41:48 +02:00
{
2018-12-20 13:19:44 +01:00
if ( r is StatusReportRow row )
{
row . DeRegisterEvents ( ) ;
DeRegisterRowEvents ( row ) ;
row . CancelAction ( ) ;
}
2013-06-24 13:41:48 +02:00
}
}
2018-12-20 13:19:44 +01:00
private void RunActions ( )
2013-06-24 13:41:48 +02:00
{
2018-12-20 13:19:44 +01:00
try
2013-06-24 13:41:48 +02:00
{
2018-12-20 13:19:44 +01:00
labelError . Text = "" ;
progressBar1 . Value = 0 ;
dataGridViewEx1 . SuspendLayout ( ) ;
dataGridViewEx1 . Rows . Clear ( ) ;
2023-02-13 14:47:21 +01:00
OutputFolder = CreateOutputFolder ( ) ;
2018-12-20 13:19:44 +01:00
var capabilityKeys = new List < string > ( ) ;
long size = 0 ;
foreach ( Capability c in CapabilityList )
{
if ( c . Key ! = "client-logs" )
size + = c . MaxSize ;
capabilityKeys . Add ( c . Key ) ;
}
2013-06-24 13:41:48 +02:00
2018-12-20 13:19:44 +01:00
var rowList = new List < DataGridViewRow > ( ) ;
2013-06-24 13:41:48 +02:00
2018-12-20 13:19:44 +01:00
var includeClientLogs = capabilityKeys . Contains ( "client-logs" ) ;
if ( includeClientLogs | | SelectedHosts . Count > 0 )
2023-02-13 14:47:21 +01:00
rowList . Add ( new ClientSideStatusReportRow ( SelectedHosts , includeClientLogs ) ) ;
foreach ( var host in SelectedHosts )
{
rowList . Add ( new SingleHostStatusReportRow ( host , size , capabilityKeys ) ) ;
}
2013-06-24 13:41:48 +02:00
2023-02-13 14:47:21 +01:00
rowList . Add ( new ZipStatusReportRow ( OutputFile ) ) ;
2018-12-20 13:19:44 +01:00
dataGridViewEx1 . Rows . AddRange ( rowList . ToArray ( ) ) ;
}
finally
{
dataGridViewEx1 . ResumeLayout ( ) ;
2013-06-24 13:41:48 +02:00
}
2018-12-20 13:19:44 +01:00
foreach ( var r in dataGridViewEx1 . Rows )
2013-06-24 13:41:48 +02:00
{
2021-06-21 14:21:53 +02:00
var row = r as StatusReportRow ;
2023-02-13 14:47:21 +01:00
if ( ! CanRunRowAction ( row ) )
{
continue ;
}
RegisterRowEvents ( row ) ;
// PackageStatusReportRow must be run at the very end in a synchronous manner.
// this is handled within the RowStatusCompleted method
if ( ! ( row is ZipStatusReportRow ) )
{
2021-06-21 14:21:53 +02:00
RunRowAction ( row ) ;
2023-02-13 14:47:21 +01:00
}
2013-06-24 13:41:48 +02:00
}
2018-12-20 13:19:44 +01:00
OnPageUpdated ( ) ;
2013-06-24 13:41:48 +02:00
}
2021-06-21 14:21:53 +02:00
private bool CanRunRowAction ( StatusReportRow row )
{
if ( row . Action ! = null )
return false ;
2023-02-13 14:47:21 +01:00
return row is ClientSideStatusReportRow | |
row is ZipStatusReportRow | |
row is SingleHostStatusReportRow hostRow & &
2021-06-21 14:21:53 +02:00
dataGridViewEx1 . Rows . Cast < StatusReportRow > ( ) . Count ( r = >
2023-02-13 14:47:21 +01:00
r is SingleHostStatusReportRow hsr & & hsr . Connection = = hostRow . Connection & &
2021-06-21 14:21:53 +02:00
hsr . Action ! = null & & ! hsr . IsCompleted ) < MAX_DOWNLOADS_PER_CONNECTION ;
}
private void RunRowAction ( StatusReportRow row )
{
row . RunAction ( OutputFolder , DateTime . Now . ToString ( "yyyy-MM-dd-HH-mm-ss" ) ) ;
}
2023-02-13 14:47:21 +01:00
private void RunPackageStatusReportAction ( bool successExists )
{
var lastRow = ( StatusReportRow ) dataGridViewEx1 . Rows [ dataGridViewEx1 . Rows . Count - 1 ] ;
if ( ! successExists )
{
// no part of the report was generated, we don't need to
// start the packaging action at all
lastRow . CancelAction ( ) ;
}
else
{
RunRowAction ( lastRow ) ;
}
_packagedReport = true ;
}
2018-12-20 13:19:44 +01:00
private void Row_RowStatusChanged ( StatusReportRow row )
2013-06-24 13:41:48 +02:00
{
2018-12-20 13:19:44 +01:00
UpdateTotalPercentComplete ( ) ;
OnPageUpdated ( ) ;
}
private void Row_RowStatusCompleted ( StatusReportRow row )
{
DeRegisterRowEvents ( row ) ;
UpdateTotalPercentComplete ( ) ;
2023-02-13 14:47:21 +01:00
var allCompleted = AllActionsCompleted ( out var successExists , out var failureExists , out var packageStatusReportFailed , considerDownloadReportRow : _packagedReport ) ;
2018-12-20 13:19:44 +01:00
2023-02-13 14:47:21 +01:00
if ( allCompleted & & ! _packagedReport )
{
RunPackageStatusReportAction ( successExists ) ;
}
else if ( allCompleted )
2013-06-24 13:41:48 +02:00
{
2023-02-13 14:47:21 +01:00
if ( packageStatusReportFailed )
{
labelError . Text = Messages . ACTION_SYSTEM_STATUS_SAVE_FAILED ;
}
else if ( ! successExists )
{
labelError . Text = Messages . ACTION_SYSTEM_STATUS_COMPILE_FAILED ;
}
2018-12-20 13:19:44 +01:00
else if ( ! failureExists )
2023-02-13 14:47:21 +01:00
{
labelError . Text = Messages . ACTION_SYSTEM_STATUS_COMPILE_SUCCESSFUL ;
}
2018-12-20 13:19:44 +01:00
else
2023-02-13 14:47:21 +01:00
{
labelError . Text = Messages . ACTION_SYSTEM_STATUS_COMPILE_PARTIAL ;
}
_packagedReport = false ;
}
else
{
RunQueuedActions ( _packagedReport ) ;
2013-06-24 13:41:48 +02:00
}
2018-12-20 13:19:44 +01:00
OnPageUpdated ( ) ;
2013-06-24 13:41:48 +02:00
}
2018-12-20 13:19:44 +01:00
private void RegisterRowEvents ( StatusReportRow row )
2013-06-24 13:41:48 +02:00
{
2018-12-20 13:19:44 +01:00
row . RowStatusChanged + = Row_RowStatusChanged ;
row . RowStatusCompleted + = Row_RowStatusCompleted ;
}
private void DeRegisterRowEvents ( StatusReportRow row )
{
row . RowStatusChanged - = Row_RowStatusChanged ;
row . RowStatusCompleted - = Row_RowStatusCompleted ;
}
private void UpdateTotalPercentComplete ( )
{
int total = 0 ;
foreach ( var r in dataGridViewEx1 . Rows )
{
var row = ( StatusReportRow ) r ;
total + = row . PercentComplete ;
}
var percentage = total / dataGridViewEx1 . RowCount ;
if ( percentage < 0 )
percentage = 0 ;
if ( percentage > 100 )
percentage = 100 ;
if ( percentage > progressBar1 . Value )
progressBar1 . Value = percentage ;
}
private static string CreateOutputFolder ( )
{
var folder = Path . Combine ( Path . GetTempPath ( ) , Path . GetRandomFileName ( ) ) ;
if ( Directory . Exists ( folder ) )
Directory . Delete ( folder ) ;
Directory . CreateDirectory ( folder ) ;
return folder ;
2013-06-24 13:41:48 +02:00
}
2023-02-13 14:47:21 +01:00
private void linkLabelBlurb_LinkClicked ( object sender , LinkLabelLinkClickedEventArgs e )
2013-06-24 13:41:48 +02:00
{
2023-02-13 14:47:21 +01:00
try
2013-06-24 13:41:48 +02:00
{
2023-02-13 14:47:21 +01:00
Process . Start ( InvisibleMessages . CIS_URL ) ;
2013-06-24 13:41:48 +02:00
}
2023-02-13 14:47:21 +01:00
catch ( Exception exception )
2018-12-20 13:19:44 +01:00
{
2023-02-13 14:47:21 +01:00
Log . Error ( $"Error while opening {InvisibleMessages.CIS_URL}." , exception ) ;
using ( var dlg = new ErrorDialog ( string . Format ( Messages . COULD_NOT_OPEN_URL , InvisibleMessages . CIS_URL ) ) )
dlg . ShowDialog ( Program . MainWindow ) ;
2018-12-20 13:19:44 +01:00
}
2013-06-24 13:41:48 +02:00
}
}
}